From: Tony Tkacik Date: Tue, 24 Feb 2015 09:39:34 +0000 (+0000) Subject: Merge "Bug 2731: Discard changes only when transaction exist." X-Git-Tag: release/lithium~473^2~5 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=controller.git;a=commitdiff_plain;h=5c008222efa5c0af49cf8a52881a6299b1e249dc;hp=b64695583bf72ed6dc8d5cdd642b4807fb671fba Merge "Bug 2731: Discard changes only when transaction exist." --- diff --git a/features/akka/pom.xml b/features/akka/pom.xml index f804505c64..4185972180 100644 --- a/features/akka/pom.xml +++ b/features/akka/pom.xml @@ -174,9 +174,8 @@ --> - org.opendaylight.yangtools + org.opendaylight.odlparent features-test - ${feature.test.version} test @@ -249,7 +248,7 @@ ${karaf.empty.version} - org.opendaylight.yangtools:features-test + org.opendaylight.odlparent:features-test diff --git a/features/config-netty/pom.xml b/features/config-netty/pom.xml index 0057fc05c7..71b28415b5 100644 --- a/features/config-netty/pom.xml +++ b/features/config-netty/pom.xml @@ -56,7 +56,7 @@ - org.opendaylight.yangtools + org.opendaylight.odlparent features-test @@ -114,7 +114,7 @@ ${commons.opendaylight.version} - org.opendaylight.yangtools:features-test + org.opendaylight.odlparent:features-test diff --git a/features/config-persister/pom.xml b/features/config-persister/pom.xml index f3b42ca143..6fae0e7641 100644 --- a/features/config-persister/pom.xml +++ b/features/config-persister/pom.xml @@ -81,7 +81,7 @@ - org.opendaylight.yangtools + org.opendaylight.odlparent features-test @@ -139,7 +139,7 @@ ${commons.opendaylight.version} - org.opendaylight.yangtools:features-test + org.opendaylight.odlparent:features-test diff --git a/features/config/pom.xml b/features/config/pom.xml index 1fa248615c..683ce3dab1 100644 --- a/features/config/pom.xml +++ b/features/config/pom.xml @@ -88,7 +88,7 @@ - org.opendaylight.yangtools + org.opendaylight.odlparent features-test @@ -146,7 +146,7 @@ ${commons.opendaylight.version} - org.opendaylight.yangtools:features-test + org.opendaylight.odlparent:features-test diff --git a/features/extras/pom.xml b/features/extras/pom.xml index 20089c726a..6ac5872f00 100644 --- a/features/extras/pom.xml +++ b/features/extras/pom.xml @@ -23,7 +23,6 @@ 1.1.0-SNAPSHOT 1.5.0-SNAPSHOT 3.0.1 - 0.7.0-SNAPSHOT 1.5.0-SNAPSHOT 2.16 @@ -34,9 +33,8 @@ ${jolokia.version} - org.opendaylight.yangtools + org.opendaylight.odlparent features-test - ${feature.test.version} test @@ -101,7 +99,7 @@ ${karaf.empty.version} - org.opendaylight.yangtools:features-test + org.opendaylight.odlparent:features-test diff --git a/features/flow/pom.xml b/features/flow/pom.xml index 53b45d2810..ad513e5d56 100644 --- a/features/flow/pom.xml +++ b/features/flow/pom.xml @@ -74,7 +74,7 @@ - org.opendaylight.yangtools + org.opendaylight.odlparent features-test @@ -132,7 +132,7 @@ ${commons.opendaylight.version} - org.opendaylight.yangtools:features-test + org.opendaylight.odlparent:features-test diff --git a/features/mdsal/pom.xml b/features/mdsal/pom.xml index c63b39c74d..35edb286ab 100644 --- a/features/mdsal/pom.xml +++ b/features/mdsal/pom.xml @@ -246,6 +246,15 @@ sal-inmemory-datastore + + org.opendaylight.controller + mdsal-netconf-connector + + + org.opendaylight.controller + mdsal-netconf-monitoring + + org.opendaylight.controller sal-netconf-connector @@ -354,9 +363,8 @@ - org.opendaylight.yangtools + org.opendaylight.odlparent features-test - 0.7.0-SNAPSHOT @@ -413,7 +421,7 @@ ${commons.opendaylight.version} - org.opendaylight.yangtools:features-test + org.opendaylight.odlparent:features-test diff --git a/features/mdsal/src/main/resources/features.xml b/features/mdsal/src/main/resources/features.xml index 5b9f4a674a..4b9f8c2288 100644 --- a/features/mdsal/src/main/resources/features.xml +++ b/features/mdsal/src/main/resources/features.xml @@ -24,12 +24,14 @@ - + + odl-config-all odl-netconf-all mvn:org.opendaylight.controller/netconf-ssh/${netconf.version} odl-mdsal-broker mvn:org.opendaylight.controller/mdsal-netconf-connector/${netconf.version} + mvn:org.opendaylight.controller/mdsal-netconf-monitoring/${netconf.version} mvn:org.opendaylight.controller/netconf-config-dispatcher/${config.version} mvn:org.opendaylight.controller/netconf-config/${netconf.version}/xml/config diff --git a/features/netconf-connector/pom.xml b/features/netconf-connector/pom.xml index 4e94996634..9cb2b1e33f 100644 --- a/features/netconf-connector/pom.xml +++ b/features/netconf-connector/pom.xml @@ -119,10 +119,6 @@ org.opendaylight.controller netconf-config-dispatcher - - org.opendaylight.controller - mdsal-netconf-connector - org.opendaylight.controller netconf-tcp @@ -168,9 +164,8 @@ - org.opendaylight.yangtools + org.opendaylight.odlparent features-test - ${yangtools.version} test @@ -242,7 +237,7 @@ ${commons.opendaylight.version} - org.opendaylight.yangtools:features-test + org.opendaylight.odlparent:features-test diff --git a/features/netconf-connector/src/main/resources/features.xml b/features/netconf-connector/src/main/resources/features.xml index 92e6507d21..7cabbb4929 100644 --- a/features/netconf-connector/src/main/resources/features.xml +++ b/features/netconf-connector/src/main/resources/features.xml @@ -105,6 +105,7 @@ odl-netconf-tcp + odl-config-netty diff --git a/features/netconf/pom.xml b/features/netconf/pom.xml index 4edd936294..b28a53b65c 100644 --- a/features/netconf/pom.xml +++ b/features/netconf/pom.xml @@ -150,9 +150,8 @@ - org.opendaylight.yangtools + org.opendaylight.odlparent features-test - ${yangtools.version} test @@ -218,7 +217,7 @@ ${commons.opendaylight.version} - org.opendaylight.yangtools:features-test + org.opendaylight.odlparent:features-test diff --git a/features/netconf/src/main/resources/features.xml b/features/netconf/src/main/resources/features.xml index a65502124b..aa8287d709 100644 --- a/features/netconf/src/main/resources/features.xml +++ b/features/netconf/src/main/resources/features.xml @@ -27,6 +27,7 @@ mvn:org.opendaylight.controller/ietf-netconf-monitoring-extension/${project.version} mvn:org.opendaylight.yangtools.model/ietf-inet-types/${ietf-inet-types.version} mvn:org.opendaylight.yangtools.model/ietf-yang-types/${ietf-yang-types.version} + mvn:org.opendaylight.yangtools.model/ietf-yang-types-20130715/2013.07.15.7-SNAPSHOT odl-netconf-api diff --git a/features/protocol-framework/pom.xml b/features/protocol-framework/pom.xml index d5387b43c3..f663c1ce3e 100644 --- a/features/protocol-framework/pom.xml +++ b/features/protocol-framework/pom.xml @@ -28,7 +28,7 @@ - org.opendaylight.yangtools + org.opendaylight.odlparent features-test @@ -86,7 +86,7 @@ ${commons.opendaylight.version} - org.opendaylight.yangtools:features-test + org.opendaylight.odlparent:features-test diff --git a/features/restconf/pom.xml b/features/restconf/pom.xml index 632b4cd592..347a19a75a 100644 --- a/features/restconf/pom.xml +++ b/features/restconf/pom.xml @@ -256,9 +256,8 @@ --> - org.opendaylight.yangtools + org.opendaylight.odlparent features-test - ${yangtools.version} test @@ -331,7 +330,7 @@ ${commons.opendaylight.version} - org.opendaylight.yangtools:features-test + org.opendaylight.odlparent:features-test diff --git a/opendaylight/archetypes/opendaylight-startup/src/main/resources/META-INF/maven/archetype-metadata.xml b/opendaylight/archetypes/opendaylight-startup/src/main/resources/META-INF/maven/archetype-metadata.xml index 7057a86fe5..6eab884cc5 100644 --- a/opendaylight/archetypes/opendaylight-startup/src/main/resources/META-INF/maven/archetype-metadata.xml +++ b/opendaylight/archetypes/opendaylight-startup/src/main/resources/META-INF/maven/archetype-metadata.xml @@ -28,7 +28,7 @@ - __artifactId__-karaf + karaf pom.xml @@ -37,13 +37,13 @@ - __artifactId__-features + features pom.xml - __artifactId__-features/src/main/features + features/src/main/features **/*.xml @@ -52,25 +52,31 @@ - __artifactId__-impl + impl pom.xml - __artifactId__-impl/src/main/java + impl/src/main/java **/*.java - __artifactId__-impl/src/main/config + impl/src/test/java + + **/*.java + + + + impl/src/main/config **/*.xml - __artifactId__-impl/src/main/yang + impl/src/main/yang **/*.yang @@ -79,13 +85,13 @@ - __artifactId__-api + api pom.xml - __artifactId__-api/src/main/yang + api/src/main/yang **/*.yang @@ -93,7 +99,7 @@ - __artifactId__-artifacts + artifacts pom.xml diff --git a/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/__artifactId__-api/.gitignore b/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/api/.gitignore similarity index 100% rename from opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/__artifactId__-api/.gitignore rename to opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/api/.gitignore diff --git a/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/__artifactId__-api/pom.xml b/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/api/pom.xml similarity index 100% rename from opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/__artifactId__-api/pom.xml rename to opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/api/pom.xml diff --git a/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/__artifactId__-api/src/main/yang/__artifactId__.yang b/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/api/src/main/yang/__artifactId__.yang similarity index 100% rename from opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/__artifactId__-api/src/main/yang/__artifactId__.yang rename to opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/api/src/main/yang/__artifactId__.yang diff --git a/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/__artifactId__-artifacts/pom.xml b/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/artifacts/pom.xml similarity index 100% rename from opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/__artifactId__-artifacts/pom.xml rename to opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/artifacts/pom.xml diff --git a/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/__artifactId__-features/pom.xml b/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/features/pom.xml similarity index 97% rename from opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/__artifactId__-features/pom.xml rename to opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/features/pom.xml index 49b43f442e..c5adb28db7 100644 --- a/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/__artifactId__-features/pom.xml +++ b/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/features/pom.xml @@ -26,6 +26,7 @@ and is available at http://www.eclipse.org/legal/epl-v10.html INTERNAL 1.2.0-SNAPSHOT 0.7.0-SNAPSHOT + etc/opendaylight/karaf diff --git a/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/__artifactId__-features/src/main/features/features.xml b/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/features/src/main/features/features.xml similarity index 78% rename from opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/__artifactId__-features/src/main/features/features.xml rename to opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/features/src/main/features/features.xml index bbeb2de90e..1facf4c8aa 100644 --- a/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/__artifactId__-features/src/main/features/features.xml +++ b/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/features/src/main/features/features.xml @@ -16,22 +16,22 @@ and is available at http://www.eclipse.org/legal/epl-v10.html mvn:org.opendaylight.yangtools/features-yangtools/${symbol_dollar}{yangtools.version}/xml/features mvn:org.opendaylight.controller/features-mdsal/${symbol_dollar}{mdsal.version}/xml/features mvn:org.opendaylight.controller/features-restconf/${symbol_dollar}{mdsal.version}/xml/features - + odl-yangtools-models mvn:${groupId}/${artifactId}-api/${symbol_dollar}{project.version} - + odl-mdsal-broker odl-${artifactId}-api mvn:${groupId}/${artifactId}-impl/${symbol_dollar}{project.version} - mvn:${groupId}/${artifactId}-impl/${symbol_dollar}{project.version}/xml/config + mvn:${groupId}/${artifactId}-impl/${symbol_dollar}{project.version}/xml/config - - odl-${artifactId}-impl + + odl-${artifactId} odl-restconf - - odl-${artifactId}-impl-rest + + odl-${artifactId}-rest odl-mdsal-apidocs odl-mdsal-xsql diff --git a/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/__artifactId__-impl/pom.xml b/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/impl/pom.xml similarity index 80% rename from opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/__artifactId__-impl/pom.xml rename to opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/impl/pom.xml index 64d6834fef..f9e77ed4b1 100644 --- a/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/__artifactId__-impl/pom.xml +++ b/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/impl/pom.xml @@ -29,6 +29,19 @@ and is available at http://www.eclipse.org/legal/epl-v10.html ${artifactId}-api ${symbol_dollar}{project.version} + + + + junit + junit + test + + + + org.mockito + mockito-all + test + diff --git a/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/__artifactId__-impl/src/main/config/default-config.xml b/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/impl/src/main/config/default-config.xml similarity index 96% rename from opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/__artifactId__-impl/src/main/config/default-config.xml rename to opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/impl/src/main/config/default-config.xml index e777fd25bc..2e73bde717 100644 --- a/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/__artifactId__-impl/src/main/config/default-config.xml +++ b/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/impl/src/main/config/default-config.xml @@ -20,7 +20,7 @@ and is available at http://www.eclipse.org/legal/epl-v10.html - prefix:${artifactId}-impl + prefix:${artifactId} ${artifactId}-default binding:binding-broker-osgi-registry diff --git a/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/__artifactId__-impl/src/main/java/__packageInPathFormat__/__classPrefix__Provider.java b/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/impl/src/main/java/__packageInPathFormat__/impl/__classPrefix__Provider.java similarity index 97% rename from opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/__artifactId__-impl/src/main/java/__packageInPathFormat__/__classPrefix__Provider.java rename to opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/impl/src/main/java/__packageInPathFormat__/impl/__classPrefix__Provider.java index 070fb4d4e4..b913f58ed4 100644 --- a/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/__artifactId__-impl/src/main/java/__packageInPathFormat__/__classPrefix__Provider.java +++ b/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/impl/src/main/java/__packageInPathFormat__/impl/__classPrefix__Provider.java @@ -8,7 +8,7 @@ * 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; diff --git a/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/__artifactId__-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/__artifactId__/impl/rev141210/__classPrefix__ImplModule.java b/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/__artifactId__/impl/rev141210/__classPrefix__Module.java similarity index 55% rename from opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/__artifactId__-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/__artifactId__/impl/rev141210/__classPrefix__ImplModule.java rename to opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/__artifactId__/impl/rev141210/__classPrefix__Module.java index 558b018788..3cf83415d7 100644 --- a/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/__artifactId__-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/__artifactId__/impl/rev141210/__classPrefix__ImplModule.java +++ b/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/__artifactId__/impl/rev141210/__classPrefix__Module.java @@ -10,14 +10,14 @@ */ 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); } diff --git a/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/__artifactId__-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/__artifactId__/impl/rev141210/__classPrefix__ImplModuleFactory.java b/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/__artifactId__/impl/rev141210/__classPrefix__ModuleFactory.java similarity index 80% rename from opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/__artifactId__-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/__artifactId__/impl/rev141210/__classPrefix__ImplModuleFactory.java rename to opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/__artifactId__/impl/rev141210/__classPrefix__ModuleFactory.java index a531fe076e..596549b5fe 100644 --- a/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/__artifactId__-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/__artifactId__/impl/rev141210/__classPrefix__ImplModuleFactory.java +++ b/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/__artifactId__/impl/rev141210/__classPrefix__ModuleFactory.java @@ -18,6 +18,6 @@ * 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 { } diff --git a/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/__artifactId__-impl/src/main/yang/__artifactId__-impl.yang b/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/impl/src/main/yang/__artifactId__-impl.yang similarity index 87% rename from opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/__artifactId__-impl/src/main/yang/__artifactId__-impl.yang rename to opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/impl/src/main/yang/__artifactId__-impl.yang index 7b00b849a1..41a74b3f1a 100644 --- a/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/__artifactId__-impl/src/main/yang/__artifactId__-impl.yang +++ b/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/impl/src/main/yang/__artifactId__-impl.yang @@ -14,14 +14,14 @@ module ${artifactId}-impl { "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 { diff --git a/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/impl/src/test/java/__packageInPathFormat__/impl/__classPrefix__ProviderTest.java b/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/impl/src/test/java/__packageInPathFormat__/impl/__classPrefix__ProviderTest.java new file mode 100644 index 0000000000..09b3dc4622 --- /dev/null +++ b/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/impl/src/test/java/__packageInPathFormat__/impl/__classPrefix__ProviderTest.java @@ -0,0 +1,37 @@ +#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(); + } +} diff --git a/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/impl/src/test/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/__artifactId__/impl/rev141210/__classPrefix__ModuleFactoryTest.java b/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/impl/src/test/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/__artifactId__/impl/rev141210/__classPrefix__ModuleFactoryTest.java new file mode 100644 index 0000000000..9f8991f3ec --- /dev/null +++ b/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/impl/src/test/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/__artifactId__/impl/rev141210/__classPrefix__ModuleFactoryTest.java @@ -0,0 +1,22 @@ +#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}(); + } +} diff --git a/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/impl/src/test/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/__artifactId__/impl/rev141210/__classPrefix__ModuleTest.java b/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/impl/src/test/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/__artifactId__/impl/rev141210/__classPrefix__ModuleTest.java new file mode 100644 index 0000000000..bbef4d8487 --- /dev/null +++ b/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/impl/src/test/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/__artifactId__/impl/rev141210/__classPrefix__ModuleTest.java @@ -0,0 +1,58 @@ +#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(); + } +} diff --git a/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/__artifactId__-karaf/pom.xml b/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/karaf/pom.xml similarity index 77% rename from opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/__artifactId__-karaf/pom.xml rename to opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/karaf/pom.xml index 87b955c6b9..486e3d39ba 100644 --- a/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/__artifactId__-karaf/pom.xml +++ b/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/karaf/pom.xml @@ -24,7 +24,7 @@ and is available at http://www.eclipse.org/legal/epl-v10.html INTERNAL 3.1.1 - odl-${artifactId}-impl-ui + odl-${artifactId}-ui @@ -54,4 +54,23 @@ and is available at http://www.eclipse.org/legal/epl-v10.html INTERNAL runtime + + + + + org.apache.maven.plugins + maven-deploy-plugin + + true + + + + org.apache.maven.plugins + maven-install-plugin + + true + + + + diff --git a/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/pom.xml b/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/pom.xml index 3221efd362..616704ada0 100644 --- a/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/pom.xml +++ b/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/pom.xml @@ -9,18 +9,18 @@ and is available at http://www.eclipse.org/legal/epl-v10.html INTERNAL ${groupId} ${artifactId}-aggregator ${version} - ${project.artifactId} + ${artifactId} pom 4.0.0 3.1.1 - ${artifactId}-api - ${artifactId}-impl - ${artifactId}-karaf - ${artifactId}-features - ${artifactId}-artifacts + api + impl + karaf + features + artifacts @@ -41,4 +41,11 @@ and is available at http://www.eclipse.org/legal/epl-v10.html INTERNAL + + + scm:git:ssh://git.opendaylight.org:29418/${artifactId}.git + scm:git:ssh://git.opendaylight.org:29418/${artifactId}.git + HEAD + https://wiki.opendaylight.org/view/${artifactId}:Main + diff --git a/opendaylight/commons/opendaylight/pom.xml b/opendaylight/commons/opendaylight/pom.xml index 9a88e61007..7a977f9545 100644 --- a/opendaylight/commons/opendaylight/pom.xml +++ b/opendaylight/commons/opendaylight/pom.xml @@ -15,7 +15,7 @@ - 2.3.4 + 2.3.9 0.5.0-SNAPSHOT 0.1.0-SNAPSHOT 0.6.0-SNAPSHOT @@ -81,6 +81,7 @@ 0000.0002.0038.0 1.6.0 + 1.5.0-SNAPSHOT 1.5.0-SNAPSHOT 2.4.0 0.5.0-SNAPSHOT @@ -104,7 +105,6 @@ src/main/yang-gen-config 0.1.0-SNAPSHOT 1.1.4 - 2.0.1 1.1.1 2.0 1.1.0-SNAPSHOT @@ -287,11 +287,6 @@ java-concurrent-hash-trie-map ${ctrie.version} - - com.google.code.findbugs - jsr305 - ${jsr305.api.version} - com.google.code.gson gson @@ -310,9 +305,10 @@ - com.jcabi - jcabi-maven-slf4j - 0.8 + org.apache.maven + maven-core + 3.1.1 + provided @@ -1194,6 +1190,12 @@ ${yangtools.version} test + + org.opendaylight.odlparent + features-test + ${features.test.version} + test + org.opendaylight.yangtools features-yangtools diff --git a/opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/ConfigRegistry.java b/opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/ConfigRegistry.java index 5c9ed8767c..3d72114daf 100644 --- a/opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/ConfigRegistry.java +++ b/opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/ConfigRegistry.java @@ -66,23 +66,4 @@ public interface ConfigRegistry extends LookupRegistry, ServiceReferenceReadable Set getAvailableModuleNames(); - - /** - * Find all runtime beans - * - * @return objectNames - */ - Set lookupRuntimeBeans(); - - /** - * Find all runtime of specified module - * - * @param moduleName - * of bean - * @param instanceName - * of bean - * @return objectNames - */ - Set lookupRuntimeBeans(String moduleName, String instanceName); - } diff --git a/opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/LookupRegistry.java b/opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/LookupRegistry.java index b90fc9c034..5d615c2084 100644 --- a/opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/LookupRegistry.java +++ b/opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/LookupRegistry.java @@ -71,4 +71,21 @@ public interface LookupRegistry { */ Set getAvailableModuleFactoryQNames(); + /** + * Find all runtime beans + * + * @return objectNames + */ + Set lookupRuntimeBeans(); + + /** + * Find all runtime of specified module + * + * @param moduleName + * of bean + * @param instanceName + * of bean + * @return objectNames + */ + Set lookupRuntimeBeans(String moduleName, String instanceName); } diff --git a/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/ConfigTransactionControllerImpl.java b/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/ConfigTransactionControllerImpl.java index 37c2e2d777..186a7218ba 100644 --- a/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/ConfigTransactionControllerImpl.java +++ b/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/ConfigTransactionControllerImpl.java @@ -482,6 +482,25 @@ class ConfigTransactionControllerImpl implements public void checkConfigBeanExists(ObjectName objectName) throws InstanceNotFoundException { txLookupRegistry.checkConfigBeanExists(objectName); } + + + /** + * {@inheritDoc} + */ + @Override + public Set lookupRuntimeBeans() { + return txLookupRegistry.lookupRuntimeBeans(); + } + + /** + * {@inheritDoc} + */ + @Override + public Set lookupRuntimeBeans(String moduleName, + String instanceName) { + return txLookupRegistry.lookupRuntimeBeans(moduleName, instanceName); + } + // -- /** diff --git a/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/ConfigTransactionLookupRegistry.java b/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/ConfigTransactionLookupRegistry.java index f9a3801171..a0138b2d9d 100644 --- a/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/ConfigTransactionLookupRegistry.java +++ b/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/ConfigTransactionLookupRegistry.java @@ -116,6 +116,26 @@ class ConfigTransactionLookupRegistry implements LookupRegistry, Closeable { return ModuleQNameUtil.getQNames(allCurrentFactories); } + /** + * {@inheritDoc} + */ + @Override + public Set lookupRuntimeBeans() { + return lookupRuntimeBeans("*", "*"); + } + + /** + * {@inheritDoc} + */ + @Override + public Set 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() { diff --git a/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/ServiceReferenceRegistryImpl.java b/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/ServiceReferenceRegistryImpl.java index 27f0d5c1f2..dd6c2b9422 100644 --- a/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/ServiceReferenceRegistryImpl.java +++ b/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/ServiceReferenceRegistryImpl.java @@ -97,6 +97,16 @@ public class ServiceReferenceRegistryImpl implements CloseableServiceReferenceRe throw new UnsupportedOperationException(); } + @Override + public Set lookupRuntimeBeans() { + throw new UnsupportedOperationException(); + } + + @Override + public Set lookupRuntimeBeans(final String moduleName, final String instanceName) { + throw new UnsupportedOperationException(); + } + @Override public String toString() { return "initial"; diff --git a/opendaylight/config/config-util/src/main/java/org/opendaylight/controller/config/util/BeanReader.java b/opendaylight/config/config-util/src/main/java/org/opendaylight/controller/config/util/BeanReader.java new file mode 100644 index 0000000000..de9baa6897 --- /dev/null +++ b/opendaylight/config/config-util/src/main/java/org/opendaylight/controller/config/util/BeanReader.java @@ -0,0 +1,10 @@ +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); +} diff --git a/opendaylight/config/config-util/src/main/java/org/opendaylight/controller/config/util/ConfigRegistryClient.java b/opendaylight/config/config-util/src/main/java/org/opendaylight/controller/config/util/ConfigRegistryClient.java index 99d46cb638..d384ae55c0 100644 --- a/opendaylight/config/config-util/src/main/java/org/opendaylight/controller/config/util/ConfigRegistryClient.java +++ b/opendaylight/config/config-util/src/main/java/org/opendaylight/controller/config/util/ConfigRegistryClient.java @@ -10,7 +10,7 @@ package org.opendaylight.controller.config.util; 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(); @@ -23,6 +23,4 @@ public interface ConfigRegistryClient extends ConfigRegistryMXBean { Object invokeMethod(ObjectName on, String name, Object[] params, String[] signature); - Object getAttributeCurrentValue(ObjectName on, String attributeName); - } diff --git a/opendaylight/config/config-util/src/main/java/org/opendaylight/controller/config/util/ConfigRegistryJMXClient.java b/opendaylight/config/config-util/src/main/java/org/opendaylight/controller/config/util/ConfigRegistryJMXClient.java index 099d010642..a39111afee 100644 --- a/opendaylight/config/config-util/src/main/java/org/opendaylight/controller/config/util/ConfigRegistryJMXClient.java +++ b/opendaylight/config/config-util/src/main/java/org/opendaylight/controller/config/util/ConfigRegistryJMXClient.java @@ -211,7 +211,7 @@ public class ConfigRegistryJMXClient implements ConfigRegistryClient { } catch (AttributeNotFoundException | InstanceNotFoundException | MBeanException | ReflectionException e) { throw new RuntimeException("Unable to get attribute " - + attributeName + " for " + on, e); + + attributeName + " for " + on + ". Available beans: " + lookupConfigBeans(), e); } } diff --git a/opendaylight/config/config-util/src/main/java/org/opendaylight/controller/config/util/ConfigTransactionClient.java b/opendaylight/config/config-util/src/main/java/org/opendaylight/controller/config/util/ConfigTransactionClient.java index 359035d51d..c7c072d39d 100644 --- a/opendaylight/config/config-util/src/main/java/org/opendaylight/controller/config/util/ConfigTransactionClient.java +++ b/opendaylight/config/config-util/src/main/java/org/opendaylight/controller/config/util/ConfigTransactionClient.java @@ -16,7 +16,7 @@ import org.opendaylight.controller.config.api.jmx.CommitStatus; import org.opendaylight.controller.config.api.jmx.ConfigTransactionControllerMXBean; public interface ConfigTransactionClient extends - ConfigTransactionControllerMXBean { + ConfigTransactionControllerMXBean, BeanReader { CommitStatus commit() throws ConflictingVersionException, ValidationException; @@ -47,7 +47,7 @@ public interface ConfigTransactionClient extends * @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); } diff --git a/opendaylight/config/config-util/src/main/java/org/opendaylight/controller/config/util/ConfigTransactionJMXClient.java b/opendaylight/config/config-util/src/main/java/org/opendaylight/controller/config/util/ConfigTransactionJMXClient.java index a0af19796e..26ca1391ad 100644 --- a/opendaylight/config/config-util/src/main/java/org/opendaylight/controller/config/util/ConfigTransactionJMXClient.java +++ b/opendaylight/config/config-util/src/main/java/org/opendaylight/controller/config/util/ConfigTransactionJMXClient.java @@ -240,6 +240,26 @@ public class ConfigTransactionJMXClient implements ConfigTransactionClient { 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 { @@ -273,22 +293,17 @@ public class ConfigTransactionJMXClient implements ConfigTransactionClient { } @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 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 lookupRuntimeBeans() { + return configTransactionControllerMXBeanProxy.lookupRuntimeBeans(); } @Override - public Set getAvailableModuleFactoryQNames() { - return configTransactionControllerMXBeanProxy.getAvailableModuleFactoryQNames(); + public Set lookupRuntimeBeans(final String moduleName, final String instanceName) { + return configTransactionControllerMXBeanProxy.lookupRuntimeBeans(moduleName, instanceName); } } diff --git a/opendaylight/config/config-util/src/test/java/org/opendaylight/controller/config/util/TestingConfigTransactionController.java b/opendaylight/config/config-util/src/test/java/org/opendaylight/controller/config/util/TestingConfigTransactionController.java index e1138addc7..e69019405d 100644 --- a/opendaylight/config/config-util/src/test/java/org/opendaylight/controller/config/util/TestingConfigTransactionController.java +++ b/opendaylight/config/config-util/src/test/java/org/opendaylight/controller/config/util/TestingConfigTransactionController.java @@ -8,6 +8,7 @@ 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; @@ -184,6 +185,16 @@ public class TestingConfigTransactionController implements return Sets.newHashSet("availableModuleFactoryQNames"); } + @Override + public Set lookupRuntimeBeans() { + return Collections.emptySet(); + } + + @Override + public Set lookupRuntimeBeans(final String moduleName, final String instanceName) { + return Collections.emptySet(); + } + @Override public ObjectName getServiceReference(String serviceInterfaceQName, String refName) throws InstanceNotFoundException { return conf3; diff --git a/opendaylight/config/yang-jmx-generator-plugin/pom.xml b/opendaylight/config/yang-jmx-generator-plugin/pom.xml index 6c8a591bb8..ae190848c9 100644 --- a/opendaylight/config/yang-jmx-generator-plugin/pom.xml +++ b/opendaylight/config/yang-jmx-generator-plugin/pom.xml @@ -21,11 +21,6 @@ guava - - com.jcabi - jcabi-maven-slf4j - - commons-io commons-io @@ -36,17 +31,6 @@ commons-lang3 - - org.codehaus.gmaven.runtime - gmaven-runtime-2.0 - - - org.sonatype.gossip - gossip - - - - org.opendaylight.controller yang-jmx-generator @@ -66,6 +50,10 @@ org.opendaylight.yangtools yang-maven-plugin-spi + + org.apache.maven + maven-core + org.slf4j diff --git a/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/JMXGenerator.java b/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/JMXGenerator.java index 9ad8d2826f..1f1776f0a5 100644 --- a/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/JMXGenerator.java +++ b/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/JMXGenerator.java @@ -23,7 +23,6 @@ import java.util.Set; 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; @@ -35,42 +34,54 @@ import org.opendaylight.yangtools.yang.common.QName; 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 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 generateSources(SchemaContext context, - File outputBaseDir, Set yangModulesInCurrentMavenModule) { + public Collection generateSources(final SchemaContext context, + final File outputBaseDir, final Set yangModulesInCurrentMavenModule) { Preconditions.checkArgument(context != null, "Null context received"); Preconditions.checkArgument(outputBaseDir != null, @@ -173,7 +184,8 @@ public class JMXGenerator implements CodeGenerator { 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); @@ -183,18 +195,14 @@ public class JMXGenerator implements CodeGenerator { } @Override - public void setAdditionalConfig(Map additionalCfg) { - if (LOG != null) { - LOG.debug(getClass().getCanonicalName(), - ": Additional configuration received: ", - additionalCfg.toString()); - } + public void setAdditionalConfig(final Map additionalCfg) { + LOG.debug("{}: Additional configuration received: {}", getClass().getCanonicalName(), additionalCfg); this.namespaceToPackageMapping = extractNamespaceMapping(additionalCfg); this.generateModuleFactoryFile = extractModuleFactoryBoolean(additionalCfg); } private boolean extractModuleFactoryBoolean( - Map additionalCfg) { + final Map additionalCfg) { String bool = additionalCfg.get(MODULE_FACTORY_FILE_BOOLEAN); if (bool == null) { return true; @@ -205,13 +213,8 @@ public class JMXGenerator implements CodeGenerator { return true; } - @Override - public void setLog(Log log) { - StaticLoggerBinder.getSingleton().setMavenLog(log); - } - private static Map extractNamespaceMapping( - Map additionalCfg) { + final Map additionalCfg) { Map namespaceToPackage = Maps.newHashMap(); for (String key : additionalCfg.keySet()) { if (key.startsWith(NAMESPACE_TO_PACKAGE_PREFIX)) { @@ -224,46 +227,30 @@ public class JMXGenerator implements CodeGenerator { 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 files = Sets.newHashSet(); - void addFile(File file) { + void addFile(final File file) { if (files.contains(file)) { List undeletedFiles = Lists.newArrayList(); for (File presentFile : files) { @@ -283,7 +270,7 @@ public class JMXGenerator implements CodeGenerator { files.add(file); } - void addFile(Collection files) { + void addFile(final Collection files) { for (File file : files) { addFile(file); } diff --git a/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/TemplateFactory.java b/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/TemplateFactory.java index 19e875f9b1..00454d8acf 100644 --- a/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/TemplateFactory.java +++ b/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/TemplateFactory.java @@ -48,9 +48,9 @@ import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.Meth 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 { @@ -59,7 +59,7 @@ public class TemplateFactory { * bean as value that should be persisted from this instance. */ public static Map getTOAndMXInterfaceFtlFiles( - RuntimeBeanEntry entry) { + final RuntimeBeanEntry entry) { Map result = new HashMap<>(); { // create GeneralInterfaceFtlFile for runtime MXBean. Attributes will // be transformed to getter methods @@ -109,7 +109,7 @@ public class TemplateFactory { } // 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(); @@ -131,11 +131,11 @@ public class TemplateFactory { } } - 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(); @@ -151,7 +151,7 @@ public class TemplateFactory { } public static GeneralInterfaceTemplate serviceInterfaceFromSie( - ServiceInterfaceEntry sie) { + final ServiceInterfaceEntry sie) { List extendedInterfaces = Lists .newArrayList(AbstractServiceInterface.class.getCanonicalName()); @@ -177,7 +177,7 @@ public class TemplateFactory { } public static AbstractFactoryTemplate abstractFactoryTemplateFromMbe( - ModuleMXBeanEntry mbe) { + final ModuleMXBeanEntry mbe) { AbstractFactoryAttributesProcessor attrProcessor = new AbstractFactoryAttributesProcessor(); attrProcessor.processAttributes(mbe.getAttributes(), mbe.getPackageName()); @@ -191,7 +191,7 @@ public class TemplateFactory { } public static AbstractModuleTemplate abstractModuleTemplateFromMbe( - ModuleMXBeanEntry mbe) { + final ModuleMXBeanEntry mbe) { AbstractModuleAttributesProcessor attrProcessor = new AbstractModuleAttributesProcessor(mbe.getAttributes()); List moduleFields = attrProcessor.getModuleFields(); @@ -234,7 +234,7 @@ public class TemplateFactory { } public static StubFactoryTemplate stubFactoryTemplateFromMbe( - ModuleMXBeanEntry mbe) { + final ModuleMXBeanEntry mbe) { return new StubFactoryTemplate(getHeaderFromEntry(mbe), mbe.getPackageName(), mbe.getStubFactoryName(), mbe.getFullyQualifiedName(mbe.getAbstractFactoryName()) @@ -242,7 +242,7 @@ public class TemplateFactory { } public static GeneralInterfaceTemplate mXBeanInterfaceTemplateFromMbe( - ModuleMXBeanEntry mbe) { + final ModuleMXBeanEntry mbe) { MXBeanInterfaceAttributesProcessor attrProcessor = new MXBeanInterfaceAttributesProcessor(); attrProcessor.processAttributes(mbe.getAttributes()); GeneralInterfaceTemplate ifcTemplate = new GeneralInterfaceTemplate( @@ -254,7 +254,7 @@ public class TemplateFactory { } public static Map tOsFromMbe( - ModuleMXBeanEntry mbe) { + final ModuleMXBeanEntry mbe) { Map retVal = Maps.newHashMap(); TOAttributesProcessor processor = new TOAttributesProcessor(); processor.processAttributes(mbe.getAttributes()); @@ -275,7 +275,7 @@ public class TemplateFactory { } public static Map tOsFromRbe( - RuntimeBeanEntry rbe) { + final RuntimeBeanEntry rbe) { Map retVal = Maps.newHashMap(); TOAttributesProcessor processor = new TOAttributesProcessor(); Map yangPropertiesToTypesMap = Maps.newHashMap(rbe.getYangPropertiesToTypesMap()); @@ -316,7 +316,7 @@ public class TemplateFactory { return retVal; } - private static Header getHeaderFromEntry(AbstractEntry mbe) { + private static Header getHeaderFromEntry(final AbstractEntry mbe) { return new Header(mbe.getYangModuleName(), mbe.getYangModuleLocalname()); } @@ -326,7 +326,7 @@ public class TemplateFactory { private final List tos = Lists.newArrayList(); - void processAttributes(Map attributes) { + void processAttributes(final Map attributes) { for (Entry attrEntry : attributes.entrySet()) { AttributeIfc attributeIfc = attrEntry.getValue(); if (attributeIfc instanceof TOAttribute) { @@ -342,7 +342,7 @@ public class TemplateFactory { } } - private void createTOInternal(TOAttribute toAttribute) { + private void createTOInternal(final TOAttribute toAttribute) { Map attrs = toAttribute.getCapitalizedPropertiesToTypesMap(); // recursive processing of TO's attributes @@ -360,12 +360,12 @@ public class TemplateFactory { private List fields; private List methods; - public TOInternal(Type type, Map attrs) { + public TOInternal(final Type type, final Map attrs) { this(type.getFullyQualifiedName(), type.getName(), attrs, type.getPackageName()); } - public TOInternal(String fullyQualifiedName, String name, - Map attrs, String packageName) { + public TOInternal(final String fullyQualifiedName, final String name, + final Map attrs, final String packageName) { this.fullyQualifiedName = fullyQualifiedName; this.name = name; processAttrs(attrs, packageName); @@ -374,7 +374,7 @@ public class TemplateFactory { private final static String dependencyResolverVarName = "dependencyResolver"; private final static String dependencyResolverInjectMethodName = "injectDependencyResolver"; - private void processAttrs(Map attrs, String packageName) { + private void processAttrs(final Map attrs, final String packageName) { fields = Lists.newArrayList(); methods = Lists.newArrayList(); @@ -386,8 +386,7 @@ public class TemplateFactory { for (Entry 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) { @@ -449,7 +448,7 @@ public class TemplateFactory { private static class MXBeanInterfaceAttributesProcessor { private final List methods = Lists.newArrayList(); - void processAttributes(Map attributes) { + void processAttributes(final Map attributes) { for (Entry attrEntry : attributes.entrySet()) { String returnType; AttributeIfc attributeIfc = attrEntry.getValue(); @@ -473,8 +472,7 @@ public class TemplateFactory { MethodDeclaration getter = new MethodDeclaration(returnType, getterName, Collections. emptyList()); - String varName = BindingGeneratorUtil - .parseToValidParamName(attrEntry.getKey()); + String varName = BindingMapping.getPropertyName(attrEntry.getKey()); String setterName = "set" + attributeIfc.getUpperCaseCammelCase(); MethodDeclaration setter = new MethodDeclaration("void", @@ -519,8 +517,8 @@ public class TemplateFactory { private final List fields = Lists.newArrayList(); - void processAttributes(Map attributes, - String packageName) { + void processAttributes(final Map attributes, + final String packageName) { for (Entry attrEntry : attributes.entrySet()) { String type; String nullableDefaultWrapped = null; @@ -550,7 +548,7 @@ public class TemplateFactory { private final List moduleFields; private final List methods; - private Holder(List moduleFields, List methods) { + private Holder(final List moduleFields, final List methods) { this.moduleFields = Collections.unmodifiableList(moduleFields); this.methods = Collections.unmodifiableList(methods); } @@ -559,11 +557,11 @@ public class TemplateFactory { private final Holder holder; - private AbstractModuleAttributesProcessor(Map attributes) { + private AbstractModuleAttributesProcessor(final Map attributes) { this.holder = processAttributes(attributes); } - private static Holder processAttributes(Map attributes) { + private static Holder processAttributes(final Map attributes) { List moduleFields = new ArrayList<>(); List methods = new ArrayList<>(); for (Entry attrEntry : attributes.entrySet()) { @@ -607,8 +605,7 @@ public class TemplateFactory { } } - String varName = BindingGeneratorUtil - .parseToValidParamName(attrEntry.getKey()); + String varName = BindingMapping.getPropertyName(attrEntry.getKey()); ModuleField field; if (isIdentity) { @@ -689,7 +686,7 @@ public class TemplateFactory { } - private static boolean needsDepResolver(AttributeIfc value) { + private static boolean needsDepResolver(final AttributeIfc value) { if(value instanceof TOAttribute) { return true; } @@ -701,7 +698,7 @@ public class TemplateFactory { 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); diff --git a/opendaylight/config/yang-jmx-generator-plugin/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/JMXGeneratorTest.java b/opendaylight/config/yang-jmx-generator-plugin/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/JMXGeneratorTest.java index a6cfc58c34..3dae004161 100644 --- a/opendaylight/config/yang-jmx-generator-plugin/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/JMXGeneratorTest.java +++ b/opendaylight/config/yang-jmx-generator-plugin/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/JMXGeneratorTest.java @@ -16,11 +16,8 @@ import static org.junit.Assert.assertNotNull; 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; @@ -42,7 +39,6 @@ import java.util.Set; 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; @@ -124,13 +120,6 @@ public class JMXGeneratorTest extends AbstractGeneratorTest { 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); @@ -158,18 +147,19 @@ public class JMXGeneratorTest extends AbstractGeneratorTest { 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 toFileNames(Collection files) { + public static List toFileNames(final Collection files) { List result = new ArrayList<>(); for (File f : files) { result.add(f.getName()); @@ -279,7 +269,7 @@ public class JMXGeneratorTest extends AbstractGeneratorTest { new Predicate() { @Override - public boolean apply(File input) { + public boolean apply(final File input) { return input.getName().endsWith("xml"); } }); @@ -288,7 +278,7 @@ public class JMXGeneratorTest extends AbstractGeneratorTest { new Predicate() { @Override - public boolean apply(File input) { + public boolean apply(final File input) { return input.getName().endsWith("java"); } }); @@ -303,16 +293,21 @@ public class JMXGeneratorTest extends AbstractGeneratorTest { 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); @@ -348,25 +343,25 @@ public class JMXGeneratorTest extends AbstractGeneratorTest { } - private void verifyXmlFiles(Collection xmlFiles) throws Exception { + private void verifyXmlFiles(final Collection 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()); } @@ -386,7 +381,7 @@ public class JMXGeneratorTest extends AbstractGeneratorTest { } - private void assertEventBusModuleFactory(MbeASTVisitor visitor) { + private void assertEventBusModuleFactory(final MbeASTVisitor visitor) { assertEquals(PackageTranslatorTest.EXPECTED_PACKAGE_PREFIX + ".threads.java", visitor.packageName); assertEquals("EventBusModuleFactory", visitor.type); @@ -406,7 +401,7 @@ public class JMXGeneratorTest extends AbstractGeneratorTest { 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); @@ -427,7 +422,7 @@ public class JMXGeneratorTest extends AbstractGeneratorTest { } private void assertAbstractNamingThreadFactoryModuleFactory( - MbeASTVisitor visitor) { + final MbeASTVisitor visitor) { assertEquals(PackageTranslatorTest.EXPECTED_PACKAGE_PREFIX + ".threads.java", visitor.packageName); assertEquals("AbstractNamingThreadFactoryModuleFactory", visitor.type); @@ -450,7 +445,7 @@ public class JMXGeneratorTest extends AbstractGeneratorTest { } - private void assertFactoryMethods(Set methods, int expectedSize) { + private void assertFactoryMethods(final Set methods, final int expectedSize) { List args = Lists.newArrayList(); ArgumentAssertion oldInstanceArg = new ArgumentAssertion(DynamicMBeanWithInstance.class.getCanonicalName(), "old"); @@ -496,12 +491,12 @@ public class JMXGeneratorTest extends AbstractGeneratorTest { } - private void assertMethodPresent(Set methods, MethodAssertion methodAssertion) { + private void assertMethodPresent(final Set 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); @@ -511,7 +506,7 @@ public class JMXGeneratorTest extends AbstractGeneratorTest { } - private void assertAbstractDynamicThreadPoolModule(MbeASTVisitor visitor) { + private void assertAbstractDynamicThreadPoolModule(final MbeASTVisitor visitor) { assertEquals(PackageTranslatorTest.EXPECTED_PACKAGE_PREFIX + ".threads.java", visitor.packageName); assertNotNull(visitor.javadoc); @@ -557,8 +552,8 @@ public class JMXGeneratorTest extends AbstractGeneratorTest { visitor.methodJavadoc.get("void setMaximumSize(java.lang.Long maximumSize)")); } - private void assertDeclaredField(Set fieldDeclarations, - String declaration) { + private void assertDeclaredField(final Set fieldDeclarations, + final String declaration) { assertTrue("Missing field " + declaration + ", got: " + fieldDeclarations, fieldDeclarations.contains(declaration + ";\n")); @@ -570,13 +565,13 @@ public class JMXGeneratorTest extends AbstractGeneratorTest { protected Map 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) { @@ -604,7 +599,7 @@ public class JMXGeneratorTest extends AbstractGeneratorTest { } @Override - public boolean visit(TypeDeclaration node) { + public boolean visit(final TypeDeclaration node) { javadoc = node.getJavadoc() == null ? null : node.getJavadoc() .toString(); type = node.getName().toString(); @@ -624,7 +619,7 @@ public class JMXGeneratorTest extends AbstractGeneratorTest { private final Map 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()) @@ -638,16 +633,16 @@ public class JMXGeneratorTest extends AbstractGeneratorTest { } @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()) { @@ -668,7 +663,7 @@ public class JMXGeneratorTest extends AbstractGeneratorTest { } @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 @@ -680,14 +675,14 @@ public class JMXGeneratorTest extends AbstractGeneratorTest { } - 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 files, - Map verifiers) throws IOException { + private void processGeneratedCode(final Collection files, + final Map verifiers) throws IOException { ASTParser parser = ASTParser.newParser(AST.JLS3); Map options = JavaCore.getOptions(); JavaCore.setComplianceOptions(JavaCore.VERSION_1_7, options); @@ -705,27 +700,31 @@ public class JMXGeneratorTest extends AbstractGeneratorTest { 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 readLines = Files .readLines(file, Charset.forName("utf-8")); char[] retVal = new char[0]; @@ -741,15 +740,15 @@ public class JMXGeneratorTest extends AbstractGeneratorTest { private static class MethodAssertion extends ArgumentAssertion{ - private List arguments; + private final List arguments; - MethodAssertion(String type, String name, List arguments) { + MethodAssertion(final String type, final String name, final List arguments) { super(type, name); this.arguments = arguments; } - MethodAssertion(String type, String name) { + MethodAssertion(final String type, final String name) { this(type, name, Collections.emptyList()); } @@ -763,8 +762,9 @@ public class JMXGeneratorTest extends AbstractGeneratorTest { 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(); @@ -775,7 +775,7 @@ public class JMXGeneratorTest extends AbstractGeneratorTest { 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; } diff --git a/opendaylight/md-sal/messagebus-impl/pom.xml b/opendaylight/md-sal/messagebus-impl/pom.xml index 8e088ba578..ccb72191c4 100644 --- a/opendaylight/md-sal/messagebus-impl/pom.xml +++ b/opendaylight/md-sal/messagebus-impl/pom.xml @@ -19,7 +19,6 @@ org.opendaylight.controller ietf-netconf-notifications - 0.3.0-SNAPSHOT org.opendaylight.controller @@ -95,7 +94,6 @@ org.codehaus.mojo build-helper-maven-plugin - 1.8 add-source diff --git a/opendaylight/md-sal/messagebus-impl/src/main/java/org/opendaylight/controller/messagebus/app/impl/Topic.java b/opendaylight/md-sal/messagebus-impl/src/main/java/org/opendaylight/controller/messagebus/app/impl/Topic.java index aebde0c043..a32413064e 100644 --- a/opendaylight/md-sal/messagebus-impl/src/main/java/org/opendaylight/controller/messagebus/app/impl/Topic.java +++ b/opendaylight/md-sal/messagebus-impl/src/main/java/org/opendaylight/controller/messagebus/app/impl/Topic.java @@ -63,10 +63,7 @@ public class Topic implements DataChangeListener { 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); } diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/AbstractReplicatedLogImpl.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/AbstractReplicatedLogImpl.java index e2aa16918e..1aecc89eea 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/AbstractReplicatedLogImpl.java +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/AbstractReplicatedLogImpl.java @@ -7,6 +7,7 @@ */ package org.opendaylight.controller.cluster.raft; +import com.google.common.base.Preconditions; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -187,9 +188,14 @@ public abstract class AbstractReplicatedLogImpl implements ReplicatedLog { @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 snapshotJournalEntries = journal.subList(0, (int) (snapshotCapturedIndex - snapshotIndex)); + + snapshottedJournal.addAll(snapshotJournalEntries); clear(0, (int) (snapshotCapturedIndex - snapshotIndex)); previousSnapshotIndex = snapshotIndex; diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/ConfigParams.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/ConfigParams.java index 78a1335d58..fd49737cac 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/ConfigParams.java +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/ConfigParams.java @@ -75,7 +75,7 @@ public interface ConfigParams { * The interval in which the leader needs to check itself if its isolated * @return FiniteDuration */ - FiniteDuration getIsolatedCheckInterval(); + long getIsolatedCheckIntervalInMillis(); /** diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/DefaultConfigParamsImpl.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/DefaultConfigParamsImpl.java index 86867e1d04..3e6742c17d 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/DefaultConfigParamsImpl.java +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/DefaultConfigParamsImpl.java @@ -42,8 +42,7 @@ public class DefaultConfigParamsImpl implements ConfigParams { 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 @@ -68,7 +67,7 @@ public class DefaultConfigParamsImpl implements ConfigParams { } public void setIsolatedLeaderCheckInterval(FiniteDuration isolatedLeaderCheckInterval) { - this.isolatedLeaderCheckInterval = isolatedLeaderCheckInterval; + this.isolatedLeaderCheckInterval = isolatedLeaderCheckInterval.toMillis(); } public void setElectionTimeoutFactor(long electionTimeoutFactor){ @@ -112,7 +111,7 @@ public class DefaultConfigParamsImpl implements ConfigParams { } @Override - public FiniteDuration getIsolatedCheckInterval() { + public long getIsolatedCheckIntervalInMillis() { return isolatedLeaderCheckInterval; } diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/FollowerLogInformationImpl.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/FollowerLogInformationImpl.java index 935c4f0bbd..04b9f163f4 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/FollowerLogInformationImpl.java +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/FollowerLogInformationImpl.java @@ -100,4 +100,16 @@ public class FollowerLogInformationImpl implements FollowerLogInformation { 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(); + } + + } diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/RaftActor.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/RaftActor.java index 854ceb23d0..285be39c0b 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/RaftActor.java +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/RaftActor.java @@ -22,6 +22,7 @@ import com.google.common.base.Stopwatch; 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; @@ -82,6 +83,9 @@ import org.slf4j.LoggerFactory; * */ 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()); /** @@ -278,6 +282,12 @@ public abstract class RaftActor extends AbstractUntypedPersistentActor { 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(), @@ -667,12 +677,22 @@ public abstract class RaftActor extends AbstractUntypedPersistentActor { 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(), @@ -788,7 +808,9 @@ public abstract class RaftActor extends AbstractUntypedPersistentActor { 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; diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/base/messages/ApplyState.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/base/messages/ApplyState.java index 0a7a632880..9299e752d1 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/base/messages/ApplyState.java +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/base/messages/ApplyState.java @@ -9,21 +9,22 @@ 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() { @@ -37,4 +38,17 @@ public class ApplyState implements Serializable { public ReplicatedLogEntry getReplicatedLogEntry() { return replicatedLogEntry; } + + public long getStartTime() { + return startTime; + } + + @Override + public String toString() { + return "ApplyState{" + + "identifier='" + identifier + '\'' + + ", replicatedLogEntry.index =" + replicatedLogEntry.getIndex() + + ", startTime=" + startTime + + '}'; + } } diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/behaviors/AbstractLeader.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/behaviors/AbstractLeader.java index b2bb127eab..be51ba069c 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/behaviors/AbstractLeader.java +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/behaviors/AbstractLeader.java @@ -126,6 +126,9 @@ public abstract class AbstractLeader extends AbstractRaftActorBehavior { // (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()); } /** @@ -171,6 +174,14 @@ public abstract class AbstractLeader extends AbstractRaftActorBehavior { 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()) { @@ -273,6 +284,8 @@ public abstract class AbstractLeader extends AbstractRaftActorBehavior { return this; } + protected void beforeSendHeartbeat(){} + @Override public RaftActorBehavior handleMessage(ActorRef sender, Object originalMessage) { Preconditions.checkNotNull(sender, "sender should not be null"); @@ -294,27 +307,26 @@ public abstract class AbstractLeader extends AbstractRaftActorBehavior { } } - 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); } diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/behaviors/Follower.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/behaviors/Follower.java index 1e4fcf7225..c799441d60 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/behaviors/Follower.java +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/behaviors/Follower.java @@ -10,7 +10,6 @@ package org.opendaylight.controller.cluster.raft.behaviors; 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; @@ -330,12 +329,13 @@ public class Follower extends AbstractRaftActorBehavior { } } - @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; } } diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/behaviors/Leader.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/behaviors/Leader.java index 7a94c0c158..ebcdcd40fb 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/behaviors/Leader.java +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/behaviors/Leader.java @@ -8,12 +8,12 @@ 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 @@ -38,15 +38,12 @@ import scala.concurrent.duration.FiniteDuration; * 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) { @@ -54,8 +51,9 @@ public class Leader extends AbstractLeader { 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)); } } @@ -63,21 +61,17 @@ public class Leader extends AbstractLeader { 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(); } diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/InstallSnapshot.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/InstallSnapshot.java index 119b43ce83..13636f36d7 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/InstallSnapshot.java +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/InstallSnapshot.java @@ -73,6 +73,7 @@ public class InstallSnapshot extends AbstractRaftRPC { public Object toSerializable(){ InstallSnapshotMessages.InstallSnapshot.Builder builder = InstallSnapshotMessages.InstallSnapshot.newBuilder() + .setTerm(this.getTerm()) .setLeaderId(this.getLeaderId()) .setChunkIndex(this.getChunkIndex()) .setData(this.getData()) diff --git a/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/MockRaftActorContext.java b/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/MockRaftActorContext.java index 4d33152b41..297d781251 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/MockRaftActorContext.java +++ b/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/MockRaftActorContext.java @@ -53,7 +53,7 @@ public class MockRaftActorContext implements RaftActorContext { * 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 @@ -88,8 +88,9 @@ public class MockRaftActorContext implements RaftActorContext { 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) { @@ -133,6 +134,16 @@ public class MockRaftActorContext implements RaftActorContext { } @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; } @@ -255,6 +266,36 @@ public class MockRaftActorContext implements RaftActorContext { 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 { @@ -287,6 +328,52 @@ public class MockRaftActorContext implements RaftActorContext { 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 { diff --git a/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/RaftActorTest.java b/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/RaftActorTest.java index 1cd8550be7..56bfc21f23 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/RaftActorTest.java +++ b/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/RaftActorTest.java @@ -552,7 +552,6 @@ public class RaftActorTest extends AbstractActorTest { assertNotEquals("voted for", "foobar", mockRaftActor.getRaftActorContext().getTermInformation().getVotedFor()); mockRaftActor.onReceiveRecover(mock(RecoveryCompleted.class)); - }}; } @@ -576,12 +575,12 @@ public class RaftActorTest extends AbstractActorTest { MockRaftActor mockRaftActor = mockActorRef.underlyingActor(); + mockRaftActor.waitForInitializeBehaviorComplete(); + mockRaftActor.getRaftActorContext().getTermInformation().updateAndPersist(10, "foobar"); assertEquals("Persist called", true, persistLatch.await(5, TimeUnit.SECONDS)); - } - }; } @@ -602,14 +601,14 @@ public class RaftActorTest extends AbstractActorTest { 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)); - } - }; } @@ -630,14 +629,14 @@ public class RaftActorTest extends AbstractActorTest { 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)); - } - }; } @@ -658,6 +657,8 @@ public class RaftActorTest extends AbstractActorTest { MockRaftActor mockRaftActor = mockActorRef.underlyingActor(); + mockRaftActor.waitForInitializeBehaviorComplete(); + mockRaftActor.onReceiveCommand(new ApplyLogEntries(10)); verify(dataPersistenceProvider, times(1)).persist(anyObject(), any(Procedure.class)); @@ -685,6 +686,8 @@ public class RaftActorTest extends AbstractActorTest { MockRaftActor mockRaftActor = mockActorRef.underlyingActor(); + mockRaftActor.waitForInitializeBehaviorComplete(); + ByteString snapshotBytes = fromObject(Arrays.asList( new MockRaftActorContext.MockPayload("A"), new MockRaftActorContext.MockPayload("B"), @@ -722,6 +725,8 @@ public class RaftActorTest extends AbstractActorTest { 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))); @@ -783,6 +788,8 @@ public class RaftActorTest extends AbstractActorTest { MockRaftActor mockRaftActor = mockActorRef.underlyingActor(); + mockRaftActor.waitForInitializeBehaviorComplete(); + ReplicatedLogEntry entry = new MockRaftActorContext.MockReplicatedLogEntry(1, 5, new MockRaftActorContext.MockPayload("F")); @@ -811,6 +818,8 @@ public class RaftActorTest extends AbstractActorTest { MockRaftActor mockRaftActor = mockActorRef.underlyingActor(); + mockRaftActor.waitForInitializeBehaviorComplete(); + ReplicatedLog oldReplicatedLog = mockRaftActor.getReplicatedLog(); oldReplicatedLog.append(new MockRaftActorContext.MockReplicatedLogEntry(1, 0, mock(Payload.class))); @@ -864,6 +873,8 @@ public class RaftActorTest extends AbstractActorTest { MockRaftActor mockRaftActor = mockActorRef.underlyingActor(); + mockRaftActor.waitForInitializeBehaviorComplete(); + ByteString snapshotBytes = fromObject(Arrays.asList( new MockRaftActorContext.MockPayload("A"), new MockRaftActorContext.MockPayload("B"), @@ -892,33 +903,44 @@ public class RaftActorTest extends AbstractActorTest { 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.emptyMap(), Optional.of(config), notifierActor), persistenceId); - // sleeping for a minimum of 2 seconds, if it spans more its fine. - Uninterruptibles.sleepUninterruptibly(2, TimeUnit.SECONDS); + List 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 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()); @@ -944,11 +966,12 @@ public class RaftActorTest extends AbstractActorTest { Map peerAddresses = new HashMap<>(); peerAddresses.put(follower1Id, followerActor1.path().toString()); - TestActorRef mockActorRef = TestActorRef.create(getSystem(), + TestActorRef mockActorRef = factory.createTestActor( MockRaftActor.props(persistenceId, peerAddresses, Optional.of(config), dataPersistenceProvider), persistenceId); MockRaftActor leaderActor = mockActorRef.underlyingActor(); + leaderActor.getRaftActorContext().setCommitIndex(4); leaderActor.getRaftActorContext().setLastApplied(4); leaderActor.getRaftActorContext().getTermInformation().update(1, persistenceId); @@ -1034,7 +1057,7 @@ public class RaftActorTest extends AbstractActorTest { Map peerAddresses = new HashMap<>(); peerAddresses.put(leaderId, leaderActor1.path().toString()); - TestActorRef mockActorRef = TestActorRef.create(getSystem(), + TestActorRef mockActorRef = factory.createTestActor( MockRaftActor.props(persistenceId, peerAddresses, Optional.of(config), dataPersistenceProvider), persistenceId); @@ -1204,6 +1227,128 @@ public class RaftActorTest extends AbstractActorTest { }; } + + private static class NonPersistentProvider implements DataPersistenceProvider { + @Override + public boolean isRecoveryApplicable() { + return false; + } + + @Override + public void persist(T o, Procedure 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 peerAddresses = new HashMap<>(); + + TestActorRef mockActorRef = factory.createTestActor( + MockRaftActor.props(persistenceId, peerAddresses, + Optional.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 peerAddresses = new HashMap<>(); + + TestActorRef mockActorRef = factory.createTestActor( + MockRaftActor.props(persistenceId, peerAddresses, + Optional.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; diff --git a/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/TestActorFactory.java b/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/TestActorFactory.java index 6872c8fa45..b47df13fed 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/TestActorFactory.java +++ b/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/TestActorFactory.java @@ -21,6 +21,7 @@ import akka.actor.ActorRef; 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; @@ -108,10 +109,14 @@ public class TestActorFactory implements AutoCloseable { } @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 diff --git a/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/behaviors/AbstractLeaderTest.java b/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/behaviors/AbstractLeaderTest.java new file mode 100644 index 0000000000..dd3ed2347c --- /dev/null +++ b/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/behaviors/AbstractLeaderTest.java @@ -0,0 +1,86 @@ +/* + * 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 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 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 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); + + }}; + } + +} diff --git a/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/behaviors/AbstractRaftActorBehaviorTest.java b/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/behaviors/AbstractRaftActorBehaviorTest.java index c133c0615f..f56755b447 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/behaviors/AbstractRaftActorBehaviorTest.java +++ b/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/behaviors/AbstractRaftActorBehaviorTest.java @@ -1,30 +1,52 @@ 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 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 @@ -34,22 +56,19 @@ public abstract class AbstractRaftActorBehaviorTest extends AbstractActorTest { */ @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()); - - - }}; } @@ -61,144 +80,95 @@ public abstract class AbstractRaftActorBehaviorTest extends AbstractActorTest { * @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(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 entries = new ArrayList<>(); - entries.add( - new MockRaftActorContext.MockReplicatedLogEntry(1, 0, new MockRaftActorContext.MockPayload("zero"))); + List 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 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(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()); } /** @@ -207,51 +177,24 @@ public abstract class AbstractRaftActorBehaviorTest extends AbstractActorTest { * 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(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()); } @@ -263,40 +206,17 @@ public abstract class AbstractRaftActorBehaviorTest extends AbstractActorTest { */ @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(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 @@ -346,18 +266,21 @@ public abstract class AbstractRaftActorBehaviorTest extends AbstractActorTest { } - 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( @@ -366,10 +289,9 @@ public abstract class AbstractRaftActorBehaviorTest extends AbstractActorTest { 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); @@ -383,11 +305,11 @@ public abstract class AbstractRaftActorBehaviorTest extends AbstractActorTest { 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); } @@ -410,4 +332,18 @@ public abstract class AbstractRaftActorBehaviorTest extends AbstractActorTest { protected Object fromSerializableMessage(Object serializable){ return SerializationUtils.fromSerializable(serializable); } + + protected ByteString toByteString(Map 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); + } } diff --git a/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/behaviors/CandidateTest.java b/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/behaviors/CandidateTest.java index 0dc68c2461..60f45523cf 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/behaviors/CandidateTest.java +++ b/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/behaviors/CandidateTest.java @@ -1,68 +1,48 @@ 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 candidateActor = actorFactory.createTestActor( + Props.create(MessageCollectorActor.class), actorFactory.generateActorId("candidate")); - private final ActorRef peerActor2 = getSystem().actorOf(Props.create( - DoNothingActor.class)); + private TestActorRef[] peerActors; - private final ActorRef peerActor3 = getSystem().actorOf(Props.create( - DoNothingActor.class)); - - private final ActorRef peerActor4 = getSystem().actorOf(Props.create( - DoNothingActor.class)); - - private final Map onePeer = new HashMap<>(); - private final Map twoPeers = new HashMap<>(); - private final Map 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 @@ -70,230 +50,167 @@ public class CandidateTest extends AbstractRaftActorBehaviorTest { 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(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.emptyList(), 0, -1)); - - final Boolean out = new ExpectMsg(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(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.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(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(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 setupPeers(int count) { + Map 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()); + } } diff --git a/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/behaviors/FollowerTest.java b/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/behaviors/FollowerTest.java index 719a8256a0..4e8e7fe11b 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/behaviors/FollowerTest.java +++ b/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/behaviors/FollowerTest.java @@ -1,156 +1,121 @@ 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 followerActor = actorFactory.createTestActor( + Props.create(MessageCollectorActor.class), actorFactory.generateActorId("follower")); + private final TestActorRef 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(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(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(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()); } /** @@ -163,32 +128,25 @@ public class FollowerTest extends AbstractRaftActorBehaviorTest { */ @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 entries = - Arrays.asList( - (ReplicatedLogEntry) new MockRaftActorContext.MockReplicatedLogEntry(2, 101, - new MockRaftActorContext.MockPayload("foo")) - ); + List entries = Arrays.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()); } /** @@ -199,58 +157,30 @@ public class FollowerTest extends AbstractRaftActorBehaviorTest { * @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(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 @@ -260,278 +190,201 @@ public class FollowerTest extends AbstractRaftActorBehaviorTest { * @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 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(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 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 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(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 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 entries = new ArrayList<>(); - entries.add( - new MockRaftActorContext.MockReplicatedLogEntry(1, 4, new MockRaftActorContext.MockPayload("two-1"))); + // Prepare the entries to be sent with AppendEntries + List 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(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 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 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(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 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); } @@ -543,182 +396,137 @@ public class FollowerTest extends AbstractRaftActorBehaviorTest { */ @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 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.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 listMessages = (List) messages; - - int installSnapshotReplyReceivedCount = 0; - for (Object message: listMessages) { - if (message instanceof InstallSnapshotReply) { - ++installSnapshotReplyReceivedCount; - } - } + logStart("testHandleInstallSnapshot"); + + MockRaftActorContext context = createActorContext(); + + follower = createBehavior(context); + + HashMap 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 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 followerSnapshot = new HashMap<>(); - followerSnapshot.put("1", "A"); - followerSnapshot.put("2", "B"); - followerSnapshot.put("3", "C"); + follower = createBehavior(context); - ByteString bsSnapshot = toByteString(followerSnapshot); + HashMap 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 listMessages = (List) 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 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 replyActor) + throws Exception { + AppendEntriesReply reply = MessageCollectorActor.expectFirstMatching(replyActor, AppendEntriesReply.class); + assertEquals("isSuccess", true, reply.isSuccess()); } } diff --git a/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/behaviors/IsolatedLeaderTest.java b/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/behaviors/IsolatedLeaderTest.java index 708068a789..e16d765cde 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/behaviors/IsolatedLeaderTest.java +++ b/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/behaviors/IsolatedLeaderTest.java @@ -7,135 +7,155 @@ */ 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 leaderActor = actorFactory.createTestActor( + Props.create(MessageCollectorActor.class), actorFactory.generateActorId("leader")); -public class IsolatedLeaderTest extends AbstractRaftActorBehaviorTest { + private final TestActorRef 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 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 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 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 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 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 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(); } } diff --git a/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/behaviors/LeaderTest.java b/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/behaviors/LeaderTest.java index 119b958799..c57fce1cd5 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/behaviors/LeaderTest.java +++ b/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/behaviors/LeaderTest.java @@ -10,15 +10,14 @@ import akka.actor.Terminated; 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; @@ -35,810 +34,653 @@ import org.opendaylight.controller.cluster.raft.base.messages.IsolatedLeaderChec 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 leaderActor = actorFactory.createTestActor( + Props.create(ForwardMessageToBehaviorActor.class), actorFactory.generateActorId("leader")); + + private final TestActorRef 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 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 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 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(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 peerAddresses = new HashMap<>(); - peerAddresses.put(followerActor.path().toString(), - followerActor.path().toString()); - - MockRaftActorContext actorContext = - (MockRaftActorContext) createActorContext(leaderActor); - actorContext.setPeerAddresses(peerAddresses); - - Map 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 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 peerAddresses = new HashMap<>(); - peerAddresses.put(followerActor.path().toString(), - followerActor.path().toString()); + MockRaftActorContext actorContext = createActorContextWithFollower(); - MockRaftActorContext actorContext = - (MockRaftActorContext) createActorContext(getRef()); - actorContext.setPeerAddresses(peerAddresses); + Map leadersSnapshot = new HashMap<>(); + leadersSnapshot.put("1", "A"); + leadersSnapshot.put("2", "B"); + leadersSnapshot.put("3", "C"); - Map 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.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 peerAddresses = new HashMap<>(); - peerAddresses.put(followerActor.path().toString(), followerActor.path().toString()); + MockRaftActorContext actorContext = createActorContextWithFollower(); - MockRaftActorContext actorContext = (MockRaftActorContext) createActorContext(leaderActor); - actorContext.setPeerAddresses(peerAddresses); + Map leadersSnapshot = new HashMap<>(); + leadersSnapshot.put("1", "A"); + leadersSnapshot.put("2", "B"); + leadersSnapshot.put("3", "C"); - Map 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.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.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 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 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 peerAddresses = new HashMap<>(); - peerAddresses.put(followerActor.path().toString(), - followerActor.path().toString()); + Map 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 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(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 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 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 leadersSnapshot = new HashMap<>(); + leadersSnapshot.put("1", "A"); + leadersSnapshot.put("2", "B"); + leadersSnapshot.put("3", "C"); - TestActorRef followerActor = - TestActorRef.create(getSystem(), Props.create(MessageCollectorActor.class), "follower-reply"); + // set the snapshot variables in replicatedlog - Map 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 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 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 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 followerActor = - TestActorRef.create(getSystem(), Props.create(MessageCollectorActor.class), "follower"); - - Map 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 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 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 followerActor = - TestActorRef.create(getSystem(), Props.create(MessageCollectorActor.class), "follower-chunk"); - - Map 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 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 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 @@ -847,7 +689,7 @@ public class LeaderTest extends AbstractRaftActorBehaviorTest { } }); - MockLeader leader = new MockLeader(actorContext); + leader = new Leader(actorContext); Map leadersSnapshot = new HashMap<>(); leadersSnapshot.put("1", "A"); @@ -857,7 +699,9 @@ public class LeaderTest extends AbstractRaftActorBehaviorTest { 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; @@ -869,386 +713,294 @@ public class LeaderTest extends AbstractRaftActorBehaviorTest { 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.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 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 leaderActor = TestActorRef.create(getSystem(), - Props.create(ForwardMessageToBehaviorActor.class)); + logStart("testLeaderCreatedWithCommitIndexLessThanLastIndex"); - MockRaftActorContext leaderActorContext = - new MockRaftActorContext("leader", getSystem(), leaderActor); + MockRaftActorContext leaderActorContext = createActorContextWithFollower(); - TestActorRef 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 peerAddresses = new HashMap<>(); + peerAddresses.put(FOLLOWER_ID, followerActor.path().toString()); - Map 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 leaderActor = TestActorRef.create(getSystem(), - Props.create(ForwardMessageToBehaviorActor.class)); + logStart("testLeaderCreatedWithCommitIndexLessThanFollowersCommitIndex"); - MockRaftActorContext leaderActorContext = - new MockRaftActorContext("leader", getSystem(), leaderActor); + MockRaftActorContext leaderActorContext = createActorContext(); - TestActorRef 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 peerAddresses = new HashMap<>(); + peerAddresses.put(FOLLOWER_ID, followerActor.path().toString()); - Map 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 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 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 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 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 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 peerAddresses = new HashMap<>(); peerAddresses.put("follower-1", followerActor1.path().toString()); @@ -1256,8 +1008,7 @@ public class LeaderTest extends AbstractRaftActorBehaviorTest { leaderActorContext.setPeerAddresses(peerAddresses); - Leader leader = new Leader(leaderActorContext); - leader.stopIsolatedLeaderCheckSchedule(); + leader = new Leader(leaderActorContext); leader.markFollowerActive("follower-1"); leader.markFollowerActive("follower-2"); @@ -1289,118 +1040,150 @@ public class LeaderTest extends AbstractRaftActorBehaviorTest { 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 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 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 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 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 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 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 = 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 { diff --git a/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/utils/ForwardMessageToBehaviorActor.java b/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/utils/ForwardMessageToBehaviorActor.java new file mode 100644 index 0000000000..9bcfcd9091 --- /dev/null +++ b/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/utils/ForwardMessageToBehaviorActor.java @@ -0,0 +1,34 @@ +/* + * 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; + } +} + diff --git a/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/utils/MessageCollectorActor.java b/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/utils/MessageCollectorActor.java index 79c90cf051..62f163fb7d 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/utils/MessageCollectorActor.java +++ b/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/utils/MessageCollectorActor.java @@ -9,6 +9,7 @@ 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; @@ -18,6 +19,7 @@ import java.util.ArrayList; 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; @@ -63,29 +65,46 @@ public class MessageCollectorActor extends UntypedActor { * @return */ public static T getFirstMatching(ActorRef actor, Class clazz) throws Exception { - for(int i = 0; i < 50; i++) { - List allMessages = getAllMessages(actor); + List 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 expectFirstMatching(ActorRef actor, Class clazz) { + return expectFirstMatching(actor, clazz, 5000); + } + + public static T expectFirstMatching(ActorRef actor, Class 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 getAllMatching(ActorRef actor, Class clazz) throws Exception { + public static List getAllMatching(ActorRef actor, Class clazz) throws Exception { List allMessages = getAllMessages(actor); - List output = Lists.newArrayList(); + List output = Lists.newArrayList(); for(Object message : allMessages){ if(message.getClass().equals(clazz)){ - output.add(message); + output.add((T) message); } } @@ -105,4 +124,8 @@ public class MessageCollectorActor extends UntypedActor { throw new TimeoutException("Actor not ready in time."); } + + public static Props props() { + return Props.create(MessageCollectorActor.class); + } } diff --git a/opendaylight/md-sal/sal-akka-raft/src/test/resources/simplelogger.properties b/opendaylight/md-sal/sal-akka-raft/src/test/resources/simplelogger.properties index 4e798073f6..853fc755d2 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/test/resources/simplelogger.properties +++ b/opendaylight/md-sal/sal-akka-raft/src/test/resources/simplelogger.properties @@ -3,4 +3,4 @@ org.slf4j.simpleLogger.dateTimeFormat=hh:mm:ss,S a 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 diff --git a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/DataObjectModification.java b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/DataObjectModification.java new file mode 100644 index 0000000000..2eee0e8099 --- /dev/null +++ b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/DataObjectModification.java @@ -0,0 +1,80 @@ +/* + * 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 extends Identifiable { + + 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 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> getModifiedChildren(); + + +} diff --git a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/DataTreeChangeListener.java b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/DataTreeChangeListener.java new file mode 100644 index 0000000000..6b1df719ac --- /dev/null +++ b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/DataTreeChangeListener.java @@ -0,0 +1,43 @@ +/* + * 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. + * + *

+ * 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. + * + *

+ * 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 changes); +} diff --git a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/DataTreeChangeService.java b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/DataTreeChangeService.java new file mode 100644 index 0000000000..ae4e36f14a --- /dev/null +++ b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/DataTreeChangeService.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.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. + *

+ * You are able to register for notifications for any node or subtree + * which can be represented using {@link DataTreeIdentifier}. + *

+ * + * 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. + *

+ * 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. + * + *

+ * This method returns a {@link ListenerRegistration} object. To + * "unregister" your listener for changes call the {@link ListenerRegistration#close()} + * method on the returned object. + *

+ * 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 ListenerRegistration registerDataTreeChangeListener(@Nonnull DataTreeIdentifier treeId, @Nonnull L listener); +} \ No newline at end of file diff --git a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/DataTreeIdentifier.java b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/DataTreeIdentifier.java new file mode 100644 index 0000000000..428957e988 --- /dev/null +++ b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/DataTreeIdentifier.java @@ -0,0 +1,78 @@ +/* + * 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, 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 diff --git a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/DataTreeModification.java b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/DataTreeModification.java new file mode 100644 index 0000000000..aac51a6a4c --- /dev/null +++ b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/DataTreeModification.java @@ -0,0 +1,37 @@ +/* + * 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 getRootNode(); + +} diff --git a/opendaylight/md-sal/sal-binding-dom-it/pom.xml b/opendaylight/md-sal/sal-binding-dom-it/pom.xml index 01cd1f88ba..d6a5e498b6 100644 --- a/opendaylight/md-sal/sal-binding-dom-it/pom.xml +++ b/opendaylight/md-sal/sal-binding-dom-it/pom.xml @@ -29,11 +29,6 @@ test-jar test - - org.opendaylight.controller.model - model-flow-service - test - org.ops4j.pax.exam pax-exam-container-native @@ -49,10 +44,14 @@ yang-parser-impl test - - org.opendaylight.controller - sal-test-model - + + org.opendaylight.controller + sal-test-model + + + org.opendaylight.yangtools.model + opendaylight-l2-types + diff --git a/opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/md/sal/binding/data/ConcurrentImplicitCreateTest.java b/opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/md/sal/binding/data/ConcurrentImplicitCreateTest.java index aefc53b124..3d25018e24 100644 --- a/opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/md/sal/binding/data/ConcurrentImplicitCreateTest.java +++ b/opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/md/sal/binding/data/ConcurrentImplicitCreateTest.java @@ -17,26 +17,25 @@ 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.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_PATH = InstanceIdentifier.builder(Nodes.class).build(); - private static InstanceIdentifier NODE_FOO_PATH = NODES_PATH.child(Node.class, NODE_FOO_KEY); - private static InstanceIdentifier 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_PATH = InstanceIdentifier.builder(Top.class).build(); + private static InstanceIdentifier FOO_PATH = TOP_PATH.child(TopLevelList.class, FOO_KEY); + private static InstanceIdentifier BAR_PATH = TOP_PATH.child(TopLevelList.class, BAR_KEY); @Test public void testConcurrentCreate() throws InterruptedException, ExecutionException { @@ -44,8 +43,8 @@ public class ConcurrentImplicitCreateTest extends AbstractDataServiceTest { 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> fooFuture = fooTx.commit(); Future> barFuture = barTx.commit(); diff --git a/opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/md/sal/binding/data/WildcardedDataChangeListenerTest.java b/opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/md/sal/binding/data/WildcardedDataChangeListenerTest.java index 2b5171369b..0a611a75d0 100644 --- a/opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/md/sal/binding/data/WildcardedDataChangeListenerTest.java +++ b/opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/md/sal/binding/data/WildcardedDataChangeListenerTest.java @@ -22,87 +22,75 @@ import org.opendaylight.controller.sal.binding.api.data.DataChangeListener; 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 DEEP_WILDCARDED_PATH = InstanceIdentifier.builder(Nodes.class) - .child(Node.class) // - .augmentation(FlowCapableNode.class) // - .child(Table.class) // - .child(Flow.class) // + protected static final InstanceIdentifier 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 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 NODE_0_TCU_PATH = InstanceIdentifier + .builder(Top.class) + .child(TopLevelList.class, TOP_LEVEL_LIST_0_KEY) // + .augmentation(TreeComplexUsesAugment.class) // .build(); - private static final InstanceIdentifier
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 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 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 NODE_0_LVU_PATH = NODE_0_TCU_PATH.child(ListViaUses.class, LIST_VIA_USES_KEY); - private static final InstanceIdentifier NODE_1_FLOW_PATH = NODE_1_TABLE_PATH.child(Flow.class, FLOW_KEY); + private static final InstanceIdentifier NODE_1_LVU_PATH = NODE_1_TCU_PATH.child(ListViaUses.class, LIST_VIA_USES_KEY); - private static final InstanceIdentifier NODE_0_TABLE_FEATURES_PATH = - NODE_0_TABLE_PATH.child(TableFeatures.class, TABLE_FEATURES_KEY); + private static final InstanceIdentifier 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, DataObject>> eventFuture = SettableFuture.create(); dataBroker.registerDataChangeListener(DEEP_WILDCARDED_PATH, new DataChangeListener() { - @Override public void onDataChanged(final DataChangeEvent, DataObject> dataChangeEvent) { eventFuture.set(dataChangeEvent); @@ -110,9 +98,9 @@ public class WildcardedDataChangeListenerTest extends AbstractDataServiceTest { }); 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, DataObject> event = eventFuture.get(1000, TimeUnit.MILLISECONDS); @@ -127,29 +115,26 @@ public class WildcardedDataChangeListenerTest extends AbstractDataServiceTest { final SettableFuture, DataObject>> eventFuture = SettableFuture.create(); dataBroker.registerDataChangeListener(DEEP_WILDCARDED_PATH, new DataChangeListener() { - @Override public void onDataChanged(final DataChangeEvent, 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)); } @@ -161,7 +146,7 @@ public class WildcardedDataChangeListenerTest extends AbstractDataServiceTest { // 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 @@ -176,23 +161,23 @@ public class WildcardedDataChangeListenerTest extends AbstractDataServiceTest { 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, 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, 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)); } } diff --git a/opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/sal/binding/test/bugfix/DOMCodecBug02Test.java b/opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/sal/binding/test/bugfix/DOMCodecBug02Test.java index bddbc4e954..d85cb7a0a2 100644 --- a/opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/sal/binding/test/bugfix/DOMCodecBug02Test.java +++ b/opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/sal/binding/test/bugfix/DOMCodecBug02Test.java @@ -21,9 +21,9 @@ 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.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; @@ -33,7 +33,7 @@ import com.google.common.util.concurrent.MoreExecutors; @SuppressWarnings("deprecation") public class DOMCodecBug02Test extends AbstractDataServiceTest { - private static final InstanceIdentifier NODES_INSTANCE_ID_BA = InstanceIdentifier.builder(Nodes.class) // + private static final InstanceIdentifier TOP_INSTANCE_ID_BA = InstanceIdentifier.builder(Top.class) // .toInstance(); /** @@ -66,10 +66,10 @@ public class DOMCodecBug02Test extends AbstractDataServiceTest { .submit(new Callable>>() { @Override public Future> call() throws Exception { - NodesBuilder nodesBuilder = new NodesBuilder(); - nodesBuilder.setNode(Collections. emptyList()); + TopBuilder topBuilder = new TopBuilder(); + topBuilder.setTopLevelList(Collections. emptyList()); DataModificationTransaction transaction = baDataService.beginTransaction(); - transaction.putOperationalData(NODES_INSTANCE_ID_BA, nodesBuilder.build()); + transaction.putOperationalData(TOP_INSTANCE_ID_BA, topBuilder.build()); return transaction.commit(); } }); @@ -77,13 +77,13 @@ public class DOMCodecBug02Test extends AbstractDataServiceTest { RpcResult 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); } diff --git a/opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/sal/binding/test/bugfix/DOMCodecBug03Test.java b/opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/sal/binding/test/bugfix/DOMCodecBug03Test.java index c07125a5dc..ba4a024b7c 100644 --- a/opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/sal/binding/test/bugfix/DOMCodecBug03Test.java +++ b/opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/sal/binding/test/bugfix/DOMCodecBug03Test.java @@ -22,22 +22,20 @@ import org.opendaylight.controller.md.sal.common.api.data.DataChangeEvent; 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; @@ -49,42 +47,42 @@ import com.google.common.util.concurrent.SettableFuture; @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 NODE_KEY_BI = Collections. singletonMap(NODE_ID_QNAME, - NODE_ID); + private static final Map TLL_KEY_BI = Collections. singletonMap(TOP_LEVEL_LIST_NAME_QNAME, + TOP_LEVEL_LIST_NAME); - private static final InstanceIdentifier NODES_INSTANCE_ID_BA = InstanceIdentifier.builder(Nodes.class) // + private static final InstanceIdentifier TOP_INSTANCE_ID_BA = InstanceIdentifier.builder(Top.class) // .toInstance(); - private static final InstanceIdentifier NODE_INSTANCE_ID_BA = NODES_INSTANCE_ID_BA.child(Node.class, NODE_KEY); + private static final InstanceIdentifier TLL_INSTANCE_ID_BA = TOP_INSTANCE_ID_BA.child(TopLevelList.class, TLL_KEY); - private static final InstanceIdentifier 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_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, DataObject>> receivedChangeEvent = SettableFuture.create(); @@ -100,24 +98,22 @@ public class DOMCodecBug03Test extends AbstractDataServiceTest implements DataCh 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 result = transaction.commit().get(); assertEquals(TransactionStatus.COMMITED, result.getResult()); @@ -125,13 +121,13 @@ public class DOMCodecBug03Test extends AbstractDataServiceTest implements DataCh DataChangeEvent, 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(); } @@ -139,69 +135,66 @@ public class DOMCodecBug03Test extends AbstractDataServiceTest implements DataCh 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 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 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 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 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 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 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 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()); } @@ -211,8 +204,8 @@ public class DOMCodecBug03Test extends AbstractDataServiceTest implements DataCh 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 diff --git a/opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/sal/binding/test/bugfix/DeleteNestedAugmentationListenParentTest.java b/opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/sal/binding/test/bugfix/DeleteNestedAugmentationListenParentTest.java index 735138a530..40d4591001 100644 --- a/opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/sal/binding/test/bugfix/DeleteNestedAugmentationListenParentTest.java +++ b/opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/sal/binding/test/bugfix/DeleteNestedAugmentationListenParentTest.java @@ -9,21 +9,17 @@ import org.opendaylight.controller.md.sal.common.api.data.DataChangeEvent; 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; @@ -32,20 +28,20 @@ import com.google.common.util.concurrent.SettableFuture; @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 NODE_AUGMENT_PATH = InstanceIdentifier.builder(Nodes.class) - .child(Node.class,NODE_KEY) - .augmentation(FlowCapableNode.class) + private static final InstanceIdentifier TLL_COMPLEX_AUGMENT_PATH = InstanceIdentifier.builder(Top.class) + .child(TopLevelList.class,FOO_KEY) + .augmentation(TllComplexAugment.class) .build(); - private static final InstanceIdentifier FLOW_PATH = NODE_AUGMENT_PATH.builder() - .child(Table.class,TABLE_KEY) - .child(Flow.class,FLOW_KEY) + private static final InstanceIdentifier LIST11_PATH = TLL_COMPLEX_AUGMENT_PATH.builder() + .child(List1.class,LIST1_KEY) + .child(List11.class,LIST11_KEY) .build(); @@ -53,12 +49,12 @@ public class DeleteNestedAugmentationListenParentTest extends AbstractDataServic 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, DataObject>> event = SettableFuture.create(); - baDataService.registerDataChangeListener(FLOW_PATH, new DataChangeListener() { + baDataService.registerDataChangeListener(LIST11_PATH, new DataChangeListener() { @Override public void onDataChanged(final DataChangeEvent, DataObject> change) { @@ -67,23 +63,19 @@ public class DeleteNestedAugmentationListenParentTest extends AbstractDataServic }); DataModificationTransaction deleteTx = baDataService.beginTransaction(); - deleteTx.removeOperationalData(FLOW_PATH.augmentation(FlowStatisticsData.class)); + deleteTx.removeOperationalData(LIST11_PATH.augmentation(List11SimpleAugment.class)); deleteTx.commit().get(); DataChangeEvent, 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(); } diff --git a/opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/sal/binding/test/bugfix/FlagsSerializationTest.java b/opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/sal/binding/test/bugfix/FlagsSerializationTest.java index 7143352c1f..9ad46bb99f 100644 --- a/opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/sal/binding/test/bugfix/FlagsSerializationTest.java +++ b/opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/sal/binding/test/bugfix/FlagsSerializationTest.java @@ -11,39 +11,20 @@ import static org.junit.Assert.assertEquals; 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; @@ -54,41 +35,36 @@ import com.google.common.collect.ImmutableSet; @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_INSTANCE_ID_BA = InstanceIdentifier.builder(Nodes.class) // - .child(Node.class, NODE_KEY).toInstance(); - - private static final InstanceIdentifier 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 TLL_INSTANCE_ID_BA = InstanceIdentifier.builder(Top.class) // + .child(TopLevelList.class, TLL_KEY).toInstance(); + + private static final InstanceIdentifier 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 domCheckOverlapFlags = ImmutableSet.of("CHECK_OVERLAP"); + BitFlags checkOverlapFlags = new BitFlags(true,false,false,false,false); + ImmutableSet domCheckOverlapFlags = ImmutableSet.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 domAllFalseFlags = ImmutableSet.of(); testFlags(allFalseFlags,domAllFalseFlags); - FlowModFlags allTrueFlags = new FlowModFlags(true,true,true,true,true); - ImmutableSet domAllTrueFlags = ImmutableSet.of("CHECK_OVERLAP","NO_BYT_COUNTS", "NO_PKT_COUNTS", "RESET_COUNTS", "SEND_FLOW_REM"); + BitFlags allTrueFlags = new BitFlags(true,true,true,true,true); + ImmutableSet domAllTrueFlags = ImmutableSet.of("FLAG_ONE","FLAG_TWO","FLAG_THREE","FLAG_FOUR","FLAG_FIVE"); testFlags(allTrueFlags,domAllTrueFlags); testFlags(null,null); @@ -97,14 +73,14 @@ public class FlagsSerializationTest extends AbstractDataServiceTest { } - private void testFlags(final FlowModFlags flagsToTest, final ImmutableSet domFlags) throws Exception { - Flow flow = createFlow(flagsToTest); - assertNotNull(flow); + private void testFlags(final BitFlags flagsToTest, final ImmutableSet 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); @@ -112,55 +88,30 @@ public class FlagsSerializationTest extends AbstractDataServiceTest { } 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 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 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 instructionList = Collections.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 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); } } diff --git a/opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/sal/binding/test/bugfix/PutAugmentationTest.java b/opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/sal/binding/test/bugfix/PutAugmentationTest.java index 767ccaade3..9bf6c4d291 100644 --- a/opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/sal/binding/test/bugfix/PutAugmentationTest.java +++ b/opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/sal/binding/test/bugfix/PutAugmentationTest.java @@ -23,19 +23,18 @@ import org.opendaylight.controller.md.sal.common.api.data.DataChangeEvent; 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; @@ -47,35 +46,35 @@ import com.google.common.util.concurrent.SettableFuture; @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 NODE_KEY_BI = Collections. singletonMap(NODE_ID_QNAME, - NODE_ID); + private static final Map TLL_KEY_BI = Collections. singletonMap(TLL_NAME_QNAME, + TLL_NAME); - private static final InstanceIdentifier NODES_INSTANCE_ID_BA = InstanceIdentifier.builder(Nodes.class) // + private static final InstanceIdentifier TOP_INSTANCE_ID_BA = InstanceIdentifier.builder(Top.class) // .toInstance(); - private static final InstanceIdentifier NODE_INSTANCE_ID_BA = // - NODES_INSTANCE_ID_BA.builder() // - .child(Node.class, NODE_KEY).toInstance(); + private static final InstanceIdentifier TLL_INSTANCE_ID_BA = // + TOP_INSTANCE_ID_BA.builder() // + .child(TopLevelList.class, TLL_KEY).toInstance(); - private static final InstanceIdentifier ALL_FLOW_CAPABLE_NODES = // - NODES_INSTANCE_ID_BA.builder() // - .child(Node.class) // - .augmentation(FlowCapableNode.class) // + private static final InstanceIdentifier 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 FLOW_AUGMENTATION_PATH = - NODE_INSTANCE_ID_BA.builder() // - .augmentation(FlowCapableNode.class) // + private static final InstanceIdentifier TCA_AUGMENTATION_PATH = + TLL_INSTANCE_ID_BA.builder() // + .augmentation(TllComplexAugment.class) // .build(); private SettableFuture, DataObject>> lastReceivedChangeEvent; @@ -89,32 +88,28 @@ public class PutAugmentationTest extends AbstractDataServiceTest implements Data @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 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 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 augmentIdentifier = TLL_INSTANCE_ID_BA + .augmentation(TreeComplexUsesAugment.class); DataModificationTransaction augmentedTransaction = baDataService.beginTransaction(); - augmentedTransaction.putOperationalData(augmentIdentifier, fnu); + augmentedTransaction.putOperationalData(augmentIdentifier, tca); lastReceivedChangeEvent = SettableFuture.create(); @@ -123,102 +118,98 @@ public class PutAugmentationTest extends AbstractDataServiceTest implements Data DataChangeEvent, 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 result = baseTransaction.commit().get(); DataChangeEvent, 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 ncPath = NODE_INSTANCE_ID_BA - .child(NodeConnector.class, ncKey); - InstanceIdentifier ncAugmentPath = ncPath - .augmentation(FlowCapableNodeConnector.class); + NestedListKey ncKey = new NestedListKey("test:0:0"); + InstanceIdentifier ncPath = TLL_INSTANCE_ID_BA + .child(NestedList.class, ncKey); + InstanceIdentifier 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 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 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 diff --git a/opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/sal/binding/test/bugfix/WriteParentListenAugmentTest.java b/opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/sal/binding/test/bugfix/WriteParentListenAugmentTest.java index b09ba39a65..0f9051d41c 100644 --- a/opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/sal/binding/test/bugfix/WriteParentListenAugmentTest.java +++ b/opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/sal/binding/test/bugfix/WriteParentListenAugmentTest.java @@ -18,13 +18,13 @@ import org.opendaylight.controller.md.sal.common.api.data.DataChangeEvent; 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; @@ -34,17 +34,17 @@ import com.google.common.util.concurrent.SettableFuture; @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_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 TLL_INSTANCE_ID_BA = InstanceIdentifier.builder(Top.class) // + .child(TopLevelList.class, TLL_KEY).toInstance(); - private static final InstanceIdentifier AUGMENT_WILDCARDED_PATH = InstanceIdentifier - .builder(Nodes.class).child(Node.class).augmentation(FlowCapableNode.class).toInstance(); + private static final InstanceIdentifier AUGMENT_WILDCARDED_PATH = InstanceIdentifier + .builder(Top.class).child(TopLevelList.class).augmentation(TreeComplexUsesAugment.class).toInstance(); - private static final InstanceIdentifier AUGMENT_NODE_PATH = InstanceIdentifier - .builder(Nodes.class).child(Node.class, NODE_KEY).augmentation(FlowCapableNode.class).toInstance(); + private static final InstanceIdentifier AUGMENT_TLL_PATH = InstanceIdentifier + .builder(Top.class).child(TopLevelList.class, TLL_KEY).augmentation(TreeComplexUsesAugment.class).toInstance(); @Test public void writeNodeListenAugment() throws Exception { @@ -62,29 +62,29 @@ public class WriteParentListenAugmentTest extends AbstractDataServiceTest { 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, 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 diff --git a/opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/sal/binding/test/bugfix/WriteParentReadChildTest.java b/opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/sal/binding/test/bugfix/WriteParentReadChildTest.java index ad02d9a6f6..7941f4d4ae 100644 --- a/opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/sal/binding/test/bugfix/WriteParentReadChildTest.java +++ b/opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/sal/binding/test/bugfix/WriteParentReadChildTest.java @@ -16,24 +16,16 @@ 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.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; @@ -43,23 +35,23 @@ import com.google.common.collect.ImmutableList; @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_INSTANCE_ID_BA = InstanceIdentifier.builder(Nodes.class) // - .child(Node.class, NODE_KEY).toInstance(); + private static final InstanceIdentifier TLL_INSTANCE_ID_BA = InstanceIdentifier.builder(Top.class) // + .child(TopLevelList.class, TLL_KEY).toInstance(); - private static final InstanceIdentifier
TABLE_INSTANCE_ID_BA = // - NODE_INSTANCE_ID_BA.builder() // - .augmentation(FlowCapableNode.class).child(Table.class, TABLE_KEY).build(); + private static final InstanceIdentifier LIST1_INSTANCE_ID_BA = // + TLL_INSTANCE_ID_BA.builder() // + .augmentation(TllComplexAugment.class).child(List1.class, LIST1_KEY).build(); - private static final InstanceIdentifier FLOW_INSTANCE_ID_BA = // - TABLE_INSTANCE_ID_BA.child(Flow.class, FLOW_KEY); + private static final InstanceIdentifier LIST11_INSTANCE_ID_BA = // + LIST1_INSTANCE_ID_BA.child(List11.class, LIST11_KEY); /** * * The scenario tests writing parent node, which also contains child items @@ -70,43 +62,33 @@ public class WriteParentReadChildTest extends AbstractDataServiceTest { * @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.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 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); } } diff --git a/opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/sal/binding/test/connect/dom/BrokerIntegrationTest.java b/opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/sal/binding/test/connect/dom/BrokerIntegrationTest.java index 481a7ddfa2..48027114d7 100644 --- a/opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/sal/binding/test/connect/dom/BrokerIntegrationTest.java +++ b/opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/sal/binding/test/connect/dom/BrokerIntegrationTest.java @@ -17,30 +17,37 @@ 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.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_PATH = InstanceIdentifier.builder(Top.class).build(); + private static final InstanceIdentifier FOO_PATH = TOP_PATH.child(TopLevelList.class, TLL_FOO_KEY); + private static final InstanceIdentifier BAR_PATH = TOP_PATH.child(TopLevelList.class, TLL_BAR_KEY); + private static final InstanceIdentifier 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> commitResult = transaction.commit(); assertNotNull(commitResult); @@ -50,29 +57,26 @@ public class BrokerIntegrationTest extends AbstractDataServiceTest { 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 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 @@ -85,7 +89,7 @@ public class BrokerIntegrationTest extends AbstractDataServiceTest { * We remove node 1 * */ - removalTransaction.removeConfigurationData(node1.getValue()); + removalTransaction.removeConfigurationData(BAR_PATH); /** * We commit transaction @@ -99,21 +103,13 @@ public class BrokerIntegrationTest extends AbstractDataServiceTest { 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 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(); } } diff --git a/opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/sal/binding/test/connect/dom/ChangeOriginatedInDomBrokerTest.java b/opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/sal/binding/test/connect/dom/ChangeOriginatedInDomBrokerTest.java index a3b0819501..25b159b43b 100644 --- a/opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/sal/binding/test/connect/dom/ChangeOriginatedInDomBrokerTest.java +++ b/opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/sal/binding/test/connect/dom/ChangeOriginatedInDomBrokerTest.java @@ -11,9 +11,7 @@ import static org.junit.Assert.assertEquals; 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; @@ -23,34 +21,17 @@ import org.opendaylight.controller.md.sal.common.api.data.DataChangeEvent; 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; @@ -59,83 +40,79 @@ import org.opendaylight.yangtools.yang.data.api.CompositeNode; 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, 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 NODE_KEY_BI = Collections. singletonMap(NODE_ID_QNAME, - NODE_ID); + protected final SettableFuture, DataObject>> modificationCapture = SettableFuture.create(); - private static final InstanceIdentifier NODE_INSTANCE_ID_BA = InstanceIdentifier.builder(Nodes.class) // - .child(Node.class, NODE_KEY).toInstance(); + private static final Map TLL_KEY_BI = Collections. singletonMap(TLL_NAME_QNAME, + TLL_NAME); - private static final Map FLOW_KEY_BI = // - ImmutableMap. of(FLOW_ID_QNAME, FLOW_ID.getValue()); + private static final InstanceIdentifier NODE_INSTANCE_ID_BA = InstanceIdentifier.builder(Top.class) // + .child(TopLevelList.class, TLL_KEY).toInstance(); - private static final Map TABLE_KEY_BI = // - ImmutableMap. of(TABLE_ID_QNAME, TABLE_ID);; + private static final Map LIST1_KEY_BI = // + ImmutableMap. 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 FLOWS_PATH_BA = // + private static final InstanceIdentifier 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_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 biResult = biTransaction.commit().get(); assertEquals(TransactionStatus.COMMITED, biResult.getResult()); DataChangeEvent, 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, DataObject> change) { @@ -145,61 +122,18 @@ public class ChangeOriginatedInDomBrokerTest extends AbstractDataServiceTest { }); } - 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 actionList = new ArrayList(); - 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 instructions = new ArrayList(); - 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; } } diff --git a/opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/sal/binding/test/connect/dom/CrossBrokerMountPointTest.java b/opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/sal/binding/test/connect/dom/CrossBrokerMountPointTest.java index d87470a5e9..f199d71aa5 100644 --- a/opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/sal/binding/test/connect/dom/CrossBrokerMountPointTest.java +++ b/opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/sal/binding/test/connect/dom/CrossBrokerMountPointTest.java @@ -7,10 +7,9 @@ */ 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; @@ -23,16 +22,16 @@ import org.opendaylight.controller.sal.binding.test.util.BindingBrokerTestFactor 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; @@ -43,39 +42,45 @@ import com.google.common.util.concurrent.MoreExecutors; @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 TLL_KEY_BI = Collections. singletonMap(TLL_NAME_QNAME, + TLL_NAME); - private static final Map NODE_KEY_BI = Collections. singletonMap(NODE_ID_QNAME, - NODE_ID); + private static final InstanceIdentifier TLL_INSTANCE_ID_BA = InstanceIdentifier.builder(Top.class) // + .child(TopLevelList.class, TLL_KEY).build(); - private static final InstanceIdentifier 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 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 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; @@ -102,14 +107,14 @@ public class CrossBrokerMountPointTest { @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 simpleReader = new DataReader() { @@ -125,9 +130,9 @@ public class CrossBrokerMountPointTest { 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; } @@ -135,10 +140,10 @@ public class CrossBrokerMountPointTest { } }; - 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()); } } diff --git a/opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/sal/binding/test/connect/dom/CrossBrokerRpcTest.java b/opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/sal/binding/test/connect/dom/CrossBrokerRpcTest.java index ba75d578fb..83c2f88376 100644 --- a/opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/sal/binding/test/connect/dom/CrossBrokerRpcTest.java +++ b/opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/sal/binding/test/connect/dom/CrossBrokerRpcTest.java @@ -12,11 +12,10 @@ import static org.junit.Assert.assertNotNull; 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; @@ -25,19 +24,15 @@ import org.opendaylight.controller.sal.binding.test.util.BindingBrokerTestFactor 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; @@ -45,38 +40,31 @@ import org.opendaylight.yangtools.yang.common.RpcResult; 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 BA_NODE_A_ID = createBANodeIdentifier(NODE_A); - public static final InstanceIdentifier BA_NODE_B_ID = createBANodeIdentifier(NODE_B); - public static final InstanceIdentifier BA_NODE_C_ID = createBANodeIdentifier(NODE_C); - public static final InstanceIdentifier BA_NODE_D_ID = createBANodeIdentifier(NODE_D); + public static final InstanceIdentifier NODES_PATH = InstanceIdentifier.builder(Top.class).build(); + public static final InstanceIdentifier BA_NODE_A_ID = NODES_PATH.child(TopLevelList.class, NODE_A); + public static final InstanceIdentifier BA_NODE_B_ID = NODES_PATH.child(TopLevelList.class, NODE_B); + public static final InstanceIdentifier 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 @@ -87,48 +75,49 @@ public class CrossBrokerRpcTest { 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 domResult = biRpcInvoker.invokeRpc(ADD_FLOW_QNAME, addFlowDom).get(); + CompositeNode knockKnockDom = toDomRpc(KNOCK_KNOCK_QNAME, knockKnockA); + assertNotNull(knockKnockDom); + RpcResult 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 getSupportedRpcs() { - return ImmutableSet.of(ADD_FLOW_QNAME); + return ImmutableSet.of(KNOCK_KNOCK_QNAME); } @Override @@ -137,12 +126,14 @@ public class CrossBrokerRpcTest { return Futures.immediateFuture(RpcResultBuilder.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> baResult = baFlowInvoker.addFlow(addFlow(BA_NODE_C_ID).setPriority(500).build()); + OpendaylightOfMigrationTestModelService baKnockInvoker = + providerRegistry.getRpcService(OpendaylightOfMigrationTestModelService.class); + Future> 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) { @@ -154,30 +145,26 @@ public class CrossBrokerRpcTest { testContext.close(); } - private static InstanceIdentifier 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> addFlowResult(boolean success, long xid) { - AddFlowOutput output = new AddFlowOutputBuilder() // - .setTransactionId(new TransactionId(BigInteger.valueOf(xid))).build(); - RpcResult result = RpcResultBuilder.status(success).withResult(output).build(); + private Future> knockResult(boolean success, String answer) { + KnockKnockOutput output = new KnockKnockOutputBuilder() // + .setAnswer(answer).build(); + RpcResult result = RpcResultBuilder.status(success).withResult(output).build(); return Futures.immediateFuture(result); } - private static AddFlowInputBuilder addFlow(InstanceIdentifier nodeId) { - AddFlowInputBuilder builder = new AddFlowInputBuilder(); - builder.setNode(new NodeRef(nodeId)); + private static KnockKnockInputBuilder knockKnock(InstanceIdentifier 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.> singletonList(toDomRpcInput(addFlowA))); + Collections.>singletonList(toDomRpcInput(knockInput))); } } diff --git a/opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/sal/binding/test/connect/dom/DOMRpcServiceTestBugfix560.java b/opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/sal/binding/test/connect/dom/DOMRpcServiceTestBugfix560.java index 7d616ca62c..7595ec0105 100644 --- a/opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/sal/binding/test/connect/dom/DOMRpcServiceTestBugfix560.java +++ b/opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/sal/binding/test/connect/dom/DOMRpcServiceTestBugfix560.java @@ -28,12 +28,11 @@ import org.opendaylight.controller.sal.binding.test.util.BindingTestContext; 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; @@ -64,11 +63,11 @@ public class DOMRpcServiceTestBugfix560 { 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 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 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; @@ -104,18 +103,18 @@ public class DOMRpcServiceTestBugfix560 { 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 createBANodeIdentifier( - final NodeId mountNode) { - return InstanceIdentifier.builder(Nodes.class) - .child(Node.class, new NodeKey(mountNode)).toInstance(); + private static InstanceIdentifier createBATllIdentifier( + final String mount) { + return InstanceIdentifier.builder(Top.class) + .child(TopLevelList.class, new TopLevelListKey(mount)).toInstance(); } @SuppressWarnings("deprecation") diff --git a/opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/sal/binding/test/connect/dom/MessageCapturingFlowService.java b/opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/sal/binding/test/connect/dom/MessageCapturingFlowService.java index 47e79650fe..0f9389703f 100644 --- a/opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/sal/binding/test/connect/dom/MessageCapturingFlowService.java +++ b/opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/sal/binding/test/connect/dom/MessageCapturingFlowService.java @@ -11,15 +11,11 @@ import static org.junit.Assert.assertNotNull; 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; @@ -27,76 +23,28 @@ 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> addFlowResult; - private Future> removeFlowResult; - private Future> updateFlowResult; - - private final Multimap, AddFlowInput> receivedAddFlows = HashMultimap.create(); - private final Multimap, RemoveFlowInput> receivedRemoveFlows = HashMultimap.create(); - private final Multimap, UpdateFlowInput> receivedUpdateFlows = HashMultimap.create(); - private RoutedRpcRegistration registration; - - @Override - public Future> addFlow(AddFlowInput arg0) { - receivedAddFlows.put(arg0.getNode().getValue(), arg0); - return addFlowResult; - } - - @Override - public Future> removeFlow(RemoveFlowInput arg0) { - receivedRemoveFlows.put(arg0.getNode().getValue(), arg0); - return removeFlowResult; - } - - @Override - public Future> updateFlow(UpdateFlowInput arg0) { - receivedUpdateFlows.put(arg0.getNode().getValue(), arg0); - return updateFlowResult; - } - - public Future> getAddFlowResult() { - return addFlowResult; - } +public class MessageCapturingFlowService implements OpendaylightOfMigrationTestModelService, AutoCloseable { - public MessageCapturingFlowService setAddFlowResult(Future> addFlowResult) { - this.addFlowResult = addFlowResult; - return this; - } + private Future> knockKnockResult; - public Future> getRemoveFlowResult() { - return removeFlowResult; - } - - public MessageCapturingFlowService setRemoveFlowResult(Future> removeFlowResult) { - this.removeFlowResult = removeFlowResult; - return this; - } + private final Multimap, KnockKnockInput> receivedKnocks = HashMultimap.create(); + private RoutedRpcRegistration registration; - public Future> getUpdateFlowResult() { - return updateFlowResult; + public Future> getKnockKnockResult() { + return knockKnockResult; } - public MessageCapturingFlowService setUpdateFlowResult(Future> updateFlowResult) { - this.updateFlowResult = updateFlowResult; + public MessageCapturingFlowService setKnockKnockResult(Future> kkOutput) { + this.knockKnockResult = kkOutput; return this; } - public Multimap, AddFlowInput> getReceivedAddFlows() { - return receivedAddFlows; - } - - public Multimap, RemoveFlowInput> getReceivedRemoveFlows() { - return receivedRemoveFlows; - } - - public Multimap, UpdateFlowInput> getReceivedUpdateFlows() { - return receivedUpdateFlows; + public Multimap, 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; } @@ -125,5 +73,11 @@ public class MessageCapturingFlowService implements SalFlowService, AutoCloseabl return ret; } + @Override + public Future> knockKnock(KnockKnockInput input) { + receivedKnocks.put(input.getKnockerId(), input); + return knockKnockResult; + } + } diff --git a/opendaylight/md-sal/sal-clustering-config/src/main/resources/initial/akka.conf b/opendaylight/md-sal/sal-clustering-config/src/main/resources/initial/akka.conf index 9da6a3b5a4..e72f4b2675 100644 --- a/opendaylight/md-sal/sal-clustering-config/src/main/resources/initial/akka.conf +++ b/opendaylight/md-sal/sal-clustering-config/src/main/resources/initial/akka.conf @@ -13,17 +13,27 @@ odl-cluster-data { 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 diff --git a/opendaylight/md-sal/sal-common-impl/src/main/java/org/opendaylight/controller/md/sal/common/impl/util/compat/DataNormalizer.java b/opendaylight/md-sal/sal-common-impl/src/main/java/org/opendaylight/controller/md/sal/common/impl/util/compat/DataNormalizer.java index e8b239d2c4..b4dcb1167c 100644 --- a/opendaylight/md-sal/sal-common-impl/src/main/java/org/opendaylight/controller/md/sal/common/impl/util/compat/DataNormalizer.java +++ b/opendaylight/md-sal/sal-common-impl/src/main/java/org/opendaylight/controller/md/sal/common/impl/util/compat/DataNormalizer.java @@ -14,10 +14,8 @@ import java.util.ArrayList; 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; @@ -106,24 +104,6 @@ public class DataNormalizer { 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>(normalizedPath, diff --git a/opendaylight/md-sal/sal-common-impl/src/test/java/org/opendaylight/controller/md/sal/common/impl/util/compat/DataNormalizerTest.java b/opendaylight/md-sal/sal-common-impl/src/test/java/org/opendaylight/controller/md/sal/common/impl/util/compat/DataNormalizerTest.java index e841b866ba..ce9379a4ad 100644 --- a/opendaylight/md-sal/sal-common-impl/src/test/java/org/opendaylight/controller/md/sal/common/impl/util/compat/DataNormalizerTest.java +++ b/opendaylight/md-sal/sal-common-impl/src/test/java/org/opendaylight/controller/md/sal/common/impl/util/compat/DataNormalizerTest.java @@ -668,10 +668,6 @@ public class DataNormalizerTest { 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 diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ConcurrentDOMDataBroker.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ConcurrentDOMDataBroker.java index 13334c9272..886c473067 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ConcurrentDOMDataBroker.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ConcurrentDOMDataBroker.java @@ -8,13 +8,13 @@ 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; @@ -70,17 +70,16 @@ public class ConcurrentDOMDataBroker extends AbstractDOMDataBroker { @Override public CheckedFuture submit(DOMDataWriteTransaction transaction, - Iterable cohorts) { + Collection 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); @@ -88,31 +87,31 @@ public class ConcurrentDOMDataBroker extends AbstractDOMDataBroker { private void doCanCommit(final AsyncNotifyingSettableFuture clientSubmitFuture, final DOMDataWriteTransaction transaction, - final Iterable cohorts, final int cohortSize) { + final Collection 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 futureCallback = new FutureCallback() { @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); } }; @@ -125,22 +124,22 @@ public class ConcurrentDOMDataBroker extends AbstractDOMDataBroker { private void doPreCommit(final long startTime, final AsyncNotifyingSettableFuture clientSubmitFuture, final DOMDataWriteTransaction transaction, - final Iterable cohorts, final int cohortSize) { + final Collection 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 futureCallback = new FutureCallback() { @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); } }; @@ -153,10 +152,10 @@ public class ConcurrentDOMDataBroker extends AbstractDOMDataBroker { private void doCommit(final long startTime, final AsyncNotifyingSettableFuture clientSubmitFuture, final DOMDataWriteTransaction transaction, - final Iterable cohorts, final int cohortSize) { + final Collection 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 futureCallback = new FutureCallback() { @Override public void onSuccess(Void notUsed) { @@ -170,7 +169,7 @@ public class ConcurrentDOMDataBroker extends AbstractDOMDataBroker { @Override public void onFailure(Throwable t) { - handleException(clientSubmitFuture, transaction, cohorts, cohortSize, COMMIT, + handleException(clientSubmitFuture, transaction, cohorts, COMMIT, TransactionCommitFailedExceptionMapper.COMMIT_ERROR_MAPPER, t); } }; @@ -183,7 +182,7 @@ public class ConcurrentDOMDataBroker extends AbstractDOMDataBroker { private void handleException(final AsyncNotifyingSettableFuture clientSubmitFuture, final DOMDataWriteTransaction transaction, - final Iterable cohorts, int cohortSize, + final Collection cohorts, final String phase, final TransactionCommitFailedExceptionMapper exMapper, final Throwable t) { @@ -205,7 +204,7 @@ public class ConcurrentDOMDataBroker extends AbstractDOMDataBroker { // Transaction failed - tell all cohorts to abort. @SuppressWarnings("unchecked") - ListenableFuture[] canCommitFutures = new ListenableFuture[cohortSize]; + ListenableFuture[] canCommitFutures = new ListenableFuture[cohorts.size()]; int i = 0; for(DOMStoreThreePhaseCommitCohort cohort: cohorts) { canCommitFutures[i++] = cohort.abort(); diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DataChangeListenerRegistrationProxy.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DataChangeListenerRegistrationProxy.java index 06f3afc57c..681132e660 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DataChangeListenerRegistrationProxy.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DataChangeListenerRegistrationProxy.java @@ -12,6 +12,7 @@ import akka.actor.ActorRef; 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; @@ -25,7 +26,6 @@ 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; -import com.google.common.annotations.VisibleForTesting; import scala.concurrent.Future; /** @@ -93,7 +93,7 @@ public class DataChangeListenerRegistrationProxy implements ListenerRegistration public void init(final YangInstanceIdentifier path, final AsyncDataBroker.DataChangeScope scope) { dataChangeListenerActor = actorContext.getActorSystem().actorOf( - DataChangeListener.props(listener)); + DataChangeListener.props(listener).withDispatcher(actorContext.getNotificationDispatcherPath())); Future findFuture = actorContext.findLocalShardAsync(shardName); findFuture.onComplete(new OnComplete() { @@ -109,7 +109,7 @@ public class DataChangeListenerRegistrationProxy implements ListenerRegistration doRegistration(shard, path, scope); } } - }, actorContext.getActorSystem().dispatcher()); + }, actorContext.getClientDispatcher()); } private void doRegistration(ActorRef shard, final YangInstanceIdentifier path, @@ -131,7 +131,7 @@ public class DataChangeListenerRegistrationProxy implements ListenerRegistration reply.getListenerRegistrationPath())); } } - }, actorContext.getActorSystem().dispatcher()); + }, actorContext.getClientDispatcher()); } @Override diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DistributedDataStore.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DistributedDataStore.java index 107c959112..afbdbe1fe9 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DistributedDataStore.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DistributedDataStore.java @@ -14,6 +14,7 @@ import com.google.common.base.Preconditions; 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; @@ -52,9 +53,12 @@ public class DistributedDataStore implements DOMStore, SchemaContextListener, Au 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); } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/Shard.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/Shard.java index 21d74a6e1a..0672023fcb 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/Shard.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/Shard.java @@ -60,10 +60,13 @@ import org.opendaylight.controller.cluster.datastore.messages.UpdateSchemaContex 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; @@ -71,6 +74,7 @@ import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListene 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; @@ -125,6 +129,8 @@ public class Shard extends RaftActor { private final Optional roleChangeNotifier; + private final MessageTracker appendEntriesReplyTracker; + /** * Coordinates persistence recovery on startup. */ @@ -133,6 +139,8 @@ public class Shard extends RaftActor { private final Map transactionChains = new HashMap<>(); + private final String txnDispatcherPath; + protected Shard(final ShardIdentifier name, final Map peerAddresses, final DatastoreContext datastoreContext, final SchemaContext schemaContext) { super(name.toString(), mapPeerAddresses(peerAddresses), @@ -141,7 +149,11 @@ public class Shard extends RaftActor { 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()); @@ -168,6 +180,9 @@ public class Shard extends RaftActor { // create a notifier actor for each cluster member roleChangeNotifier = createRoleChangeNotifier(name.toString()); + + appendEntriesReplyTracker = new MessageTracker(AppendEntriesReply.class, + getRaftActorContext().getConfigParams().getIsolatedCheckIntervalInMillis()); } private static Map mapPeerAddresses( @@ -224,35 +239,50 @@ public class Shard extends RaftActor { 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(); } } @@ -493,32 +523,19 @@ public class Shard extends RaftActor { 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=" @@ -526,6 +543,17 @@ public class Shard extends RaftActor { } } + 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(), diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardManager.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardManager.java index d52965e055..426a2e0934 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardManager.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardManager.java @@ -50,6 +50,7 @@ import org.opendaylight.controller.cluster.datastore.messages.PeerAddressResolve 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; @@ -87,6 +88,8 @@ public class ShardManager extends AbstractUntypedPersistentActorWithMetering { private final Configuration configuration; + private final String shardDispatcherPath; + private ShardManagerInfoMBean mBean; private final DatastoreContext datastoreContext; @@ -105,6 +108,8 @@ public class ShardManager extends AbstractUntypedPersistentActorWithMetering { 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()); @@ -283,8 +288,8 @@ public class ShardManager extends AbstractUntypedPersistentActorWithMetering { 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()); } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ThreePhaseCommitCohortProxy.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ThreePhaseCommitCohortProxy.java index 4f472266c1..4445b14e2e 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ThreePhaseCommitCohortProxy.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ThreePhaseCommitCohortProxy.java @@ -71,7 +71,7 @@ public class ThreePhaseCommitCohortProxy implements DOMStoreThreePhaseCommitCoho private Future buildCohortList() { Future> combinedFutures = Futures.sequence(cohortFutures, - actorContext.getActorSystem().dispatcher()); + actorContext.getClientDispatcher()); return combinedFutures.transform(new AbstractFunction1, Void>() { @Override @@ -83,7 +83,7 @@ public class ThreePhaseCommitCohortProxy implements DOMStoreThreePhaseCommitCoho } return null; } - }, TransactionProxy.SAME_FAILURE_TRANSFORMER, actorContext.getActorSystem().dispatcher()); + }, TransactionProxy.SAME_FAILURE_TRANSFORMER, actorContext.getClientDispatcher()); } @Override @@ -111,7 +111,7 @@ public class ThreePhaseCommitCohortProxy implements DOMStoreThreePhaseCommitCoho finishCanCommit(returnFuture); } } - }, actorContext.getActorSystem().dispatcher()); + }, actorContext.getClientDispatcher()); return returnFuture; } @@ -158,7 +158,7 @@ public class ThreePhaseCommitCohortProxy implements DOMStoreThreePhaseCommitCoho } returnFuture.set(Boolean.valueOf(result)); } - }, actorContext.getActorSystem().dispatcher()); + }, actorContext.getClientDispatcher()); } private Future> invokeCohorts(Object message) { @@ -170,7 +170,7 @@ public class ThreePhaseCommitCohortProxy implements DOMStoreThreePhaseCommitCoho futureList.add(actorContext.executeOperationAsync(cohort, message, actorContext.getTransactionCommitOperationTimeout())); } - return Futures.sequence(futureList, actorContext.getActorSystem().dispatcher()); + return Futures.sequence(futureList, actorContext.getClientDispatcher()); } @Override @@ -239,7 +239,7 @@ public class ThreePhaseCommitCohortProxy implements DOMStoreThreePhaseCommitCoho propagateException, returnFuture, callback); } } - }, actorContext.getActorSystem().dispatcher()); + }, actorContext.getClientDispatcher()); } return returnFuture; @@ -304,7 +304,7 @@ public class ThreePhaseCommitCohortProxy implements DOMStoreThreePhaseCommitCoho callback.success(); } } - }, actorContext.getActorSystem().dispatcher()); + }, actorContext.getClientDispatcher()); } @VisibleForTesting @@ -342,7 +342,7 @@ public class ThreePhaseCommitCohortProxy implements DOMStoreThreePhaseCommitCoho timerContext.stop(); Snapshot timerSnapshot = commitTimer.getSnapshot(); - double allowedLatencyInNanos = timerSnapshot.get98thPercentile(); + double allowedLatencyInNanos = timerSnapshot.get95thPercentile(); long commitTimeoutInSeconds = actorContext.getDatastoreContext() .getShardTransactionCommitTimeoutInSeconds(); diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/TransactionContextImpl.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/TransactionContextImpl.java index 530a36cff6..03d1b3a6d7 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/TransactionContextImpl.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/TransactionContextImpl.java @@ -60,7 +60,7 @@ final class TransactionContextImpl extends AbstractTransactionContext { } private Future completeOperation(Future operationFuture){ - operationFuture.onComplete(this.operationCompleter, actorContext.getActorSystem().dispatcher()); + operationFuture.onComplete(this.operationCompleter, actorContext.getClientDispatcher()); return operationFuture; } @@ -105,7 +105,7 @@ final class TransactionContextImpl extends AbstractTransactionContext { futureList.add(replyFuture); Future> 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. @@ -152,7 +152,7 @@ final class TransactionContextImpl extends AbstractTransactionContext { serializedReadyReply.getClass())); } } - }, TransactionProxy.SAME_FAILURE_TRANSFORMER, actorContext.getActorSystem().dispatcher()); + }, TransactionProxy.SAME_FAILURE_TRANSFORMER, actorContext.getClientDispatcher()); } @Override @@ -198,7 +198,7 @@ final class TransactionContextImpl extends AbstractTransactionContext { Future> combinedFutures = akka.dispatch.Futures.sequence( Lists.newArrayList(recordedOperationFutures), - actorContext.getActorSystem().dispatcher()); + actorContext.getClientDispatcher()); OnComplete> onComplete = new OnComplete>() { @Override @@ -216,7 +216,7 @@ final class TransactionContextImpl extends AbstractTransactionContext { } }; - combinedFutures.onComplete(onComplete, actorContext.getActorSystem().dispatcher()); + combinedFutures.onComplete(onComplete, actorContext.getClientDispatcher()); } } @@ -255,7 +255,7 @@ final class TransactionContextImpl extends AbstractTransactionContext { Future readFuture = executeOperationAsync(new ReadData(path)); - readFuture.onComplete(onComplete, actorContext.getActorSystem().dispatcher()); + readFuture.onComplete(onComplete, actorContext.getClientDispatcher()); } @Override @@ -280,7 +280,7 @@ final class TransactionContextImpl extends AbstractTransactionContext { Future> combinedFutures = akka.dispatch.Futures.sequence( Lists.newArrayList(recordedOperationFutures), - actorContext.getActorSystem().dispatcher()); + actorContext.getClientDispatcher()); OnComplete> onComplete = new OnComplete>() { @Override public void onComplete(Throwable failure, Iterable notUsed) @@ -297,7 +297,7 @@ final class TransactionContextImpl extends AbstractTransactionContext { } }; - combinedFutures.onComplete(onComplete, actorContext.getActorSystem().dispatcher()); + combinedFutures.onComplete(onComplete, actorContext.getClientDispatcher()); } } @@ -332,6 +332,6 @@ final class TransactionContextImpl extends AbstractTransactionContext { Future future = executeOperationAsync(new DataExists(path)); - future.onComplete(onComplete, actorContext.getActorSystem().dispatcher()); + future.onComplete(onComplete, actorContext.getClientDispatcher()); } } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/TransactionProxy.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/TransactionProxy.java index 5bc53442ae..d63ec8010d 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/TransactionProxy.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/TransactionProxy.java @@ -484,7 +484,7 @@ public class TransactionProxy implements DOMStoreReadWriteTransaction { newTxFutureCallback.setPrimaryShard(primaryShard); } } - }, actorContext.getActorSystem().dispatcher()); + }, actorContext.getClientDispatcher()); } return txFutureCallback; @@ -601,7 +601,7 @@ public class TransactionProxy implements DOMStoreReadWriteTransaction { TransactionProxy.this.transactionType.ordinal(), getTransactionChainId()).toSerializable()); - createTxFuture.onComplete(this, actorContext.getActorSystem().dispatcher()); + createTxFuture.onComplete(this, actorContext.getClientDispatcher()); } @Override @@ -621,7 +621,7 @@ public class TransactionProxy implements DOMStoreReadWriteTransaction { public void run() { tryCreateTransaction(); } - }, actorContext.getActorSystem().dispatcher()); + }, actorContext.getClientDispatcher()); return; } } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/utils/ActorContext.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/utils/ActorContext.java index cb06c898fd..26e6318f6d 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/utils/ActorContext.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/utils/ActorContext.java @@ -47,6 +47,7 @@ import org.opendaylight.yangtools.yang.model.api.SchemaContext; 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; @@ -93,6 +94,7 @@ public class ActorContext { 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; @@ -111,6 +113,7 @@ public class ActorContext { 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); @@ -127,6 +130,7 @@ public class ActorContext { transactionOutstandingOperationLimit = new CommonConfig(this.getActorSystem().settings().config()).getMailBoxCapacity(); jmxReporter.start(); + } public DatastoreContext getDatastoreContext() { @@ -200,7 +204,7 @@ public class ActorContext { throw new UnknownMessageException(String.format( "FindPrimary returned unkown response: %s", response)); } - }, FIND_PRIMARY_FAILURE_TRANSFORMER, getActorSystem().dispatcher()); + }, FIND_PRIMARY_FAILURE_TRANSFORMER, getClientDispatcher()); } /** @@ -251,7 +255,7 @@ public class ActorContext { throw new UnknownMessageException(String.format( "FindLocalShard returned unkown response: %s", response)); } - }, getActorSystem().dispatcher()); + }, getClientDispatcher()); } private String findPrimaryPathOrNull(String shardName) { @@ -514,5 +518,17 @@ public class ActorContext { 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); + } } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/utils/Dispatchers.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/utils/Dispatchers.java new file mode 100644 index 0000000000..8de8a9d193 --- /dev/null +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/utils/Dispatchers.java @@ -0,0 +1,60 @@ +/* + * 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); + } +} diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/utils/MessageTracker.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/utils/MessageTracker.java new file mode 100644 index 0000000000..2757d2f5f6 --- /dev/null +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/utils/MessageTracker.java @@ -0,0 +1,261 @@ +/* + * 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. + *
+ * Usage of the API is as follows, + *
+ *
+ *      // 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();
+ *
+ * 
+ */ +public class MessageTracker { + + private static final Context NO_OP_CONTEXT = new NoOpContext(); + + private final Class expectedMessageClass; + + private final long expectedArrivalInterval; + + private final List 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 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 getMessageProcessingTimesSinceLastExpectedMessage(); + } + + private class FailedExpectation implements Error { + + private final Object lastExpectedMessage; + private final Object currentExpectedMessage; + private final List messagesSinceLastExpectedMessage; + private final long expectedTimeInMillis; + private final long actualTimeInMillis; + + public FailedExpectation(Object lastExpectedMessage, Object message, List 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 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 error(); + } + + private static class NoOpContext implements Context { + + @Override + public Context done() { + return this; + } + + @Override + public Optional 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 error() { + return Optional.absent(); + } + } + + private class ErrorContext implements Context { + Object message; + private final Optional error; + Stopwatch stopwatch; + + ErrorContext(Object message, Optional 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 error() { + return error; + } + } +} diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/DataChangeListenerRegistrationProxyTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/DataChangeListenerRegistrationProxyTest.java index 58aec30a84..f6c8f07f6b 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/DataChangeListenerRegistrationProxyTest.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/DataChangeListenerRegistrationProxyTest.java @@ -36,6 +36,7 @@ import org.opendaylight.controller.cluster.datastore.messages.LocalShardNotFound 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; @@ -193,10 +194,12 @@ public class DataChangeListenerRegistrationProxyTest extends AbstractActorTest { 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); @@ -227,7 +230,9 @@ public class DataChangeListenerRegistrationProxyTest extends AbstractActorTest { 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(); diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/DatastoreContextTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/DatastoreContextTest.java index 3e89823718..d3a3a8fc2d 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/DatastoreContextTest.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/DatastoreContextTest.java @@ -28,7 +28,7 @@ public class DatastoreContextTest { 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()); diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ThreePhaseCommitCohortProxyTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ThreePhaseCommitCohortProxyTest.java index d2396e0524..0a2a0d1bc0 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ThreePhaseCommitCohortProxyTest.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ThreePhaseCommitCohortProxyTest.java @@ -66,12 +66,13 @@ public class ThreePhaseCommitCohortProxyTest extends AbstractActorTest { 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(); } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/TransactionProxyTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/TransactionProxyTest.java index 7ce41a4db1..fa2f9187d6 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/TransactionProxyTest.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/TransactionProxyTest.java @@ -129,6 +129,7 @@ public class TransactionProxyTest { 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(); diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/utils/ActorContextTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/utils/ActorContextTest.java index eae46da2ee..3c6a0cef5c 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/utils/ActorContextTest.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/utils/ActorContextTest.java @@ -1,17 +1,20 @@ 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; @@ -299,4 +302,41 @@ public class ActorContextTest extends AbstractActorTest{ 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(); + + } + } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/utils/DispatchersTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/utils/DispatchersTest.java new file mode 100644 index 0000000000..85a0cac3da --- /dev/null +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/utils/DispatchersTest.java @@ -0,0 +1,81 @@ +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 diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/utils/MessageTrackerTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/utils/MessageTrackerTest.java new file mode 100644 index 0000000000..a125b49a5a --- /dev/null +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/utils/MessageTrackerTest.java @@ -0,0 +1,188 @@ +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 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 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 diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/resources/application-with-custom-dispatchers.conf b/opendaylight/md-sal/sal-distributed-datastore/src/test/resources/application-with-custom-dispatchers.conf new file mode 100644 index 0000000000..32c55a65f6 --- /dev/null +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/resources/application-with-custom-dispatchers.conf @@ -0,0 +1,116 @@ +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 diff --git a/opendaylight/md-sal/sal-dom-api/pom.xml b/opendaylight/md-sal/sal-dom-api/pom.xml index 0302a7d920..89fca50354 100644 --- a/opendaylight/md-sal/sal-dom-api/pom.xml +++ b/opendaylight/md-sal/sal-dom-api/pom.xml @@ -33,6 +33,20 @@ org.osgi org.osgi.core + + + junit + junit + + + org.opendaylight.controller + sal-test-model + + + org.opendaylight.yangtools + yang-data-impl + test + diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeIdentifier.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeIdentifier.java index 7370ebee7f..f404c0637f 100644 --- a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeIdentifier.java +++ b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeIdentifier.java @@ -8,17 +8,19 @@ package org.opendaylight.controller.md.sal.dom.api; 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, Serializable { +public final class DOMDataTreeIdentifier implements Immutable, Path, Serializable, Comparable { private static final long serialVersionUID = 1L; private final YangInstanceIdentifier rootIdentifier; private final LogicalDatastoreType datastoreType; @@ -74,4 +76,30 @@ public final class DOMDataTreeIdentifier implements Immutable, Path mi = rootIdentifier.getPathArguments().iterator(); + final Iterator 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; + } } diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeInaccessibleException.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeInaccessibleException.java new file mode 100644 index 0000000000..eea1be51bb --- /dev/null +++ b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeInaccessibleException.java @@ -0,0 +1,33 @@ +/* + * 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; + } +} diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeListener.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeListener.java new file mode 100644 index 0000000000..083cd10ef3 --- /dev/null +++ b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeListener.java @@ -0,0 +1,42 @@ +/* + * 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 changes, @Nonnull Map> 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 causes); +} diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeListeningException.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeListeningException.java new file mode 100644 index 0000000000..e0d9b427d9 --- /dev/null +++ b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeListeningException.java @@ -0,0 +1,24 @@ +/* + * 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); + } +} diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeLoopException.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeLoopException.java new file mode 100644 index 0000000000..8f498a887d --- /dev/null +++ b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeLoopException.java @@ -0,0 +1,26 @@ +/* + * 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); + } +} diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeProducer.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeProducer.java new file mode 100644 index 0000000000..cbfa0122f2 --- /dev/null +++ b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeProducer.java @@ -0,0 +1,94 @@ +/* + * 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 subtrees); + + /** + * {@inheritDoc} + * + * @throws DOMDataTreeProducerBusyException when there is an open transaction. + */ + @Override + void close() throws DOMDataTreeProducerException; +} diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeProducerBusyException.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeProducerBusyException.java new file mode 100644 index 0000000000..a83a5ca3d2 --- /dev/null +++ b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeProducerBusyException.java @@ -0,0 +1,24 @@ +/* + * 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); + } +} diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeProducerException.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeProducerException.java new file mode 100644 index 0000000000..16c26760ff --- /dev/null +++ b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeProducerException.java @@ -0,0 +1,23 @@ +/* + * 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); + } +} diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeProducerFactory.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeProducerFactory.java new file mode 100644 index 0000000000..89ac8d1e6c --- /dev/null +++ b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeProducerFactory.java @@ -0,0 +1,26 @@ +/* + * 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 subtrees); +} diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeService.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeService.java new file mode 100644 index 0000000000..21ff44c539 --- /dev/null +++ b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeService.java @@ -0,0 +1,62 @@ +/* + * 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 ListenerRegistration registerListener(@Nonnull T listener, + @Nonnull Collection subtrees, boolean allowRxMerges, @Nonnull Collection producers); +} diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeShard.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeShard.java new file mode 100644 index 0000000000..e9ef0034ea --- /dev/null +++ b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeShard.java @@ -0,0 +1,35 @@ +/* + * 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); +} diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeShardingConflictException.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeShardingConflictException.java new file mode 100644 index 0000000000..d44316d39d --- /dev/null +++ b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeShardingConflictException.java @@ -0,0 +1,26 @@ +/* + * 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); + } +} diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeShardingService.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeShardingService.java new file mode 100644 index 0000000000..c087224090 --- /dev/null +++ b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeShardingService.java @@ -0,0 +1,37 @@ +/* + * 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 ListenerRegistration registerDataTreeShard(@Nonnull DOMDataTreeIdentifier prefix, @Nonnull T shard) throws DOMDataTreeShardingConflictException; +} diff --git a/opendaylight/md-sal/sal-dom-api/src/test/java/org/opendaylight/controller/md/sal/dom/api/AbstractDOMDataTreeServiceTestSuite.java b/opendaylight/md-sal/sal-dom-api/src/test/java/org/opendaylight/controller/md/sal/dom/api/AbstractDOMDataTreeServiceTestSuite.java new file mode 100644 index 0000000000..896c606eb9 --- /dev/null +++ b/opendaylight/md-sal/sal-dom-api/src/test/java/org/opendaylight/controller/md/sal/dom/api/AbstractDOMDataTreeServiceTestSuite.java @@ -0,0 +1,70 @@ +/* + * 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 f = tx.submit(); + assertNotNull(f); + + f.checkedGet(); + } + } + + // TODO: simple listener +} diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/AbstractDOMForwardedTransactionFactory.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/AbstractDOMForwardedTransactionFactory.java index 08888c13cf..8ee928e878 100644 --- a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/AbstractDOMForwardedTransactionFactory.java +++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/AbstractDOMForwardedTransactionFactory.java @@ -9,6 +9,7 @@ package org.opendaylight.controller.md.sal.dom.broker.impl; 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; @@ -77,7 +78,7 @@ abstract class AbstractDOMForwardedTransactionFactory submit(final DOMDataWriteTransaction transaction, - final Iterable cohorts); + final Collection cohorts); /** * Creates a new composite read-only transaction diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/CommitCoordinationTask.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/CommitCoordinationTask.java index e0ac702dad..b85350f95d 100644 --- a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/CommitCoordinationTask.java +++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/CommitCoordinationTask.java @@ -8,9 +8,9 @@ package org.opendaylight.controller.md.sal.dom.broker.impl; 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; @@ -32,18 +32,16 @@ final class CommitCoordinationTask implements Callable { }; private static final Logger LOG = LoggerFactory.getLogger(CommitCoordinationTask.class); - private final Iterable cohorts; + private final Collection cohorts; private final DurationStatisticsTracker commitStatTracker; private final DOMDataWriteTransaction tx; - private final int cohortSize; public CommitCoordinationTask(final DOMDataWriteTransaction transaction, - final Iterable cohorts, + final Collection 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 @@ -115,7 +113,7 @@ final class CommitCoordinationTask implements Callable { * */ 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(); @@ -162,7 +160,7 @@ final class CommitCoordinationTask implements Callable { * */ 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(); @@ -205,7 +203,7 @@ final class CommitCoordinationTask implements Callable { * @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(); @@ -256,7 +254,7 @@ final class CommitCoordinationTask implements Callable { */ private ListenableFuture 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(); diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMDataBrokerTransactionChainImpl.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMDataBrokerTransactionChainImpl.java index 77387c761c..201eb81a94 100644 --- a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMDataBrokerTransactionChainImpl.java +++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMDataBrokerTransactionChainImpl.java @@ -10,6 +10,7 @@ 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 java.util.Collection; import java.util.Map; import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; import java.util.concurrent.atomic.AtomicLong; @@ -87,7 +88,7 @@ final class DOMDataBrokerTransactionChainImpl extends AbstractDOMForwardedTransa @Override public CheckedFuture submit( - final DOMDataWriteTransaction transaction, final Iterable cohorts) { + final DOMDataWriteTransaction transaction, final Collection cohorts) { checkNotFailed(); checkNotClosed(); diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/SerializedDOMDataBroker.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/SerializedDOMDataBroker.java index 268b1b8584..ad23e3a72b 100644 --- a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/SerializedDOMDataBroker.java +++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/SerializedDOMDataBroker.java @@ -11,6 +11,7 @@ import com.google.common.util.concurrent.CheckedFuture; 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; @@ -57,7 +58,7 @@ public class SerializedDOMDataBroker extends AbstractDOMDataBroker { @Override protected CheckedFuture submit(final DOMDataWriteTransaction transaction, - final Iterable cohorts) { + final Collection 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()); diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/ShardRegistration.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/ShardRegistration.java new file mode 100644 index 0000000000..9a71089946 --- /dev/null +++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/ShardRegistration.java @@ -0,0 +1,33 @@ +/* + * 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 extends AbstractListenerRegistration { + 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); + } +} diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/ShardedDOMDataTree.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/ShardedDOMDataTree.java new file mode 100644 index 0000000000..11eae5d833 --- /dev/null +++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/ShardedDOMDataTree.java @@ -0,0 +1,181 @@ +/* + * 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 shardingTables = new EnumMap<>(LogicalDatastoreType.class); + @GuardedBy("this") + private final Map 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 ListenerRegistration registerDataTreeShard(final DOMDataTreeIdentifier prefix, final T shard) throws DOMDataTreeShardingConflictException { + final ShardRegistration 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(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 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 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 subtrees) { + Preconditions.checkArgument(!subtrees.isEmpty(), "Subtrees may not be empty"); + + final Map 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 subtrees) { + Preconditions.checkNotNull(parent); + + final Map shardMap = new HashMap<>(); + for (DOMDataTreeIdentifier s : subtrees) { + shardMap.put(s, lookupShard(s).getRegistration().getInstance()); + } + + return createProducer(shardMap); + } + + @Override + public synchronized ListenerRegistration registerListener(final T listener, final Collection subtrees, final boolean allowRxMerges, final Collection producers) { + // TODO Auto-generated method stub + return null; + } +} diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/ShardedDOMDataTreeProducer.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/ShardedDOMDataTreeProducer.java new file mode 100644 index 0000000000..9712b25ac9 --- /dev/null +++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/ShardedDOMDataTreeProducer.java @@ -0,0 +1,210 @@ +/* + * 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 shardToChain; + private final Map idToShard; + private final ShardedDOMDataTree dataTree; + + @GuardedBy("this") + private Map children = Collections.emptyMap(); + @GuardedBy("this") + private DOMDataWriteTransaction openTx; + @GuardedBy("this") + private boolean closed; + + ShardedDOMDataTreeProducer(final ShardedDOMDataTree dataTree, final Map shardMap, final Set shards) { + this.dataTree = Preconditions.checkNotNull(dataTree); + + // Create shard -> chain map + final Builder cb = ImmutableBiMap.builder(); + final Queue 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 shardToTx = new HashMap<>(); + for (Entry e : shardToChain.entrySet()) { + shardToTx.put(e.getKey(), e.getValue().newWriteOnlyTransaction()); + } + + // Create the ID->transaction map + final ImmutableMap.Builder b = ImmutableMap.builder(); + for (Entry 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 e : children.entrySet()) { + if (e.getKey().contains(s)) { + return e.getValue(); + } + } + + return null; + } + + @Override + public synchronized DOMDataTreeProducer createProducer(final Collection 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 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 shardMap) { + /* + * FIXME: we do not allow multiple multiple shards in a producer because we do not implement the + * synchronization primitives yet + */ + final Set 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 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; + } +} diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/ShardedDOMDataWriteTransaction.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/ShardedDOMDataWriteTransaction.java new file mode 100644 index 0000000000..33f15e3f67 --- /dev/null +++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/ShardedDOMDataWriteTransaction.java @@ -0,0 +1,125 @@ +/* + * 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 idToTransaction; + private final ShardedDOMDataTreeProducer producer; + private final String identifier; + @GuardedBy("this") + private boolean closed = false; + + ShardedDOMDataWriteTransaction(final ShardedDOMDataTreeProducer producer, final Map 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 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 submit() { + Preconditions.checkState(!closed, "Transaction %s is already closed", identifier); + + final Set txns = ImmutableSet.copyOf(idToTransaction.values()); + final List 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> 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); + } +} diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/ShardingTableEntry.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/ShardingTableEntry.java new file mode 100644 index 0000000000..fcd0ebdca0 --- /dev/null +++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/ShardingTableEntry.java @@ -0,0 +1,97 @@ +/* + * 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 { + private static final Logger LOG = LoggerFactory.getLogger(ShardingTableEntry.class); + private final Map 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 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 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 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()); + } +} diff --git a/opendaylight/md-sal/sal-dom-broker/src/test/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMBrokerPerformanceTest.java b/opendaylight/md-sal/sal-dom-broker/src/test/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMBrokerPerformanceTest.java index 2f02f981ab..2a7c8b6682 100644 --- a/opendaylight/md-sal/sal-dom-broker/src/test/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMBrokerPerformanceTest.java +++ b/opendaylight/md-sal/sal-dom-broker/src/test/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMBrokerPerformanceTest.java @@ -60,8 +60,8 @@ public class DOMBrokerPerformanceTest { @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); diff --git a/opendaylight/md-sal/sal-dom-broker/src/test/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMBrokerTest.java b/opendaylight/md-sal/sal-dom-broker/src/test/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMBrokerTest.java index c1d301c549..cd8ac09980 100644 --- a/opendaylight/md-sal/sal-dom-broker/src/test/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMBrokerTest.java +++ b/opendaylight/md-sal/sal-dom-broker/src/test/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMBrokerTest.java @@ -57,9 +57,9 @@ public class DOMBrokerTest { 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); diff --git a/opendaylight/md-sal/sal-dom-broker/src/test/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMTransactionChainTest.java b/opendaylight/md-sal/sal-dom-broker/src/test/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMTransactionChainTest.java index 03d39a2a62..aba78f7559 100644 --- a/opendaylight/md-sal/sal-dom-broker/src/test/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMTransactionChainTest.java +++ b/opendaylight/md-sal/sal-dom-broker/src/test/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMTransactionChainTest.java @@ -41,8 +41,8 @@ public class DOMTransactionChainTest { @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); diff --git a/opendaylight/md-sal/sal-dom-xsql/pom.xml b/opendaylight/md-sal/sal-dom-xsql/pom.xml index c7fd20248d..f0e27cf2ac 100644 --- a/opendaylight/md-sal/sal-dom-xsql/pom.xml +++ b/opendaylight/md-sal/sal-dom-xsql/pom.xml @@ -48,6 +48,10 @@ org.osgi org.osgi.core + + junit + junit + diff --git a/opendaylight/md-sal/sal-dummy-distributed-datastore/pom.xml b/opendaylight/md-sal/sal-dummy-distributed-datastore/pom.xml index c7ee3a5c0c..85ae59b161 100644 --- a/opendaylight/md-sal/sal-dummy-distributed-datastore/pom.xml +++ b/opendaylight/md-sal/sal-dummy-distributed-datastore/pom.xml @@ -81,7 +81,6 @@ org.slf4j slf4j-simple - ${slf4j.version} test @@ -94,7 +93,6 @@ org.slf4j slf4j-simple - 1.7.7 @@ -178,7 +176,6 @@ org.apache.maven.plugins maven-shade-plugin - 1.5 package diff --git a/opendaylight/md-sal/sal-dummy-distributed-datastore/src/main/java/org/opendaylight/controller/dummy/datastore/DummyShard.java b/opendaylight/md-sal/sal-dummy-distributed-datastore/src/main/java/org/opendaylight/controller/dummy/datastore/DummyShard.java index 3dffdfce57..0b72a32f10 100644 --- a/opendaylight/md-sal/sal-dummy-distributed-datastore/src/main/java/org/opendaylight/controller/dummy/datastore/DummyShard.java +++ b/opendaylight/md-sal/sal-dummy-distributed-datastore/src/main/java/org/opendaylight/controller/dummy/datastore/DummyShard.java @@ -11,6 +11,8 @@ package org.opendaylight.controller.dummy.datastore; 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; @@ -27,6 +29,7 @@ public class DummyShard extends UntypedActor{ 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; @@ -57,10 +60,21 @@ public class DummyShard extends UntypedActor{ } 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()); } diff --git a/opendaylight/md-sal/sal-dummy-distributed-datastore/src/main/resources/simplelogger.properties b/opendaylight/md-sal/sal-dummy-distributed-datastore/src/main/resources/simplelogger.properties index 067c048231..cd2d082079 100644 --- a/opendaylight/md-sal/sal-dummy-distributed-datastore/src/main/resources/simplelogger.properties +++ b/opendaylight/md-sal/sal-dummy-distributed-datastore/src/main/resources/simplelogger.properties @@ -3,4 +3,4 @@ org.slf4j.simpleLogger.dateTimeFormat=hh:mm:ss,S a 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 diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDataStoreTest.java b/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDataStoreTest.java index 4720f4b4b9..9de4892d91 100644 --- a/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDataStoreTest.java +++ b/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDataStoreTest.java @@ -45,7 +45,7 @@ public class InMemoryDataStoreTest { @Before public void setupStore() { - domStore = new InMemoryDOMDataStore("TEST", MoreExecutors.sameThreadExecutor()); + domStore = new InMemoryDOMDataStore("TEST", MoreExecutors.newDirectExecutorService()); schemaContext = TestModel.createTestContext(); domStore.onGlobalContextUpdated(schemaContext); } diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/SchemaUpdateForTransactionTest.java b/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/SchemaUpdateForTransactionTest.java index 15e5f716f6..e7af4dffae 100644 --- a/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/SchemaUpdateForTransactionTest.java +++ b/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/SchemaUpdateForTransactionTest.java @@ -31,7 +31,7 @@ public class SchemaUpdateForTransactionTest { @Before public void setupStore() { - domStore = new InMemoryDOMDataStore("TEST", MoreExecutors.sameThreadExecutor()); + domStore = new InMemoryDOMDataStore("TEST", MoreExecutors.newDirectExecutorService()); loadSchemas(RockTheHouseInput.class); } diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/TestDCLExecutorService.java b/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/TestDCLExecutorService.java index f6e6461bf5..a01933c295 100644 --- a/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/TestDCLExecutorService.java +++ b/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/TestDCLExecutorService.java @@ -8,10 +8,9 @@ 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 @@ -21,13 +20,13 @@ import com.google.common.util.concurrent.MoreExecutors; 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; } diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/config/yang/md/sal/connector/netconf/NetconfConnectorModule.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/config/yang/md/sal/connector/netconf/NetconfConnectorModule.java index b966fae3d4..44b2435da2 100644 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/config/yang/md/sal/connector/netconf/NetconfConnectorModule.java +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/config/yang/md/sal/connector/netconf/NetconfConnectorModule.java @@ -111,7 +111,7 @@ public final class NetconfConnectorModule extends org.opendaylight.controller.co 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); diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfDevice.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfDevice.java index 9a5b239024..281f82ba2a 100644 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfDevice.java +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfDevice.java @@ -223,6 +223,8 @@ public final class NetconfDevice implements RemoteDevice sourceRegistration : sourceRegistrations) { sourceRegistration.close(); @@ -314,6 +316,7 @@ public final class NetconfDevice implements RemoteDevice filterNotification(CompositeNode notification); diff --git a/opendaylight/md-sal/sal-test-model/src/main/yang/opendaylight-of-migration-test-model.yang b/opendaylight/md-sal/sal-test-model/src/main/yang/opendaylight-of-migration-test-model.yang new file mode 100644 index 0000000000..24ff5dd965 --- /dev/null +++ b/opendaylight/md-sal/sal-test-model/src/main/yang/opendaylight-of-migration-test-model.yang @@ -0,0 +1,156 @@ +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; + } + } + } +} diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/config/InstanceConfig.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/config/InstanceConfig.java index 4ae4de18f3..ba7b2f20e4 100644 --- a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/config/InstanceConfig.java +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/config/InstanceConfig.java @@ -17,7 +17,7 @@ import java.util.Map; 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; @@ -46,9 +46,9 @@ public final class InstanceConfig { private final Map yangToAttrConfig; private final String nullableDummyContainerName; private final Map jmxToAttrConfig; - private final ConfigRegistryClient configRegistryClient; + private final BeanReader configRegistryClient; - public InstanceConfig(ConfigRegistryClient configRegistryClient, Map yangNamesToAttributes, + public InstanceConfig(BeanReader configRegistryClient, Map yangNamesToAttributes, String nullableDummyContainerName) { this.yangToAttrConfig = yangNamesToAttributes; diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/Datastore.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/Datastore.java index d736595719..9c55953bbc 100644 --- a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/Datastore.java +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/Datastore.java @@ -26,7 +26,7 @@ public enum Datastore { TransactionProvider transactionProvider) { switch (source) { case running: - return new RunningDatastoreQueryStrategy(); + return new RunningDatastoreQueryStrategy(transactionProvider); case candidate: return new CandidateDatastoreQueryStrategy(transactionProvider); default: diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/EditConfig.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/EditConfig.java index ca6a8c46b9..bc84734190 100644 --- a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/EditConfig.java +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/EditConfig.java @@ -20,6 +20,7 @@ import java.util.Set; 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; @@ -262,7 +263,7 @@ public class EditConfig extends AbstractConfigNetconfOperation { public static Map> transformMbeToModuleConfigs - (final ConfigRegistryClient configRegistryClient, Map> mBeanEntries) { Map> namespaceToModuleNameToModuleConfig = Maps.newHashMap(); @@ -295,7 +296,6 @@ public class EditConfig extends AbstractConfigNetconfOperation { @Override protected Element handleWithNoSubsequentOperations(Document document, XmlElement xml) throws NetconfDocumentedException { - EditConfigXmlParser.EditConfigExecution editConfigExecution; Config cfg = getConfigMapping(getConfigRegistryClient(), yangStoreSnapshot); editConfigExecution = editConfigXmlParser.fromXml(xml, cfg); diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/get/Get.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/get/Get.java index 27d53cdc32..fe7f2773cd 100644 --- a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/get/Get.java +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/get/Get.java @@ -14,6 +14,7 @@ import java.util.Map; 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; @@ -27,6 +28,7 @@ import org.opendaylight.controller.netconf.confignetconfconnector.operations.Abs 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; @@ -38,12 +40,14 @@ import org.w3c.dom.Element; 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; } @@ -115,23 +119,30 @@ public class Get extends AbstractConfigNetconfOperation { protected Element handleWithNoSubsequentOperations(Document document, XmlElement xml) throws NetconfDocumentedException { checkXml(xml); - final Set runtimeBeans = getConfigRegistryClient().lookupRuntimeBeans(); + final ObjectName testTransaction = transactionProvider.getOrCreateReadTransaction(); + final ConfigTransactionClient registryClient = getConfigRegistryClient().getConfigTransactionClient(testTransaction); - //Transaction provider required only for candidate datastore - final Set 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 runtimeBeans = getConfigRegistryClient().lookupRuntimeBeans(); - final Map> moduleRuntimes = createModuleRuntimes(getConfigRegistryClient(), - yangStoreSnapshot.getModuleMXBeanEntryMap()); - final Map> moduleConfigs = EditConfig.transformMbeToModuleConfigs( - getConfigRegistryClient(), yangStoreSnapshot.getModuleMXBeanEntryMap()); + final Set configBeans = Datastore.getInstanceQueryStrategy(Datastore.running, transactionProvider) + .queryInstances(getConfigRegistryClient()); - final Runtime runtime = new Runtime(moduleRuntimes, moduleConfigs); + final Map> moduleRuntimes = createModuleRuntimes(getConfigRegistryClient(), + yangStoreSnapshot.getModuleMXBeanEntryMap()); + final Map> 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(); + } } } diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/getconfig/GetConfig.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/getconfig/GetConfig.java index 350ace5eb1..2c4bde0ee8 100644 --- a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/getconfig/GetConfig.java +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/getconfig/GetConfig.java @@ -72,23 +72,36 @@ public class GetConfig extends AbstractConfigNetconfOperation { private Element getResponseInternal(final Document document, final ConfigRegistryClient configRegistryClient, final Datastore source) { - Element dataElement = XmlUtil.createElement(document, XmlNetconfConstants.DATA_KEY, Optional.absent()); - final Set 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.absent()); + final Set 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 diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/getconfig/RunningDatastoreQueryStrategy.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/getconfig/RunningDatastoreQueryStrategy.java index ae9cb2eb37..74b5f60a10 100644 --- a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/getconfig/RunningDatastoreQueryStrategy.java +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/getconfig/RunningDatastoreQueryStrategy.java @@ -11,12 +11,22 @@ package org.opendaylight.controller.netconf.confignetconfconnector.operations.ge 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 queryInstances(ConfigRegistryClient configRegistryClient) { - return configRegistryClient.lookupConfigBeans(); + ObjectName on = transactionProvider.getOrCreateReadTransaction(); + ConfigTransactionClient proxy = configRegistryClient.getConfigTransactionClient(on); + return proxy.lookupConfigBeans(); } } diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/NetconfOperationProvider.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/NetconfOperationProvider.java index 612bd85998..e3fdc056d9 100644 --- a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/NetconfOperationProvider.java +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/NetconfOperationProvider.java @@ -52,7 +52,7 @@ final class NetconfOperationProvider { 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)); diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/NetconfOperationServiceFactoryImpl.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/NetconfOperationServiceFactoryImpl.java index 82c04a50e0..590f4c4ceb 100644 --- a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/NetconfOperationServiceFactoryImpl.java +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/NetconfOperationServiceFactoryImpl.java @@ -8,10 +8,19 @@ 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; @@ -68,4 +77,117 @@ public class NetconfOperationServiceFactoryImpl implements NetconfOperationServi public NetconfOperationServiceImpl createService(String netconfSessionIdForReporting) { return new NetconfOperationServiceImpl(yangStoreService, jmxClient, netconfSessionIdForReporting); } + + + @Override + public Set getCapabilities() { + return setupCapabilities(yangStoreService); + } + + @Override + public AutoCloseable registerCapabilityListener(final CapabilityListener listener) { + return yangStoreService.registerCapabilityListener(listener); + } + + public static Set setupCapabilities(final YangStoreContext yangStoreSnapshot) { + Set 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 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 getModuleNamespace() { + return Optional.absent(); + } + + @Override + public Optional getModuleName() { + return Optional.absent(); + } + + @Override + public Optional getRevision() { + return Optional.absent(); + } + + @Override + public Optional getCapabilitySchema() { + return Optional.absent(); + } + + @Override + public Collection 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 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 getModuleName() { + return Optional.of(moduleName); + } + + @Override + public Optional getModuleNamespace() { + return Optional.of(moduleNamespace); + } + + @Override + public Optional getRevision() { + return Optional.of(revision); + } + } } diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/NetconfOperationServiceImpl.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/NetconfOperationServiceImpl.java index ef0a72c0f0..28001851cc 100644 --- a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/NetconfOperationServiceImpl.java +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/NetconfOperationServiceImpl.java @@ -8,18 +8,11 @@ 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}. @@ -28,132 +21,23 @@ public class NetconfOperationServiceImpl implements NetconfOperationService { 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 getCapabilities() { - return setupCapabilities(yangStoreService); - } - @Override public Set getNetconfOperations() { return operationProvider.getOperations(); } - private static Set setupCapabilities(final YangStoreContext yangStoreSnapshot) { - Set 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 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 getModuleNamespace() { - return Optional.absent(); - } - - @Override - public Optional getModuleName() { - return Optional.absent(); - } - - @Override - public Optional getRevision() { - return Optional.absent(); - } - - @Override - public Optional getCapabilitySchema() { - return Optional.absent(); - } - - @Override - public Collection 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 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 getModuleName() { - return Optional.of(moduleName); - } - - @Override - public Optional getModuleNamespace() { - return Optional.of(moduleNamespace); - } - - @Override - public Optional getRevision() { - return Optional.of(revision); - } - } } diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/YangStoreService.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/YangStoreService.java index de151a8969..176800fb97 100644 --- a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/YangStoreService.java +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/YangStoreService.java @@ -14,6 +14,7 @@ import com.google.common.collect.Lists; 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; @@ -21,6 +22,8 @@ import java.util.concurrent.Executors; 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; @@ -77,6 +80,8 @@ public class YangStoreService implements YangStoreContext { } }); + private final Set listeners = Collections.synchronizedSet(new HashSet()); + public YangStoreService(final SchemaContextProvider schemaContextProvider, final BundleContext context) { this(schemaContextProvider, new NotificationCollectorTracker(context)); } @@ -130,7 +135,31 @@ public class YangStoreService implements YangStoreContext { 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_TO_CAPABILITY = new Function() { + @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) { @@ -142,7 +171,19 @@ public class YangStoreService implements YangStoreContext { final YangStoreContext current = getYangStoreSnapshot(); if(current.equals(previous) == false) { - notificationPublisher.onCapabilityChanged(computeDiff(previous, current)); + final Sets.SetView removed = Sets.difference(previous.getModules(), current.getModules()); + final Sets.SetView 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))); + } } } } @@ -150,15 +191,11 @@ public class YangStoreService implements YangStoreContext { private static final Function MODULE_TO_URI = new Function() { @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 removed = Sets.difference(previous.getModules(), current.getModules()); - final Sets.SetView added = Sets.difference(current.getModules(), previous.getModules()); - + static NetconfCapabilityChange computeDiff(final Sets.SetView removed, final Sets.SetView 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))); diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/transactions/TransactionProvider.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/transactions/TransactionProvider.java index b2ee63a987..7655cb300d 100644 --- a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/transactions/TransactionProvider.java +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/transactions/TransactionProvider.java @@ -31,7 +31,8 @@ public class TransactionProvider implements AutoCloseable { private final ConfigRegistryClient configRegistryClient; private final String netconfSessionIdForReporting; - private ObjectName transaction; + private ObjectName candidateTx; + private ObjectName readTx; private final List allOpenedTransactions = new ArrayList<>(); private static final String NO_TRANSACTION_FOUND_FOR_SESSION = "No transaction found for session "; @@ -56,18 +57,34 @@ public class TransactionProvider implements AutoCloseable { public synchronized Optional 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 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) { @@ -80,9 +97,20 @@ public class TransactionProvider implements AutoCloseable { 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 ta = getReadTransaction(); + + if (ta.isPresent()) { + return ta.get(); + } + readTx = configRegistryClient.beginConfig(); + allOpenedTransactions.add(readTx); + return readTx; } /** @@ -109,8 +137,8 @@ public class TransactionProvider implements AutoCloseable { 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 @@ -131,8 +159,19 @@ public class TransactionProvider implements AutoCloseable { 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 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) { diff --git a/opendaylight/netconf/config-netconf-connector/src/test/java/org/opendaylight/controller/netconf/confignetconfconnector/NetconfMappingTest.java b/opendaylight/netconf/config-netconf-connector/src/test/java/org/opendaylight/controller/netconf/confignetconfconnector/NetconfMappingTest.java index fdae71a16b..d6b5e62b27 100644 --- a/opendaylight/netconf/config-netconf-connector/src/test/java/org/opendaylight/controller/netconf/confignetconfconnector/NetconfMappingTest.java +++ b/opendaylight/netconf/config-netconf-connector/src/test/java/org/opendaylight/controller/netconf/confignetconfconnector/NetconfMappingTest.java @@ -95,8 +95,8 @@ import org.opendaylight.controller.netconf.confignetconfconnector.osgi.YangStore 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; @@ -141,7 +141,9 @@ public class NetconfMappingTest extends AbstractConfigTest { @Mock NetconfOperationRouter netconfOperationRouter; @Mock - NetconfOperationServiceSnapshotImpl netconfOperationServiceSnapshot; + AggregatedNetconfOperationServiceFactory netconfOperationServiceSnapshot; + @Mock + private AutoCloseable sessionCloseable; private TransactionProvider transactionProvider; @@ -157,13 +159,12 @@ public class NetconfMappingTest extends AbstractConfigTest { 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)); @@ -376,7 +377,7 @@ public class NetconfMappingTest extends AbstractConfigTest { edit("netconfMessages/editConfig_none.xml"); closeSession(); - verify(netconfOperationServiceSnapshot).close(); + verify(sessionCloseable).close(); verifyNoMoreInteractions(netconfOperationRouter); verifyNoMoreInteractions(netconfOperationServiceSnapshot); } @@ -390,7 +391,7 @@ public class NetconfMappingTest extends AbstractConfigTest { 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"); } @@ -715,7 +716,7 @@ public class NetconfMappingTest extends AbstractConfigTest { } 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"); } diff --git a/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/ConfigPusherImpl.java b/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/ConfigPusherImpl.java index 0aebc68bbe..22b061a128 100644 --- a/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/ConfigPusherImpl.java +++ b/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/ConfigPusherImpl.java @@ -33,10 +33,10 @@ import org.opendaylight.controller.config.api.ConflictingVersionException; 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; @@ -177,20 +177,20 @@ public class ConfigPusherImpl implements ConfigPusher { } catch(RuntimeException e) { throw new NotEnoughCapabilitiesException("Netconf service not stable for " + idForReporting, e); } - Set notFoundDiff = computeNotFoundCapabilities(expectedCapabilities, serviceCandidate); + Set 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 computeNotFoundCapabilities(Set expectedCapabilities, NetconfOperationService serviceCandidate) { + private static Set computeNotFoundCapabilities(Set expectedCapabilities, NetconfOperationServiceFactory serviceCandidate) { Collection actual = Collections2.transform(serviceCandidate.getCapabilities(), new Function() { @Override public String apply(@Nonnull final Capability input) { diff --git a/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/osgi/ConfigPersisterActivator.java b/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/osgi/ConfigPersisterActivator.java index 787f8b10b0..b27bec3c83 100644 --- a/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/osgi/ConfigPersisterActivator.java +++ b/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/osgi/ConfigPersisterActivator.java @@ -13,10 +13,10 @@ import java.lang.management.ManagementFactory; 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; @@ -70,7 +70,7 @@ public class ConfigPersisterActivator implements BundleActivator { 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) { @@ -103,7 +103,7 @@ public class ConfigPersisterActivator implements BundleActivator { ")"; } - class OuterCustomizer implements ServiceTrackerCustomizer { + class OuterCustomizer implements ServiceTrackerCustomizer { private final BundleContext context; private final InnerCustomizer innerCustomizer; @@ -113,7 +113,7 @@ public class ConfigPersisterActivator implements BundleActivator { } @Override - public NetconfOperationProvider addingService(ServiceReference reference) { + public NetconfOperationServiceFactory addingService(ServiceReference reference) { LOG.trace("Got OuterCustomizer.addingService {}", reference); // JMX was registered, track config-netconf-connector Filter filter; @@ -127,12 +127,12 @@ public class ConfigPersisterActivator implements BundleActivator { } @Override - public void modifiedService(ServiceReference reference, NetconfOperationProvider service) { + public void modifiedService(ServiceReference reference, NetconfOperationServiceFactory service) { } @Override - public void removedService(ServiceReference reference, NetconfOperationProvider service) { + public void removedService(ServiceReference reference, NetconfOperationServiceFactory service) { } } @@ -141,7 +141,9 @@ public class ConfigPersisterActivator implements BundleActivator { private final List 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 configs, long maxWaitForCapabilitiesMillis, long conflictingVersionTimeoutMillis, PersisterAggregator persisterAggregator) { @@ -153,6 +155,10 @@ public class ConfigPersisterActivator implements BundleActivator { @Override public NetconfOperationServiceFactory addingService(ServiceReference 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); @@ -196,10 +202,12 @@ public class ConfigPersisterActivator implements BundleActivator { @Override public void modifiedService(ServiceReference reference, NetconfOperationServiceFactory service) { + LOG.trace("Got InnerCustomizer.modifiedService {}", reference); } @Override public void removedService(ServiceReference reference, NetconfOperationServiceFactory service) { + LOG.trace("Got InnerCustomizer.removedService {}", reference); } } diff --git a/opendaylight/netconf/config-persister-impl/src/test/java/org/opendaylight/controller/netconf/persist/impl/osgi/ConfigPersisterTest.java b/opendaylight/netconf/config-persister-impl/src/test/java/org/opendaylight/controller/netconf/persist/impl/osgi/ConfigPersisterTest.java index 142d8f5226..7b79e41f38 100644 --- a/opendaylight/netconf/config-persister-impl/src/test/java/org/opendaylight/controller/netconf/persist/impl/osgi/ConfigPersisterTest.java +++ b/opendaylight/netconf/config-persister-impl/src/test/java/org/opendaylight/controller/netconf/persist/impl/osgi/ConfigPersisterTest.java @@ -20,8 +20,8 @@ import org.junit.After; 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; @@ -41,9 +41,10 @@ public class ConfigPersisterTest { 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()); } @@ -62,7 +63,7 @@ public class ConfigPersisterTest { @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 . Expected but not found: [required-cap]"); @@ -71,7 +72,7 @@ public class ConfigPersisterTest { @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); @@ -86,7 +87,7 @@ public class ConfigPersisterTest { 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(); @@ -109,9 +110,8 @@ public class ConfigPersisterTest { @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"); } @@ -131,7 +131,7 @@ public class ConfigPersisterTest { @Test public void testSuccessConflictingVersionException() throws Exception { - setUpContextAndStartPersister("cap1"); + setUpContextAndStartPersister("cap1", getConflictingService()); doReturn(getConflictingService()).when(ctx.serviceFactory).createService(anyString()); Thread.sleep(500); // working service: diff --git a/opendaylight/netconf/config-persister-impl/src/test/java/org/opendaylight/controller/netconf/persist/impl/osgi/MockedBundleContext.java b/opendaylight/netconf/config-persister-impl/src/test/java/org/opendaylight/controller/netconf/persist/impl/osgi/MockedBundleContext.java index 0d866ecda7..bd18c8c30e 100644 --- a/opendaylight/netconf/config-persister-impl/src/test/java/org/opendaylight/controller/netconf/persist/impl/osgi/MockedBundleContext.java +++ b/opendaylight/netconf/config-persister-impl/src/test/java/org/opendaylight/controller/netconf/persist/impl/osgi/MockedBundleContext.java @@ -12,6 +12,7 @@ import static org.mockito.Matchers.anyString; 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; @@ -28,7 +29,7 @@ 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.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; @@ -60,11 +61,11 @@ final class MockedBundleContext { 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(); @@ -77,9 +78,12 @@ final class MockedBundleContext { 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(); diff --git a/opendaylight/netconf/ietf-netconf-monitoring/src/main/yang/ietf-netconf-monitoring.yang b/opendaylight/netconf/ietf-netconf-monitoring/src/main/yang/ietf-netconf-monitoring.yang index b4ad89b558..84756fc713 100644 --- a/opendaylight/netconf/ietf-netconf-monitoring/src/main/yang/ietf-netconf-monitoring.yang +++ b/opendaylight/netconf/ietf-netconf-monitoring/src/main/yang/ietf-netconf-monitoring.yang @@ -9,7 +9,9 @@ module ietf-netconf-monitoring { import ietf-yang-types { prefix yang; + revision-date "2010-09-24"; } + import ietf-inet-types { prefix inet; } diff --git a/opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/config/yang/netconf/mdsal/mapper/NetconfMdsalMapperModule.java b/opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/config/yang/netconf/mdsal/mapper/NetconfMdsalMapperModule.java index ccf751285a..bf8bdb06f3 100644 --- a/opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/config/yang/netconf/mdsal/mapper/NetconfMdsalMapperModule.java +++ b/opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/config/yang/netconf/mdsal/mapper/NetconfMdsalMapperModule.java @@ -26,7 +26,15 @@ public class NetconfMdsalMapperModule extends org.opendaylight.controller.config @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; } } diff --git a/opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/CurrentSchemaContext.java b/opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/CurrentSchemaContext.java index df671e8f4f..1aa38eb80c 100644 --- a/opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/CurrentSchemaContext.java +++ b/opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/CurrentSchemaContext.java @@ -9,7 +9,12 @@ 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; @@ -18,6 +23,7 @@ import org.opendaylight.yangtools.yang.model.api.SchemaContextListener; public class CurrentSchemaContext implements SchemaContextListener, AutoCloseable { final AtomicReference currentContext = new AtomicReference(); private final ListenerRegistration schemaContextListenerListenerRegistration; + private final Set listeners = Collections.synchronizedSet(Sets.newHashSet()); public SchemaContext getCurrentContext() { Preconditions.checkState(currentContext.get() != null, "Current context not received"); @@ -31,11 +37,28 @@ public class CurrentSchemaContext implements SchemaContextListener, AutoCloseabl @Override public void onGlobalContextUpdated(final SchemaContext schemaContext) { currentContext.set(schemaContext); + // FIXME is notifying all the listeners from this callback wise ? + final Set 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 diff --git a/opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/MdsalNetconfOperationService.java b/opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/MdsalNetconfOperationService.java index f54c5e9838..cc22dd51aa 100644 --- a/opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/MdsalNetconfOperationService.java +++ b/opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/MdsalNetconfOperationService.java @@ -8,34 +8,17 @@ 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); } @@ -44,115 +27,9 @@ public class MdsalNetconfOperationService implements NetconfOperationService { } - // TODO does this get called dynamically ? - @Override - public Set getCapabilities() { - final Set 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 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 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 getModuleNamespace() { - return Optional.absent(); - } - - @Override - public Optional getModuleName() { - return Optional.absent(); - } - - @Override - public Optional getRevision() { - return Optional.absent(); - } - - @Override - public Optional getCapabilitySchema() { - return Optional.absent(); - } - - @Override - public Collection 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 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 getModuleName() { - return Optional.of(moduleName); - } - - @Override - public Optional getModuleNamespace() { - return Optional.of(moduleNamespace); - } - - @Override - public Optional getRevision() { - return Optional.of(revision); - } - } } diff --git a/opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/MdsalNetconfOperationServiceFactory.java b/opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/MdsalNetconfOperationServiceFactory.java index 098f25bf4a..ebb0e9d3d1 100644 --- a/opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/MdsalNetconfOperationServiceFactory.java +++ b/opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/MdsalNetconfOperationServiceFactory.java @@ -8,13 +8,27 @@ 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; @@ -32,4 +46,118 @@ public class MdsalNetconfOperationServiceFactory implements NetconfOperationServ public void close() throws Exception { currentSchemaContext.close(); } + + @Override + public Set getCapabilities() { + return transformCapabilities(currentSchemaContext.getCurrentContext()); + } + + static Set transformCapabilities(final SchemaContext currentContext1) { + final Set 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 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 getModuleNamespace() { + return Optional.absent(); + } + + @Override + public Optional getModuleName() { + return Optional.absent(); + } + + @Override + public Optional getRevision() { + return Optional.absent(); + } + + @Override + public Optional getCapabilitySchema() { + return Optional.absent(); + } + + @Override + public Collection 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 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 getModuleName() { + return Optional.of(moduleName); + } + + @Override + public Optional getModuleNamespace() { + return Optional.of(moduleNamespace); + } + + @Override + public Optional getRevision() { + return Optional.of(revision); + } + } } diff --git a/opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/ops/get/AbstractGet.java b/opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/ops/get/AbstractGet.java index e0c004461c..8f6ff417d6 100644 --- a/opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/ops/get/AbstractGet.java +++ b/opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/ops/get/AbstractGet.java @@ -9,6 +9,7 @@ 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; @@ -115,13 +116,13 @@ public abstract class AbstractGet extends AbstractLastNetconfOperation { } protected static final class GetConfigExecution { - private final Datastore datastore; + private final Optional datastore; - public GetConfigExecution(final Datastore datastore) { + public GetConfigExecution(final Optional datastore) { this.datastore = datastore; } - public Datastore getDatastore() { + public Optional getDatastore() { return datastore; } @@ -132,7 +133,7 @@ public abstract class AbstractGet extends AbstractLastNetconfOperation { throw new NetconfDocumentedException("Incorrect RPC: " + e.getMessage(), e.getErrorType(), e.getErrorTag(), e.getErrorSeverity(), e.getErrorInfo()); } - final Datastore sourceDatastore; + final Optional sourceDatastore; try { sourceDatastore = parseSource(xml); } catch (final NetconfDocumentedException e) { @@ -144,14 +145,12 @@ public abstract class AbstractGet extends AbstractLastNetconfOperation { 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 parseSource(final XmlElement xml) throws NetconfDocumentedException { + final Optional 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.absent(); } private static void validateInputRpc(final XmlElement xml, String operationName) throws NetconfDocumentedException{ diff --git a/opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/ops/get/Get.java b/opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/ops/get/Get.java index a2b2fbb0db..cebd8c8883 100644 --- a/opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/ops/get/Get.java +++ b/opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/ops/get/Get.java @@ -52,13 +52,10 @@ public class Get extends AbstractGet { } final YangInstanceIdentifier dataRoot = ROOT; - DOMDataReadWriteTransaction rwTx = getTransaction(getConfigExecution.getDatastore()); + DOMDataReadWriteTransaction rwTx = getTransaction(Datastore.running); try { final Optional> 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); diff --git a/opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/ops/get/GetConfig.java b/opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/ops/get/GetConfig.java index b56bcc795c..f2d8abbb61 100644 --- a/opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/ops/get/GetConfig.java +++ b/opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/ops/get/GetConfig.java @@ -9,6 +9,7 @@ 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; @@ -52,10 +53,13 @@ public class GetConfig extends AbstractGet { } 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> normalizedNodeOptional = rwTx.read(LogicalDatastoreType.CONFIGURATION, dataRoot).checkedGet(); - if (getConfigExecution.getDatastore() == Datastore.running) { + if (getConfigExecution.getDatastore().get() == Datastore.running) { transactionProvider.abortRunningTransaction(rwTx); rwTx = null; } diff --git a/opendaylight/netconf/mdsal-netconf-connector/src/main/yang/netconf-mdsal-mapper.yang b/opendaylight/netconf/mdsal-netconf-connector/src/main/yang/netconf-mdsal-mapper.yang index b1d04106d7..9d9966e8f1 100644 --- a/opendaylight/netconf/mdsal-netconf-connector/src/main/yang/netconf-mdsal-mapper.yang +++ b/opendaylight/netconf/mdsal-netconf-connector/src/main/yang/netconf-mdsal-mapper.yang @@ -44,6 +44,15 @@ module netconf-mdsal-mapper { } } } + + container mapper-aggregator { + uses config:service-ref { + refine type { + mandatory true; + config:required-identity nnm:netconf-mapper-registry; + } + } + } } } diff --git a/opendaylight/netconf/mdsal-netconf-monitoring/pom.xml b/opendaylight/netconf/mdsal-netconf-monitoring/pom.xml new file mode 100644 index 0000000000..3e78dd19e3 --- /dev/null +++ b/opendaylight/netconf/mdsal-netconf-monitoring/pom.xml @@ -0,0 +1,99 @@ + + + 4.0.0 + + org.opendaylight.controller + netconf-subsystem + 0.3.0-SNAPSHOT + + mdsal-netconf-monitoring + bundle + ${project.artifactId} + + + + + ${project.groupId} + netconf-api + + + ${project.groupId} + netconf-mapping-api + + + ${project.groupId} + netconf-monitoring + + + ${project.groupId} + netconf-util + + + org.opendaylight.controller + sal-binding-config + + + + com.google.guava + guava + + + + org.opendaylight.yangtools + mockito-configuration + + + org.opendaylight.yangtools.model + ietf-inet-types + + + org.slf4j + slf4j-api + + + + + + + org.apache.felix + maven-bundle-plugin + + + org.opendaylight.yangtools + yang-maven-plugin + + + config + + generate-sources + + + + + org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator + ${jmxGeneratorPath} + + urn:opendaylight:params:xml:ns:yang:controller==org.opendaylight.controller.config.yang + + + + org.opendaylight.yangtools.maven.sal.api.gen.plugin.CodeGeneratorImpl + ${salGeneratorPath} + + + true + + + + + + org.opendaylight.controller + yang-jmx-generator-plugin + ${config.version} + + + + + + + diff --git a/opendaylight/netconf/mdsal-netconf-monitoring/src/main/java/org/opendaylight/controller/config/yang/netconf/mdsal/monitoring/MonitoringToMdsalWriter.java b/opendaylight/netconf/mdsal-netconf-monitoring/src/main/java/org/opendaylight/controller/config/yang/netconf/mdsal/monitoring/MonitoringToMdsalWriter.java new file mode 100644 index 0000000000..50958e423f --- /dev/null +++ b/opendaylight/netconf/mdsal-netconf-monitoring/src/main/java/org/opendaylight/controller/config/yang/netconf/mdsal/monitoring/MonitoringToMdsalWriter.java @@ -0,0 +1,84 @@ +/* + * 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 submit = tx.submit(); + + Futures.addCallback(submit, new FutureCallback() { + @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 submit = tx.submit(); + + Futures.addCallback(submit, new FutureCallback() { + @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); + } +} diff --git a/opendaylight/netconf/mdsal-netconf-monitoring/src/main/java/org/opendaylight/controller/config/yang/netconf/mdsal/monitoring/NetconfMdsalMonitoringMapperModule.java b/opendaylight/netconf/mdsal-netconf-monitoring/src/main/java/org/opendaylight/controller/config/yang/netconf/mdsal/monitoring/NetconfMdsalMonitoringMapperModule.java new file mode 100644 index 0000000000..dadc0f493b --- /dev/null +++ b/opendaylight/netconf/mdsal-netconf-monitoring/src/main/java/org/opendaylight/controller/config/yang/netconf/mdsal/monitoring/NetconfMdsalMonitoringMapperModule.java @@ -0,0 +1,149 @@ +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 CAPABILITIES = Sets.newHashSet(new Capability() { + + @Override + public String getCapabilityUri() { + return MonitoringConstants.URI; + } + + @Override + public Optional getModuleNamespace() { + return Optional.of(MonitoringConstants.NAMESPACE); + } + + @Override + public Optional getModuleName() { + return Optional.of(MonitoringConstants.MODULE_NAME); + } + + @Override + public Optional getRevision() { + return Optional.of(MonitoringConstants.MODULE_REVISION); + } + + @Override + public Optional getCapabilitySchema() { + return Optional.absent(); + } + + @Override + public Collection getLocation() { + return Collections.emptyList(); + } + }); + + private static final AutoCloseable AUTO_CLOSEABLE = new AutoCloseable() { + @Override + public void close() throws Exception { + // NOOP + } + }; + + private final List listeners = new ArrayList<>(); + + public MdSalMonitoringMapperFactory(final NetconfOperationService operationService) { + this.operationService = operationService; + } + + @Override + public NetconfOperationService createService(final String netconfSessionIdForReporting) { + return operationService; + } + + @Override + public Set 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 getNetconfOperations() { + return Collections.singleton(new GetSchema(serverMonitoringDependency)); + } + + @Override + public void close() { + // NOOP + } + } +} diff --git a/opendaylight/netconf/mdsal-netconf-monitoring/src/main/java/org/opendaylight/controller/config/yang/netconf/mdsal/monitoring/NetconfMdsalMonitoringMapperModuleFactory.java b/opendaylight/netconf/mdsal-netconf-monitoring/src/main/java/org/opendaylight/controller/config/yang/netconf/mdsal/monitoring/NetconfMdsalMonitoringMapperModuleFactory.java new file mode 100644 index 0000000000..e0d459318f --- /dev/null +++ b/opendaylight/netconf/mdsal-netconf-monitoring/src/main/java/org/opendaylight/controller/config/yang/netconf/mdsal/monitoring/NetconfMdsalMonitoringMapperModuleFactory.java @@ -0,0 +1,13 @@ +/* +* 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 { + +} diff --git a/opendaylight/netconf/mdsal-netconf-monitoring/src/main/java/org/opendaylight/controller/netconf/monitoring/NetconfMonitoringOperationService.java b/opendaylight/netconf/mdsal-netconf-monitoring/src/main/java/org/opendaylight/controller/netconf/monitoring/NetconfMonitoringOperationService.java new file mode 100644 index 0000000000..9ae4df429c --- /dev/null +++ b/opendaylight/netconf/mdsal-netconf-monitoring/src/main/java/org/opendaylight/controller/netconf/monitoring/NetconfMonitoringOperationService.java @@ -0,0 +1,35 @@ +/* +* 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 getNetconfOperations() { + return Sets.newHashSet(new Get(monitor), new GetSchema(monitor)); + } + + @Override + public void close() { + } + +} diff --git a/opendaylight/netconf/mdsal-netconf-monitoring/src/main/java/org/opendaylight/controller/netconf/monitoring/NetconfMonitoringOperationServiceFactory.java b/opendaylight/netconf/mdsal-netconf-monitoring/src/main/java/org/opendaylight/controller/netconf/monitoring/NetconfMonitoringOperationServiceFactory.java new file mode 100644 index 0000000000..78c23688e4 --- /dev/null +++ b/opendaylight/netconf/mdsal-netconf-monitoring/src/main/java/org/opendaylight/controller/netconf/monitoring/NetconfMonitoringOperationServiceFactory.java @@ -0,0 +1,99 @@ +/* + * 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 CAPABILITIES = Sets.newHashSet(new Capability() { + + @Override + public String getCapabilityUri() { + return MonitoringConstants.URI; + } + + @Override + public Optional getModuleNamespace() { + return Optional.of(MonitoringConstants.NAMESPACE); + } + + @Override + public Optional getModuleName() { + return Optional.of(MonitoringConstants.MODULE_NAME); + } + + @Override + public Optional getRevision() { + return Optional.of(MonitoringConstants.MODULE_REVISION); + } + + @Override + public Optional getCapabilitySchema() { + return Optional.absent(); + } + + @Override + public Collection getLocation() { + return Collections.emptyList(); + } + }); + + private static final AutoCloseable AUTO_CLOSEABLE = new AutoCloseable() { + @Override + public void close() throws Exception { + // NOOP + } + }; + + private final List listeners = new ArrayList<>(); + + public NetconfMonitoringOperationServiceFactory(final NetconfMonitoringOperationService operationService) { + this.operationService = operationService; + } + + @Override + public NetconfOperationService createService(final String netconfSessionIdForReporting) { + return operationService; + } + + @Override + public Set 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()); + } + } +} diff --git a/opendaylight/netconf/mdsal-netconf-monitoring/src/main/yang/netconf-mdsal-monitoring.yang b/opendaylight/netconf/mdsal-netconf-monitoring/src/main/yang/netconf-mdsal-monitoring.yang new file mode 100644 index 0000000000..68a248e48e --- /dev/null +++ b/opendaylight/netconf/mdsal-netconf-monitoring/src/main/yang/netconf-mdsal-monitoring.yang @@ -0,0 +1,60 @@ +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; + } + } + } + } + } + +} diff --git a/opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/Capability.java b/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/Capability.java similarity index 93% rename from opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/Capability.java rename to opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/Capability.java index 408756bf4d..6a061b1ea9 100644 --- a/opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/Capability.java +++ b/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/Capability.java @@ -6,7 +6,7 @@ * 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; diff --git a/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/monitoring/CapabilityListener.java b/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/monitoring/CapabilityListener.java new file mode 100644 index 0000000000..5d9468c8ea --- /dev/null +++ b/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/monitoring/CapabilityListener.java @@ -0,0 +1,19 @@ +/* + * 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 addedCaps); + + void onCapabilitiesRemoved(Set removedCaps); +} diff --git a/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/monitoring/NetconfMonitoringService.java b/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/monitoring/NetconfMonitoringService.java index 51eea9307d..d22412c7cf 100644 --- a/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/monitoring/NetconfMonitoringService.java +++ b/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/monitoring/NetconfMonitoringService.java @@ -7,12 +7,30 @@ */ 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 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); + } } diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/SessionMonitoringService.java b/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/monitoring/SessionListener.java similarity index 59% rename from opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/SessionMonitoringService.java rename to opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/monitoring/SessionListener.java index 7a0b8b7170..a11fb879ca 100644 --- a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/SessionMonitoringService.java +++ b/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/monitoring/SessionListener.java @@ -1,16 +1,17 @@ /* - * 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); diff --git a/opendaylight/netconf/netconf-api/src/main/yang/netconf-northbound.yang b/opendaylight/netconf/netconf-api/src/main/yang/netconf-northbound.yang index f775da91c0..e2b0d35867 100644 --- a/opendaylight/netconf/netconf-api/src/main/yang/netconf-northbound.yang +++ b/opendaylight/netconf/netconf-api/src/main/yang/netconf-northbound.yang @@ -19,4 +19,9 @@ module netconf-northbound { 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 diff --git a/opendaylight/netconf/netconf-artifacts/pom.xml b/opendaylight/netconf/netconf-artifacts/pom.xml index 1248933915..d27ea55812 100644 --- a/opendaylight/netconf/netconf-artifacts/pom.xml +++ b/opendaylight/netconf/netconf-artifacts/pom.xml @@ -87,6 +87,11 @@ netconf-monitoring ${project.version} + + ${project.groupId} + mdsal-netconf-monitoring + ${project.version} + ${project.groupId} netconf-netty-util diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/config/yang/config/netconf/northbound/impl/NetconfMapperAggregatorModule.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/config/yang/config/netconf/northbound/impl/NetconfMapperAggregatorModule.java new file mode 100644 index 0000000000..284c600f0c --- /dev/null +++ b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/config/yang/config/netconf/northbound/impl/NetconfMapperAggregatorModule.java @@ -0,0 +1,22 @@ +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(); + } + +} diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/config/yang/config/netconf/northbound/impl/NetconfMapperAggregatorModuleFactory.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/config/yang/config/netconf/northbound/impl/NetconfMapperAggregatorModuleFactory.java new file mode 100644 index 0000000000..0e415bdfcb --- /dev/null +++ b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/config/yang/config/netconf/northbound/impl/NetconfMapperAggregatorModuleFactory.java @@ -0,0 +1,13 @@ +/* +* 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 { + +} diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/config/yang/config/netconf/northbound/impl/NetconfServerDispatcherModule.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/config/yang/config/netconf/northbound/impl/NetconfServerDispatcherModule.java index e64620d4ad..3c476608a2 100644 --- a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/config/yang/config/netconf/northbound/impl/NetconfServerDispatcherModule.java +++ b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/config/yang/config/netconf/northbound/impl/NetconfServerDispatcherModule.java @@ -1,13 +1,12 @@ 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 { @@ -27,8 +26,8 @@ public class NetconfServerDispatcherModule extends org.opendaylight.controller.c @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( @@ -44,12 +43,8 @@ public class NetconfServerDispatcherModule extends org.opendaylight.controller.c } - 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); } diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/config/yang/config/netconf/northbound/impl/NetconfServerMonitoringModule.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/config/yang/config/netconf/northbound/impl/NetconfServerMonitoringModule.java new file mode 100644 index 0000000000..dc18cd3687 --- /dev/null +++ b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/config/yang/config/netconf/northbound/impl/NetconfServerMonitoringModule.java @@ -0,0 +1,24 @@ +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()); + } + +} diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/config/yang/config/netconf/northbound/impl/NetconfServerMonitoringModuleFactory.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/config/yang/config/netconf/northbound/impl/NetconfServerMonitoringModuleFactory.java new file mode 100644 index 0000000000..fe74486a77 --- /dev/null +++ b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/config/yang/config/netconf/northbound/impl/NetconfServerMonitoringModuleFactory.java @@ -0,0 +1,13 @@ +/* +* 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 { + +} diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/CapabilityProviderImpl.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/CapabilityProviderImpl.java deleted file mode 100644 index 13cc973ba7..0000000000 --- a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/CapabilityProviderImpl.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * 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 capabilityURIs; - - private static final Logger LOG = LoggerFactory.getLogger(CapabilityProviderImpl.class); - - public CapabilityProviderImpl(NetconfOperationServiceSnapshot netconfOperationServiceSnapshot) { - this.netconfOperationServiceSnapshot = netconfOperationServiceSnapshot; - Map urisToCapabilitiesInternalMap = getCapabilitiesInternal(netconfOperationServiceSnapshot); - List capabilityURIs = new ArrayList<>(urisToCapabilitiesInternalMap.keySet()); - Collections.sort(capabilityURIs); - this.capabilityURIs = Collections.unmodifiableSet(new TreeSet<>(capabilityURIs)); - } - - private static Map getCapabilitiesInternal( - NetconfOperationServiceSnapshot netconfOperationServiceSnapshot) { - Map capabilityMap = Maps.newHashMap(); - - for (NetconfOperationService netconfOperationService : netconfOperationServiceSnapshot.getServices()) { - final Set 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 revision) { - - Map> mappedModulesToRevisionToSchema = Maps.newHashMap(); - - for (NetconfOperationService netconfOperationService : netconfOperationServiceSnapshot.getServices()) { - final Set 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 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 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 getCapabilities() { - return capabilityURIs; - } - -} diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSession.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSession.java index 8f2c39df06..0cf2dbc281 100644 --- a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSession.java +++ b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSession.java @@ -55,9 +55,9 @@ public final class NetconfServerSession extends AbstractNetconfSession { 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; @@ -45,6 +45,8 @@ public class NetconfServerSessionListener implements NetconfSessionListener { - - 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); - } -} diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSessionNegotiatorFactory.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSessionNegotiatorFactory.java index 451c066b77..cf489608ca 100644 --- a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSessionNegotiatorFactory.java +++ b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSessionNegotiatorFactory.java @@ -8,8 +8,6 @@ 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; @@ -19,11 +17,13 @@ import io.netty.util.concurrent.Promise; 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; @@ -42,28 +42,28 @@ public class NetconfServerSessionNegotiatorFactory implements SessionNegotiatorF 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 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 baseCapabilities) { + public NetconfServerSessionNegotiatorFactory(final Timer timer, final NetconfOperationServiceFactory netconfOperationProvider, + final SessionIdProvider idProvider, final long connectionTimeoutMillis, + final CommitNotifier commitNot, + final NetconfMonitoringService monitoringService, final Set baseCapabilities) { this.timer = timer; - this.netconfOperationProvider = netconfOperationProvider; + this.aggregatedOpService = netconfOperationProvider; this.idProvider = idProvider; this.connectionTimeoutMillis = connectionTimeoutMillis; this.commitNotificationProducer = commitNot; @@ -73,12 +73,12 @@ public class NetconfServerSessionNegotiatorFactory implements SessionNegotiatorF private ImmutableSet validateBaseCapabilities(final Set baseCapabilities) { // Check base capabilities to be supported by the server - Sets.SetView unknownBaseCaps = Sets.difference(baseCapabilities, DEFAULT_BASE_CAPABILITIES); + final Sets.SetView 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 b = ImmutableSet.builder(); + final ImmutableSet.Builder b = ImmutableSet.builder(); b.addAll(baseCapabilities); // Base 1.0 capability is supported by default b.add(XmlNetconfConstants.URN_IETF_PARAMS_NETCONF_BASE_1_0); @@ -95,32 +95,33 @@ public class NetconfServerSessionNegotiatorFactory implements SessionNegotiatorF * @return session negotiator */ @Override - public SessionNegotiator getSessionNegotiator(SessionListenerFactory defunctSessionListenerFactory, - Channel channel, Promise promise) { - long sessionId = idProvider.getNextSessionId(); - NetconfOperationServiceSnapshot netconfOperationServiceSnapshot = netconfOperationProvider.openSnapshot( - getNetconfSessionIdForReporting(sessionId)); - CapabilityProvider capabilityProvider = new CapabilityProviderImpl(netconfOperationServiceSnapshot); - - NetconfServerSessionPreferences proposal = null; + public SessionNegotiator getSessionNegotiator(final SessionListenerFactory defunctSessionListenerFactory, + final Channel channel, final Promise 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); } } diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/mapping/CapabilityProvider.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/mapping/CapabilityProvider.java deleted file mode 100644 index 60cde27fe5..0000000000 --- a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/mapping/CapabilityProvider.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * 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 revision); - - Set getCapabilities(); - -} diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/mapping/operations/DefaultCommit.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/mapping/operations/DefaultCommit.java index 742255f973..8b2c02bcd4 100644 --- a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/mapping/operations/DefaultCommit.java +++ b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/mapping/operations/DefaultCommit.java @@ -8,18 +8,24 @@ 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; @@ -32,10 +38,10 @@ public class DefaultCommit extends AbstractNetconfOperation { 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; @@ -73,12 +79,22 @@ public class DefaultCommit extends AbstractNetconfOperation { 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 transformCapabilities(final Capabilities capabilities) { + return Sets.newHashSet(Collections2.transform(capabilities.getCapability(), new Function() { + @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"); diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/AggregatedNetconfOperationServiceFactory.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/AggregatedNetconfOperationServiceFactory.java new file mode 100644 index 0000000000..ae68ecc120 --- /dev/null +++ b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/AggregatedNetconfOperationServiceFactory.java @@ -0,0 +1,140 @@ +/* + * 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 factories = new HashSet<>(); + private final Multimap registrations = HashMultimap.create(); + private final Set 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 getCapabilities() { + final HashSet capabilities = Sets.newHashSet(); + for (final NetconfOperationServiceFactory factory : factories) { + capabilities.addAll(factory.getCapabilities()); + } + return capabilities; + } + + @Override + public synchronized AutoCloseable registerCapabilityListener(final CapabilityListener listener) { + final Map 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 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 services; + + public AggregatedNetconfOperation(final Set factories, final String netconfSessionIdForReporting) { + final Builder b = ImmutableSet.builder(); + for (final NetconfOperationServiceFactory factory : factories) { + b.add(factory.createService(netconfSessionIdForReporting)); + } + this.services = b.build(); + } + + @Override + public Set getNetconfOperations() { + final HashSet 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); + } + } + } +} diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfImplActivator.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfImplActivator.java index a55e32a954..1e35597d9a 100644 --- a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfImplActivator.java +++ b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfImplActivator.java @@ -19,7 +19,7 @@ import org.opendaylight.controller.netconf.impl.DefaultCommitNotificationProduce 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; @@ -40,7 +40,7 @@ public class NetconfImplActivator implements BundleActivator { @Override public void start(final BundleContext context) { - NetconfOperationServiceFactoryListenerImpl factoriesListener = new NetconfOperationServiceFactoryListenerImpl(); + AggregatedNetconfOperationServiceFactory factoriesListener = new AggregatedNetconfOperationServiceFactory(); startOperationServiceFactoryTracker(context, factoriesListener); SessionIdProvider idProvider = new SessionIdProvider(); @@ -50,7 +50,7 @@ public class NetconfImplActivator implements BundleActivator { 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); @@ -64,17 +64,14 @@ public class NetconfImplActivator implements BundleActivator { 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 dic = new Hashtable<>(); regMonitoring = context.registerService(NetconfMonitoringService.class, netconfMonitoringServiceImpl, dic); diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfMonitoringServiceImpl.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfMonitoringServiceImpl.java index efbe3ad68f..b02137b748 100644 --- a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfMonitoringServiceImpl.java +++ b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfMonitoringServiceImpl.java @@ -8,25 +8,32 @@ 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; @@ -38,7 +45,8 @@ import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.mon 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 NETCONF_LOCATIONS = ImmutableList.of(NETCONF_LOCATION); private static final Logger LOG = LoggerFactory.getLogger(NetconfMonitoringServiceImpl.class); @@ -48,67 +56,142 @@ public class NetconfMonitoringServiceImpl implements NetconfMonitoringService, S return input.toManagementSession(); } }; + private static final Function CAPABILITY_TO_URI = new Function() { + @Override + public Uri apply(final Capability input) { + return new Uri(input.getCapabilityUri()); + } + }; private final Set sessions = new ConcurrentSet<>(); - private final NetconfOperationProvider netconfOperationProvider; + private final NetconfOperationServiceFactory netconfOperationProvider; + private final Map capabilities = new ConcurrentHashMap<>(); + + private final Set 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 services) { - // FIXME: Capability implementations do not have hashcode/equals! - final Set 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 revision) { + + // FIXME not effective at all + + Map> mappedModulesToRevisionToSchema = Maps.newHashMap(); + + final Collection 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 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 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 caps) { final List 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); @@ -132,10 +215,38 @@ public class NetconfMonitoringServiceImpl implements NetconfMonitoringService, S final Builder 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 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 addedCaps) { + for (final Capability addedCap : addedCaps) { + capabilities.remove(addedCap.getCapabilityUri()); + } + notifyListeners(); + } + + @Override + public synchronized void close() throws Exception { + listeners.clear(); + sessions.clear(); + capabilities.clear(); + } } diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfOperationRouterImpl.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfOperationRouterImpl.java index c8fa341747..9d58bd911c 100644 --- a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfOperationRouterImpl.java +++ b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfOperationRouterImpl.java @@ -17,12 +17,11 @@ import java.util.NavigableMap; 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; @@ -30,7 +29,6 @@ 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.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; @@ -40,29 +38,20 @@ import org.w3c.dom.Document; 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 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 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); } @@ -154,8 +143,8 @@ public class NetconfOperationRouterImpl implements NetconfOperationRouter { 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); } } diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfOperationServiceFactoryListenerImpl.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfOperationServiceFactoryListenerImpl.java deleted file mode 100644 index 6c55c35e25..0000000000 --- a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfOperationServiceFactoryListenerImpl.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * 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 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); - } - -} diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfOperationServiceFactoryTracker.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfOperationServiceFactoryTracker.java index 9a077a6130..d97ac90922 100644 --- a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfOperationServiceFactoryTracker.java +++ b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfOperationServiceFactoryTracker.java @@ -9,6 +9,7 @@ package org.opendaylight.controller.netconf.impl.osgi; 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; @@ -40,8 +41,9 @@ class NetconfOperationServiceFactoryTracker extends @Override public void removedService(ServiceReference reference, NetconfOperationServiceFactory netconfOperationServiceFactory) { - if (netconfOperationServiceFactory != null) + if (netconfOperationServiceFactory != null) { factoriesListener.onRemoveNetconfOperationServiceFactory(netconfOperationServiceFactory); + } } } diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfOperationServiceSnapshotImpl.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfOperationServiceSnapshotImpl.java deleted file mode 100644 index 26abdd974d..0000000000 --- a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfOperationServiceSnapshotImpl.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * 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 services; - private final String netconfSessionIdForReporting; - - public NetconfOperationServiceSnapshotImpl(final Set factories, final String sessionIdForReporting) { - final Builder 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 getServices() { - return services; - } - - @Override - public void close() throws Exception { - CloseableUtil.closeAll(services); - } - - @Override - public String toString() { - return "NetconfOperationServiceSnapshotImpl{" + netconfSessionIdForReporting + '}'; - } -} diff --git a/opendaylight/netconf/netconf-impl/src/main/yang/netconf-northbound-impl.yang b/opendaylight/netconf/netconf-impl/src/main/yang/netconf-northbound-impl.yang index 6ca0a7781c..7ad1fef55d 100644 --- a/opendaylight/netconf/netconf-impl/src/main/yang/netconf-northbound-impl.yang +++ b/opendaylight/netconf/netconf-impl/src/main/yang/netconf-northbound-impl.yang @@ -67,6 +67,15 @@ module netconf-northbound-impl { } } + 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 { @@ -77,4 +86,43 @@ module netconf-northbound-impl { } } + + 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 diff --git a/opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/ConcurrentClientsTest.java b/opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/ConcurrentClientsTest.java index 8e8a73b914..512a127d22 100644 --- a/opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/ConcurrentClientsTest.java +++ b/opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/ConcurrentClientsTest.java @@ -12,7 +12,9 @@ import static com.google.common.base.Preconditions.checkNotNull; 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; @@ -48,8 +50,11 @@ import org.junit.BeforeClass; 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; @@ -57,9 +62,7 @@ import org.opendaylight.controller.netconf.client.SimpleNetconfClientSessionList 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; @@ -71,6 +74,8 @@ import org.opendaylight.controller.netconf.util.messages.NetconfMessageUtil; 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; @@ -115,10 +120,19 @@ public class ConcurrentClientsTest { 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.emptyList()).build()).when(monitoring).getCapabilities(); return monitoring; } @@ -143,7 +157,7 @@ public class ConcurrentClientsTest { 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)); @@ -259,13 +273,22 @@ public class ConcurrentClientsTest { this.operations = operations; } + @Override + public Set 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 getCapabilities() { - return Collections.emptySet(); - } @Override public Set getNetconfOperations() { diff --git a/opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/NetconfDispatcherImplTest.java b/opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/NetconfDispatcherImplTest.java index df4069ac17..4fd04e29ef 100644 --- a/opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/NetconfDispatcherImplTest.java +++ b/opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/NetconfDispatcherImplTest.java @@ -17,7 +17,7 @@ import java.net.InetSocketAddress; 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 { @@ -33,7 +33,7 @@ public class NetconfDispatcherImplTest { commitNot = new DefaultCommitNotificationProducer( ManagementFactory.getPlatformMBeanServer()); - NetconfOperationServiceFactoryListenerImpl factoriesListener = new NetconfOperationServiceFactoryListenerImpl(); + AggregatedNetconfOperationServiceFactory factoriesListener = new AggregatedNetconfOperationServiceFactory(); SessionIdProvider idProvider = new SessionIdProvider(); hashedWheelTimer = new HashedWheelTimer(); diff --git a/opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/NetconfMonitoringServiceImplTest.java b/opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/NetconfMonitoringServiceImplTest.java index 93caa09286..fed4742dd8 100644 --- a/opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/NetconfMonitoringServiceImplTest.java +++ b/opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/NetconfMonitoringServiceImplTest.java @@ -8,107 +8,7 @@ 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 caps = Sets.newHashSet(cap); - Set services = Sets.newHashSet(operationService); - doReturn(snapshot).when(operationProvider).openSnapshot(anyString()); - doReturn(services).when(snapshot).getServices(); - doReturn(caps).when(operationService).getCapabilities(); - Optional 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 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 } diff --git a/opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/mapping/operations/DefaultCommitTest.java b/opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/mapping/operations/DefaultCommitTest.java index 15aeb8d27c..0c7406543f 100644 --- a/opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/mapping/operations/DefaultCommitTest.java +++ b/opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/mapping/operations/DefaultCommitTest.java @@ -18,17 +18,19 @@ import static org.mockito.Mockito.never; 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; @@ -38,7 +40,7 @@ public class DefaultCommitTest { private Document requestMessage; private NetconfOperationRouter router; private DefaultCommitNotificationProducer notifier; - private CapabilityProvider cap; + private NetconfMonitoringService cap; private DefaultCommit commit; @Before @@ -49,8 +51,8 @@ public class DefaultCommitTest { 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.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); diff --git a/opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/osgi/NetconfImplActivatorTest.java b/opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/osgi/NetconfImplActivatorTest.java index 413c9cc945..2fb989507c 100644 --- a/opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/osgi/NetconfImplActivatorTest.java +++ b/opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/osgi/NetconfImplActivatorTest.java @@ -12,7 +12,6 @@ import static org.mockito.Matchers.any; 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; @@ -44,11 +43,11 @@ public class NetconfImplActivatorTest { 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)); } @@ -57,7 +56,7 @@ public class NetconfImplActivatorTest { 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); } } diff --git a/opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/osgi/NetconfOperationServiceFactoryTrackerTest.java b/opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/osgi/NetconfOperationServiceFactoryTrackerTest.java index 8cb569eaa2..c75cdcd27a 100644 --- a/opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/osgi/NetconfOperationServiceFactoryTrackerTest.java +++ b/opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/osgi/NetconfOperationServiceFactoryTrackerTest.java @@ -22,6 +22,7 @@ import org.mockito.Mock; 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; diff --git a/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/AbstractNetconfConfigTest.java b/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/AbstractNetconfConfigTest.java index b70e97bac9..c2812dbf61 100644 --- a/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/AbstractNetconfConfigTest.java +++ b/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/AbstractNetconfConfigTest.java @@ -13,7 +13,6 @@ import static org.mockito.Matchers.any; 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; @@ -47,6 +46,7 @@ import org.opendaylight.controller.config.yang.test.impl.MultipleDependenciesMod 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; @@ -57,13 +57,11 @@ import org.opendaylight.controller.netconf.impl.DefaultCommitNotificationProduce 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; @@ -108,14 +106,16 @@ public abstract class AbstractNetconfConfigTest extends AbstractConfigTest { 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()); } @@ -137,8 +137,8 @@ public abstract class AbstractNetconfConfigTest extends AbstractConfigTest { 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) { @@ -157,16 +157,12 @@ public abstract class AbstractNetconfConfigTest extends AbstractConfigTest { return notificationProducer; } - protected Iterable getAdditionalServiceFactories() { + protected Iterable 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.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(); @@ -206,7 +202,7 @@ public abstract class AbstractNetconfConfigTest extends AbstractConfigTest { } protected NetconfServerDispatcherImpl createDispatcher( - final NetconfOperationServiceFactoryListenerImpl factoriesListener, final SessionMonitoringService sessionMonitoringService, + final AggregatedNetconfOperationServiceFactory factoriesListener, final NetconfMonitoringService sessionMonitoringService, final DefaultCommitNotificationProducer commitNotifier) { final SessionIdProvider idProvider = new SessionIdProvider(); diff --git a/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfConfigPersisterITTest.java b/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfConfigPersisterITTest.java index 92c96d92f2..c421a46fdf 100644 --- a/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfConfigPersisterITTest.java +++ b/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfConfigPersisterITTest.java @@ -9,23 +9,18 @@ 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.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; @@ -38,15 +33,6 @@ import org.opendaylight.controller.netconf.api.NetconfMessage; 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; @@ -58,29 +44,12 @@ public class NetconfConfigPersisterITTest extends AbstractNetconfConfigTest { 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 getAdditionalServiceFactories() { - return Collections.singletonList(new NetconfMonitoringActivator.NetconfMonitoringOperationServiceFactory( - new NetconfMonitoringOperationService(netconfMonitoringService))); - } - @Override protected DefaultCommitNotificationProducer getNotificationProducer() { return new DefaultCommitNotificationProducer(ManagementFactory.getPlatformMBeanServer()); @@ -95,7 +64,6 @@ public class NetconfConfigPersisterITTest extends AbstractNetconfConfigTest { 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"); @@ -112,8 +80,8 @@ public class NetconfConfigPersisterITTest extends AbstractNetconfConfigTest { } 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 @@ -143,20 +111,6 @@ public class NetconfConfigPersisterITTest extends AbstractNetconfConfigTest { 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 caps = Sets.newHashSet(); - doReturn(caps).when(service).getCapabilities(); - final Set 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 notifications = Lists.newArrayList(); diff --git a/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITMonitoringTest.java b/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITMonitoringTest.java index ea94fd3f8b..e745b393fb 100644 --- a/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITMonitoringTest.java +++ b/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITMonitoringTest.java @@ -8,6 +8,7 @@ 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; @@ -26,21 +27,14 @@ import java.util.Collections; 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 { @@ -49,45 +43,11 @@ 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 getAdditionalServiceFactories() { - return Collections.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))) { @@ -151,23 +111,24 @@ public class NetconfITMonitoringTest extends AbstractNetconfConfigTest { 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 caps = Sets.newHashSet(); caps.add(TESTING_CAPABILITY); - doReturn(caps).when(service).getCapabilities(); - final Set 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; } diff --git a/opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/NetconfOperationProvider.java b/opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/NetconfOperationProvider.java deleted file mode 100644 index f5c50f8167..0000000000 --- a/opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/NetconfOperationProvider.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * 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; - } - - } - -} diff --git a/opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/NetconfOperationService.java b/opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/NetconfOperationService.java index 5e2cba2cba..1a1a124895 100644 --- a/opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/NetconfOperationService.java +++ b/opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/NetconfOperationService.java @@ -15,11 +15,6 @@ import java.util.Set; */ public interface NetconfOperationService extends AutoCloseable { - /** - * Get capabilities announced by server hello message. - */ - Set getCapabilities(); - /** * Get set of netconf operations that are handled by this service. */ diff --git a/opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/NetconfOperationServiceFactory.java b/opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/NetconfOperationServiceFactory.java index 81401f26ee..8caa177bfa 100644 --- a/opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/NetconfOperationServiceFactory.java +++ b/opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/NetconfOperationServiceFactory.java @@ -8,6 +8,10 @@ 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 @@ -15,6 +19,16 @@ package org.opendaylight.controller.netconf.mapping.api; */ public interface NetconfOperationServiceFactory { + /** + * Get capabilities supported by current operation service. + */ + Set 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); } diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfOperationServiceFactoryListener.java b/opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/NetconfOperationServiceFactoryListener.java similarity index 79% rename from opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfOperationServiceFactoryListener.java rename to opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/NetconfOperationServiceFactoryListener.java index 8e1052cfeb..30a4f9bf82 100644 --- a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfOperationServiceFactoryListener.java +++ b/opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/NetconfOperationServiceFactoryListener.java @@ -6,9 +6,7 @@ * 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 { diff --git a/opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/NetconfOperationServiceSnapshot.java b/opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/NetconfOperationServiceSnapshot.java deleted file mode 100644 index eaa69379f7..0000000000 --- a/opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/NetconfOperationServiceSnapshot.java +++ /dev/null @@ -1,18 +0,0 @@ -/* - * 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 getServices(); - -} diff --git a/opendaylight/netconf/netconf-mapping-api/src/main/yang/netconf-northbound-mapper.yang b/opendaylight/netconf/netconf-mapping-api/src/main/yang/netconf-northbound-mapper.yang index a709665c63..3ffecde700 100644 --- a/opendaylight/netconf/netconf-mapping-api/src/main/yang/netconf-northbound-mapper.yang +++ b/opendaylight/netconf/netconf-mapping-api/src/main/yang/netconf-northbound-mapper.yang @@ -19,4 +19,9 @@ module netconf-northbound-mapper { 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 diff --git a/opendaylight/netconf/netconf-mdsal-config/src/main/resources/initial/08-netconf-mdsal.xml b/opendaylight/netconf/netconf-mdsal-config/src/main/resources/initial/08-netconf-mdsal.xml index 042447b1e6..4ca3c99e81 100644 --- a/opendaylight/netconf/netconf-mdsal-config/src/main/resources/initial/08-netconf-mdsal.xml +++ b/opendaylight/netconf/netconf-mdsal-config/src/main/resources/initial/08-netconf-mdsal.xml @@ -25,6 +25,10 @@ dom:dom-async-data-broker inmemory-data-broker + + prefix:netconf-mapper-registry + mapper-aggregator-registry + @@ -32,8 +36,12 @@ netconf-mdsal-server-dispatcher dom:netconf-northbound-mapper - netconf-mdsal-mapper + mapper-aggregator + + prefix:netconf-server-monitoring + server-monitor + prefix:netty-threadgroup global-boss-group @@ -48,6 +56,37 @@ + + prefix:netconf-mdsal-monitoring-mapper + netconf-mdsal-monitoring-mapper + + prefix:netconf-server-monitoring + server-monitor + + + prefix:binding-broker-osgi-registry + binding-osgi-broker + + + prefix:netconf-mapper-registry + mapper-aggregator-registry + + + + + prefix:netconf-mapper-aggregator + mapper-aggregator + + + + prefix:netconf-server-monitoring-impl + server-monitor + + dom:netconf-northbound-mapper + mapper-aggregator + + + prefix:netconf-northbound-ssh netconf-mdsal-ssh-server @@ -76,6 +115,13 @@ + + prefix:netconf-server-monitoring + + server-monitor + /modules/module[type='netconf-server-monitoring-impl'][name='server-monitor'] + + prefix:netconf-northbound-mapper @@ -83,6 +129,20 @@ /modules/module[type='netconf-mdsal-mapper'][name='netconf-mdsal-mapper'] + + prefix:netconf-northbound-mapper + + mapper-aggregator + /modules/module[type='netconf-mapper-aggregator'][name='mapper-aggregator'] + + + + prefix:netconf-mapper-registry + + mapper-aggregator-registry + /modules/module[type='netconf-mapper-aggregator'][name='mapper-aggregator'] + + prefix:netconf-server-dispatcher @@ -96,6 +156,7 @@ urn:opendaylight:params:xml:ns:yang:controller:netconf:mdsal:mapper?module=netconf-mdsal-mapper&revision=2015-01-14 + urn:opendaylight:params:xml:ns:yang:controller:netconf:mdsal:monitoring?module=netconf-mdsal-monitoring&revision=2015-02-18 urn:opendaylight:params:xml:ns:yang:controller:netconf:northbound:ssh?module=netconf-northbound-ssh&revision=2015-01-14 urn:opendaylight:params:xml:ns:yang:controller:config:netconf:northbound:impl?module=netconf-northbound-impl&revision=2015-01-12 urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl:scheduled?module=threadpool-impl-scheduled&revision=2013-12-01 diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/mapping/operations/DefaultGetSchema.java b/opendaylight/netconf/netconf-monitoring/src/main/java/org/opendaylight/controller/netconf/monitoring/GetSchema.java similarity index 75% rename from opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/mapping/operations/DefaultGetSchema.java rename to opendaylight/netconf/netconf-monitoring/src/main/java/org/opendaylight/controller/netconf/monitoring/GetSchema.java index 5310704876..d02cb432cb 100644 --- a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/mapping/operations/DefaultGetSchema.java +++ b/opendaylight/netconf/netconf-monitoring/src/main/java/org/opendaylight/controller/netconf/monitoring/GetSchema.java @@ -1,19 +1,19 @@ /* - * 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; @@ -23,16 +23,16 @@ import org.slf4j.LoggerFactory; 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; } @@ -47,16 +47,16 @@ public final class DefaultGetSchema extends AbstractLastNetconfOperation { } @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 errorInfo = Maps.newHashMap(); + } catch (final IllegalStateException e) { + final Map 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, @@ -64,7 +64,7 @@ public final class DefaultGetSchema extends AbstractLastNetconfOperation { 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); @@ -76,19 +76,19 @@ public final class DefaultGetSchema extends AbstractLastNetconfOperation { private final String identifier; private final Optional 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 versionElement = getSchemaElement + final Optional versionElement = getSchemaElement .getOnlyChildElementWithSameNamespaceOptionally(VERSION); if (versionElement.isPresent()) { version = Optional.of(versionElement.get().getTextContent()); diff --git a/opendaylight/netconf/netconf-monitoring/src/main/java/org/opendaylight/controller/netconf/monitoring/osgi/NetconfMonitoringActivator.java b/opendaylight/netconf/netconf-monitoring/src/main/java/org/opendaylight/controller/netconf/monitoring/osgi/NetconfMonitoringActivator.java index 1411350cd3..1f094f1caf 100644 --- a/opendaylight/netconf/netconf-monitoring/src/main/java/org/opendaylight/controller/netconf/monitoring/osgi/NetconfMonitoringActivator.java +++ b/opendaylight/netconf/netconf-monitoring/src/main/java/org/opendaylight/controller/netconf/monitoring/osgi/NetconfMonitoringActivator.java @@ -7,6 +7,10 @@ */ 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; @@ -31,22 +35,43 @@ public class NetconfMonitoringActivator implements 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 getCapabilities() { + return Collections.emptySet(); + } + + @Override + public AutoCloseable registerCapabilityListener(final CapabilityListener listener) { + return AUTO_CLOSEABLE; + } + + @Override + public void close() {} } } diff --git a/opendaylight/netconf/netconf-monitoring/src/main/java/org/opendaylight/controller/netconf/monitoring/osgi/NetconfMonitoringOperationService.java b/opendaylight/netconf/netconf-monitoring/src/main/java/org/opendaylight/controller/netconf/monitoring/osgi/NetconfMonitoringOperationService.java index a17e139131..efe71e9127 100644 --- a/opendaylight/netconf/netconf-monitoring/src/main/java/org/opendaylight/controller/netconf/monitoring/osgi/NetconfMonitoringOperationService.java +++ b/opendaylight/netconf/netconf-monitoring/src/main/java/org/opendaylight/controller/netconf/monitoring/osgi/NetconfMonitoringOperationService.java @@ -7,67 +7,25 @@ */ 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 CAPABILITIES = Sets.newHashSet(new Capability() { - - @Override - public String getCapabilityUri() { - return MonitoringConstants.URI; - } - - @Override - public Optional getModuleNamespace() { - return Optional.of(MonitoringConstants.NAMESPACE); - } - - @Override - public Optional getModuleName() { - return Optional.of(MonitoringConstants.MODULE_NAME); - } - - @Override - public Optional getRevision() { - return Optional.of(MonitoringConstants.MODULE_REVISION); - } - - @Override - public Optional getCapabilitySchema() { - return Optional.absent(); - } - - @Override - public Collection getLocation() { - return Collections.emptyList(); - } - }); - private final NetconfMonitoringService monitor; public NetconfMonitoringOperationService(final NetconfMonitoringService monitor) { this.monitor = monitor; } - @Override - public Set getCapabilities() { - return CAPABILITIES; - } - @Override public Set getNetconfOperations() { - return Sets.newHashSet(new Get(monitor)); + return Sets.newHashSet(new Get(monitor), new GetSchema(monitor)); } @Override diff --git a/opendaylight/netconf/netconf-monitoring/src/main/java/org/opendaylight/controller/netconf/monitoring/osgi/NetconfMonitoringServiceTracker.java b/opendaylight/netconf/netconf-monitoring/src/main/java/org/opendaylight/controller/netconf/monitoring/osgi/NetconfMonitoringServiceTracker.java index 9e7c105aa4..5d0a2a91ad 100644 --- a/opendaylight/netconf/netconf-monitoring/src/main/java/org/opendaylight/controller/netconf/monitoring/osgi/NetconfMonitoringServiceTracker.java +++ b/opendaylight/netconf/netconf-monitoring/src/main/java/org/opendaylight/controller/netconf/monitoring/osgi/NetconfMonitoringServiceTracker.java @@ -25,6 +25,7 @@ public class NetconfMonitoringServiceTracker extends ServiceTracker reg; + private NetconfMonitoringActivator.NetconfMonitoringOperationServiceFactory factory; NetconfMonitoringServiceTracker(final BundleContext context) { super(context, NetconfMonitoringService.class, null); @@ -38,7 +39,7 @@ public class NetconfMonitoringServiceTracker extends ServiceTracker properties = new Hashtable<>(); @@ -58,6 +59,9 @@ public class NetconfMonitoringServiceTracker extends ServiceTracker\n" + " threadpool-api\n" + @@ -45,15 +46,16 @@ public class DefaultGetSchemaTest { @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 diff --git a/opendaylight/netconf/netconf-monitoring/src/test/java/org/opendaylight/controller/netconf/monitoring/osgi/NetconfMonitoringOperationServiceTest.java b/opendaylight/netconf/netconf-monitoring/src/test/java/org/opendaylight/controller/netconf/monitoring/osgi/NetconfMonitoringOperationServiceTest.java index 7873183188..67a54366cd 100644 --- a/opendaylight/netconf/netconf-monitoring/src/test/java/org/opendaylight/controller/netconf/monitoring/osgi/NetconfMonitoringOperationServiceTest.java +++ b/opendaylight/netconf/netconf-monitoring/src/test/java/org/opendaylight/controller/netconf/monitoring/osgi/NetconfMonitoringOperationServiceTest.java @@ -11,25 +11,17 @@ package org.opendaylight.controller.netconf.monitoring.osgi; 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.absent(), service.getCapabilities().iterator().next().getCapabilitySchema()); - assertEquals(Collections.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()); } } diff --git a/opendaylight/netconf/netconf-monitoring/src/test/java/org/opendaylight/controller/netconf/monitoring/xml/JaxBSerializerTest.java b/opendaylight/netconf/netconf-monitoring/src/test/java/org/opendaylight/controller/netconf/monitoring/xml/JaxBSerializerTest.java index 4b5dcd7d55..a8236b2ebc 100644 --- a/opendaylight/netconf/netconf-monitoring/src/test/java/org/opendaylight/controller/netconf/monitoring/xml/JaxBSerializerTest.java +++ b/opendaylight/netconf/netconf-monitoring/src/test/java/org/opendaylight/controller/netconf/monitoring/xml/JaxBSerializerTest.java @@ -11,9 +11,13 @@ import static org.hamcrest.MatcherAssert.assertThat; 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; @@ -25,6 +29,7 @@ import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.mon 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; @@ -42,6 +47,26 @@ public class JaxBSerializerTest { 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 addedCaps) { + + } + + @Override + public void onCapabilitiesRemoved(final Set addedCaps) { + + } + @Override public Sessions getSessions() { return new SessionsBuilder().setSession(Lists.newArrayList(getMockSession(NetconfTcp.class), getMockSession(NetconfSsh.class))).build(); @@ -51,6 +76,26 @@ public class JaxBSerializerTest { 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 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", ""); diff --git a/opendaylight/netconf/netconf-notifications-impl/src/main/java/org/opendaylight/controller/netconf/notifications/impl/osgi/Activator.java b/opendaylight/netconf/netconf-notifications-impl/src/main/java/org/opendaylight/controller/netconf/notifications/impl/osgi/Activator.java index 9eab5caff7..a0a0dfcb8f 100644 --- a/opendaylight/netconf/netconf-notifications-impl/src/main/java/org/opendaylight/controller/netconf/notifications/impl/osgi/Activator.java +++ b/opendaylight/netconf/netconf-notifications-impl/src/main/java/org/opendaylight/controller/netconf/notifications/impl/osgi/Activator.java @@ -15,8 +15,9 @@ import java.util.Collections; 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; @@ -42,16 +43,30 @@ public class Activator implements BundleActivator { final NetconfOperationServiceFactory netconfOperationServiceFactory = new NetconfOperationServiceFactory() { + private final Set capabilities = Collections.singleton(new NotificationsCapability()); + + @Override + public Set 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 getCapabilities() { - return Collections.singleton(new NotificationsCapability()); - } @Override public Set getNetconfOperations() { @@ -68,7 +83,7 @@ public class Activator implements BundleActivator { } }; - Dictionary properties = new Hashtable<>(); + final Dictionary properties = new Hashtable<>(); properties.put(NetconfConstants.SERVICE_NAME, NetconfConstants.NETCONF_MONITORING); operationaServiceRegistration = context.registerService(NetconfOperationServiceFactory.class, netconfOperationServiceFactory, properties); diff --git a/opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/FakeModuleBuilderCapability.java b/opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/FakeModuleBuilderCapability.java index fcb513f016..3aba6f81a3 100644 --- a/opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/FakeModuleBuilderCapability.java +++ b/opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/FakeModuleBuilderCapability.java @@ -12,8 +12,8 @@ import com.google.common.base.Optional; 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; diff --git a/opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/ModuleBuilderCapability.java b/opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/ModuleBuilderCapability.java index 68f8796d81..11af568321 100644 --- a/opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/ModuleBuilderCapability.java +++ b/opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/ModuleBuilderCapability.java @@ -12,8 +12,8 @@ import com.google.common.base.Optional; 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; diff --git a/opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/NetconfDeviceSimulator.java b/opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/NetconfDeviceSimulator.java index 30b3076589..bb67af2032 100644 --- a/opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/NetconfDeviceSimulator.java +++ b/opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/NetconfDeviceSimulator.java @@ -55,19 +55,20 @@ import org.apache.sshd.common.util.ThreadUtils; 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; @@ -141,9 +142,15 @@ public class NetconfDeviceSimulator implements Closeable { 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()); @@ -152,7 +159,7 @@ public class NetconfDeviceSimulator implements Closeable { : 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); @@ -396,65 +403,43 @@ public class NetconfDeviceSimulator implements Closeable { // close Everything } - private static class SimulatedOperationProvider implements NetconfOperationProvider { - private final SessionIdProvider idProvider; - private final Set netconfOperationServices; + private static class SimulatedOperationProvider implements NetconfOperationServiceFactory { + private final Set caps; + private final SimulatedOperationService simulatedOperationService; public SimulatedOperationProvider(final SessionIdProvider idProvider, final Set caps, final Optional notificationsFile) { - this.idProvider = idProvider; - final SimulatedOperationService simulatedOperationService = new SimulatedOperationService(caps, idProvider.getCurrentSessionId(), notificationsFile); - this.netconfOperationServices = Sets.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 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 netconfOperationServices; - - public SimulatedServiceSnapshot(final SessionIdProvider idProvider, final Set netconfOperationServices) { - this.idProvider = idProvider; - this.netconfOperationServices = netconfOperationServices; - } - - @Override - public String getNetconfSessionIdForReporting() { - return String.valueOf(idProvider.getCurrentSessionId()); - } - - @Override - public Set 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 capabilities; private final long currentSessionId; private final Optional notificationsFile; - public SimulatedOperationService(final Set capabilities, final long currentSessionId, final Optional notificationsFile) { - this.capabilities = capabilities; + public SimulatedOperationService(final long currentSessionId, final Optional notificationsFile) { this.currentSessionId = currentSessionId; this.notificationsFile = notificationsFile; } - @Override - public Set getCapabilities() { - return capabilities; - } - @Override public Set getNetconfOperations() { final DataList storage = new DataList(); @@ -475,16 +460,4 @@ public class NetconfDeviceSimulator implements Closeable { } } - 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); - } - } - } diff --git a/opendaylight/netconf/pom.xml b/opendaylight/netconf/pom.xml index 4566845e4f..a990b5c6cb 100644 --- a/opendaylight/netconf/pom.xml +++ b/opendaylight/netconf/pom.xml @@ -21,6 +21,7 @@ netconf-impl config-netconf-connector mdsal-netconf-connector + mdsal-netconf-monitoring netconf-util netconf-netty-util config-persister-impl