From: Ed Warnicke Date: Thu, 4 Sep 2014 22:53:39 +0000 (+0000) Subject: Merge "BUG 1595 - Clustering : NPE on startup" X-Git-Tag: release/helium~139 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=commitdiff_plain;h=e71922c94cec22e9f37648a2d04bf2eb3274cf2f;hp=56faf3cea4400b35fde6686c4ab4a3bdb0da8fdc;p=controller.git Merge "BUG 1595 - Clustering : NPE on startup" --- diff --git a/features/akka/pom.xml b/features/akka/pom.xml new file mode 100644 index 0000000000..f1f3017c20 --- /dev/null +++ b/features/akka/pom.xml @@ -0,0 +1,264 @@ + + + 4.0.0 + + org.opendaylight.controller + commons.opendaylight + 1.4.2-SNAPSHOT + ../../opendaylight/commons/opendaylight + + features-akka + org.opendaylight.controller + jar + + features.xml + + 1.0.0-SNAPSHOT + 1.4.2-SNAPSHOT + 3.0.1 + 0.6.2-SNAPSHOT + 1.4.2-SNAPSHOT + 2.16 + + + + + + + + + org.scala-lang + scala-library + ${scala.version}.${scala.micro.version} + + + org.scala-lang + scala-reflect + ${scala.version}.${scala.micro.version} + + + com.typesafe + config + ${typesafe.config.version} + + + com.typesafe.akka + akka-actor_${scala.version} + ${akka.version} + + + com.typesafe.akka + akka-slf4j_${scala.version} + ${akka.version} + + + com.typesafe.akka + akka-osgi_${scala.version} + ${akka.version} + + + org.uncommons.maths + uncommons-maths + ${uncommons.maths.version} + + + jfree + jcommon + + + jfree + jfreechart + + + + + com.google.protobuf + protobuf-java + ${protobuf.version} + + + io.netty + netty + 3.8.0.Final + + + com.typesafe.akka + akka-remote_${scala.version} + ${akka.version} + + + com.typesafe.akka + akka-cluster_${scala.version} + ${akka.version} + + + org.iq80.leveldb + leveldb + ${leveldb.version} + + + org.fusesource.leveldbjni + leveldbjni-all + ${leveldbjni.version} + + + + + org.opendaylight.yangtools + features-test + ${feature.test.version} + test + + + + org.opendaylight.controller + opendaylight-karaf-empty + ${karaf.empty.version} + zip + + + + + + + + src/main/resources + true + + + + + org.apache.maven.plugins + maven-resources-plugin + + + filter + generate-resources + + resources + + + + + + org.codehaus.mojo + build-helper-maven-plugin + + + attach-artifacts + package + + attach-artifact + + + + + ${project.build.directory}/classes/${features.file} + xml + features + + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + ${surefire.version} + + + org.opendaylight.controller + opendaylight-karaf-empty + ${karaf.empty.version} + + + org.opendaylight.yangtools:features-test + + + + + + + scm:git:ssh://git.opendaylight.org:29418/controller.git + scm:git:ssh://git.opendaylight.org:29418/controller.git + HEAD + https://git.opendaylight.org/gerrit/gitweb?p=controller.git;a=summary + + diff --git a/features/akka/src/main/resources/features.xml b/features/akka/src/main/resources/features.xml new file mode 100644 index 0000000000..182ff766e6 --- /dev/null +++ b/features/akka/src/main/resources/features.xml @@ -0,0 +1,117 @@ + + + + + + + + + odl-akka-scala + odl-akka-system + odl-akka-clustering + odl-akka-leveldb + odl-akka-persistence + + + + mvn:org.scala-lang/scala-library/${scala.version}.${scala.micro.version} + mvn:org.scala-lang/scala-reflect/${scala.version}.${scala.micro.version} + + + odl-akka-scala + mvn:com.typesafe/config/${typesafe.config.version} + mvn:com.typesafe.akka/akka-actor_${scala.version}/${akka.version} + mvn:com.typesafe.akka/akka-slf4j_${scala.version}/${akka.version} + mvn:com.typesafe.akka/akka-osgi_${scala.version}/${akka.version} + + + odl-akka-system + wrap:mvn:org.uncommons.maths/uncommons-maths/${uncommons.maths.version} + mvn:com.google.protobuf/protobuf-java/${protobuf.version} + mvn:io.netty/netty/3.8.0.Final + mvn:com.typesafe.akka/akka-remote_${scala.version}/${akka.version} + mvn:com.typesafe.akka/akka-cluster_${scala.version}/${akka.version} + + + wrap:mvn:org.iq80.leveldb/leveldb/${leveldb.version} + mvn:org.fusesource.leveldbjni/leveldbjni-all/${leveldbjni.version} + + + odl-akka-leveldb + odl-akka-system + mvn:com.typesafe.akka/akka-persistence-experimental_${scala.version}/${akka.version} + wrap:mvn:com.google.protobuf/protobuf-java/${protobuf.version}$overwrite=merge&DynamicImport-Package=org.opendaylight.controller.protobuff.messages.*;org.opendaylight.controller.cluster.raft.protobuff.client.messages.* + + + + diff --git a/features/base/pom.xml b/features/base/pom.xml index ed8e2a8c20..8fec90fd9d 100644 --- a/features/base/pom.xml +++ b/features/base/pom.xml @@ -258,47 +258,38 @@ orbit org.apache.catalina - 7.0.53.v201406061610 orbit org.apache.catalina.ha - 7.0.53.v201406070630 orbit org.apache.catalina.tribes - 7.0.53.v201406070630 orbit org.apache.coyote - 7.0.53.v201406070630 orbit org.apache.el - 7.0.53.v201406060720 orbit org.apache.jasper - 7.0.53.v201406070630 orbit org.apache.juli.extras - 7.0.53.v201406060720 orbit org.apache.tomcat.api - 7.0.53.v201406060720 orbit org.apache.tomcat.util - 7.0.53.v201406070630 org.aopalliance diff --git a/features/base/src/main/resources/features.xml b/features/base/src/main/resources/features.xml index 999cf704d2..e4c455ccca 100644 --- a/features/base/src/main/resources/features.xml +++ b/features/base/src/main/resources/features.xml @@ -114,16 +114,16 @@ odl-base-gemini-web odl-base-eclipselink-persistence - mvn:orbit/org.apache.catalina/${commons.karaf.catalina} + mvn:orbit/org.apache.catalina/${commons.catalina} mvn:geminiweb/org.eclipse.gemini.web.tomcat/${geminiweb.version} - mvn:orbit/org.apache.catalina.ha/${commons.karaf.catalina.ha} - mvn:orbit/org.apache.catalina.tribes/${commons.karaf.catalina.tribes} - mvn:orbit/org.apache.coyote/${commons.karaf.coyote} - mvn:orbit/org.apache.el/${commons.karaf.el} - mvn:orbit/org.apache.jasper/${commons.karaf.jasper} - mvn:orbit/org.apache.juli.extras/${commons.karaf.juli.version} - mvn:orbit/org.apache.tomcat.api/${commons.karaf.tomcat.api} - mvn:orbit/org.apache.tomcat.util/${commons.karaf.tomcat.util} + mvn:orbit/org.apache.catalina.ha/${commons.catalina.ha} + mvn:orbit/org.apache.catalina.tribes/${commons.catalina.tribes} + mvn:orbit/org.apache.coyote/${commons.coyote} + mvn:orbit/org.apache.el/${commons.el} + mvn:orbit/org.apache.jasper/${commons.jasper} + mvn:orbit/org.apache.juli.extras/${commons.juli.version} + mvn:orbit/org.apache.tomcat.api/${commons.tomcat.api} + mvn:orbit/org.apache.tomcat.util/${commons.tomcat.util} mvn:org.opendaylight.controller/karaf-tomcat-security/${karaf.security.version} wrap:mvn:virgomirror/org.eclipse.jdt.core.compiler.batch/${eclipse.jdt.core.compiler.batch.version} diff --git a/features/mdsal/pom.xml b/features/mdsal/pom.xml index 3a9de7abcd..38fe92fa82 100644 --- a/features/mdsal/pom.xml +++ b/features/mdsal/pom.xml @@ -40,6 +40,13 @@ features xml + + org.opendaylight.controller + features-akka + ${commons.opendaylight.version} + features + xml + org.opendaylight.controller sal-core-api @@ -60,6 +67,12 @@ org.opendaylight.controller sal-dom-xsql + + org.opendaylight.controller + sal-dom-xsql-config + config + xml + org.opendaylight.controller sal-binding-api @@ -91,6 +104,30 @@ xml config + + org.opendaylight.controller + sal-distributed-datastore + + + org.opendaylight.controller + sal-remoterpc-connector + + + org.opendaylight.controller + sal-clustering-commons + + + org.opendaylight.controller + sal-akka-raft + ${mdsal.version} + + + org.opendaylight.controller + sal-clustering-config + ${mdsal.version} + xml + config + org.opendaylight.controller sal-netconf-connector @@ -184,6 +221,42 @@ xml config + + org.opendaylight.controller + sal-rest-docgen + + + com.fasterxml.jackson.core + jackson-annotations + + + com.fasterxml.jackson.core + jackson-core + + + com.fasterxml.jackson.core + jackson-databind + + + com.fasterxml.jackson.datatype + jackson-datatype-json-org + + + com.fasterxml.jackson.module + jackson-module-jaxb-annotations + + + com.fasterxml.jackson.jaxrs + jackson-jaxrs-base + + + com.fasterxml.jackson.jaxrs + jackson-jaxrs-json-provider + + + org.json + json + org.opendaylight.yangtools diff --git a/features/mdsal/src/main/resources/features.xml b/features/mdsal/src/main/resources/features.xml index 9e29fc84f8..ae73c71ee4 100644 --- a/features/mdsal/src/main/resources/features.xml +++ b/features/mdsal/src/main/resources/features.xml @@ -7,11 +7,13 @@ mvn:org.opendaylight.controller/features-config/${config.version}/xml/features mvn:org.opendaylight.controller/features-config-persister/${config.version}/xml/features mvn:org.opendaylight.controller/features-config-netty/${config.version}/xml/features + mvn:org.opendaylight.controller/features-akka/${commons.opendaylight.version}/xml/features odl-mdsal-broker odl-mdsal-netconf-connector odl-restconf odl-mdsal-xsql + odl-mdsal-clustering odl-toaster @@ -45,6 +47,7 @@ war mvn:org.opendaylight.controller/sal-rest-connector/${project.version} mvn:com.google.code.gson/gson/${gson.version} + mvn:org.opendaylight.yangtools/yang-data-codec-gson/${yangtools.version} mvn:com.sun.jersey/jersey-core/${jersey.version} mvn:com.sun.jersey/jersey-server/${jersey.version} mvn:com.sun.jersey/jersey-servlet/${jersey.version} @@ -71,4 +74,48 @@ mvn:org.opendaylight.controller/sal-dom-xsql/${project.version} mvn:org.opendaylight.controller/sal-dom-xsql-config/${project.version}/xml/config + + odl-restconf + mvn:org.opendaylight.controller/sal-rest-docgen/${project.version} + mvn:com.fasterxml.jackson.core/jackson-annotations/${jackson.version} + mvn:com.fasterxml.jackson.core/jackson-core/${jackson.version} + mvn:com.fasterxml.jackson.core/jackson-databind/${jackson.version} + mvn:com.fasterxml.jackson.datatype/jackson-datatype-json-org/${jackson.version} + mvn:com.fasterxml.jackson.module/jackson-module-jaxb-annotations/${jackson.version} + mvn:com.fasterxml.jackson.jaxrs/jackson-jaxrs-base/${jackson.version} + mvn:com.fasterxml.jackson.jaxrs/jackson-jaxrs-json-provider/${jackson.version} + mvn:com.sun.jersey/jersey-core/${jersey.version} + mvn:com.sun.jersey/jersey-server/${jersey.version} + mvn:com.sun.jersey/jersey-servlet/${jersey.version} + wrap:mvn:org.json/json/${org.json.version} + + + odl-mdsal-broker + odl-akka-system + odl-akka-persistence + mvn:org.opendaylight.controller/sal-clustering-commons/${project.version} + mvn:org.opendaylight.controller/sal-akka-raft/${project.version} + mvn:com.codahale.metrics/metrics-core/3.0.1 + + + odl-mdsal-broker + odl-mdsal-clustering-commons + odl-akka-clustering + mvn:org.opendaylight.controller/sal-distributed-datastore/${project.version} + + + odl-mdsal-broker + odl-mdsal-clustering-commons + odl-akka-clustering + odl-akka-leveldb + mvn:org.opendaylight.controller/sal-remoterpc-connector/${project.version} + + + odl-mdsal-remoterpc-connector + odl-mdsal-distributed-datastore + mvn:org.opendaylight.controller/sal-clustering-config/${project.version}/xml/config + mvn:org.opendaylight.controller/sal-clustering-config/${project.version}/xml/akkaconf + mvn:org.opendaylight.controller/sal-clustering-config/${project.version}/xml/moduleshardconf + mvn:org.opendaylight.controller/sal-clustering-config/${project.version}/xml/moduleconf + diff --git a/features/netconf/pom.xml b/features/netconf/pom.xml index 51d6f6291b..d18d227f00 100644 --- a/features/netconf/pom.xml +++ b/features/netconf/pom.xml @@ -34,6 +34,10 @@ org.opendaylight.controller netconf-api + + org.opendaylight.controller + netconf-auth + org.opendaylight.controller ietf-netconf-monitoring diff --git a/features/netconf/src/main/resources/features.xml b/features/netconf/src/main/resources/features.xml index 1453a5fb96..0a6356231a 100644 --- a/features/netconf/src/main/resources/features.xml +++ b/features/netconf/src/main/resources/features.xml @@ -19,6 +19,7 @@ odl-protocol-framework mvn:org.opendaylight.controller/netconf-api/${project.version} + mvn:org.opendaylight.controller/netconf-auth/${project.version} mvn:org.opendaylight.controller/ietf-netconf-monitoring/${project.version} mvn:org.opendaylight.controller/ietf-netconf-monitoring-extension/${project.version} mvn:org.opendaylight.yangtools.model/ietf-inet-types/${ietf-inet-types.version} diff --git a/features/pom.xml b/features/pom.xml index 039060b4ae..01156cf02a 100644 --- a/features/pom.xml +++ b/features/pom.xml @@ -26,5 +26,6 @@ netconf protocol-framework adsal-compatibility + akka \ No newline at end of file diff --git a/opendaylight/archetypes/opendaylight-configfile-archetype/src/main/resources/archetype-resources/pom.xml b/opendaylight/archetypes/opendaylight-configfile-archetype/src/main/resources/archetype-resources/pom.xml index e217b9abfa..3c778e262b 100644 --- a/opendaylight/archetypes/opendaylight-configfile-archetype/src/main/resources/archetype-resources/pom.xml +++ b/opendaylight/archetypes/opendaylight-configfile-archetype/src/main/resources/archetype-resources/pom.xml @@ -14,6 +14,10 @@ will pick the pom.xml from the parent directory as the parent pom, which may or may not be correct. --> + ${artifactId} ${groupId} Configuration files for md-sal @@ -23,7 +27,7 @@ jar - 80-configfile.xml + 80-${repoName}.xml @@ -44,6 +48,18 @@ xml config + diff --git a/opendaylight/archetypes/opendaylight-configfile-archetype/src/main/resources/archetype-resources/src/main/resources/80-configfile.xml b/opendaylight/archetypes/opendaylight-configfile-archetype/src/main/resources/archetype-resources/src/main/resources/80-__repoName__.xml similarity index 100% rename from opendaylight/archetypes/opendaylight-configfile-archetype/src/main/resources/archetype-resources/src/main/resources/80-configfile.xml rename to opendaylight/archetypes/opendaylight-configfile-archetype/src/main/resources/archetype-resources/src/main/resources/80-__repoName__.xml diff --git a/opendaylight/commons/opendaylight/pom.xml b/opendaylight/commons/opendaylight/pom.xml index 425e968cf7..b05170ddc8 100644 --- a/opendaylight/commons/opendaylight/pom.xml +++ b/opendaylight/commons/opendaylight/pom.xml @@ -39,25 +39,17 @@ 0.4.2-SNAPSHOT 0.4.2-SNAPSHOT - 7.0.32.v201211201336 - 7.0.32.v201211201952 - 7.0.32.v201211201952 - 7.0.32.v201211201952 - 7.0.32.v201211081135 - 7.0.32.v201211201952 - 7.0.32.v201211081135 - 7.0.32.v201211081135 - 7.0.32.v201211201952 + 3.0.1 - 7.0.53.v201406061610 - 7.0.53.v201406070630 - 7.0.53.v201406070630 - 7.0.53.v201406070630 - 7.0.53.v201406060720 - 7.0.53.v201406070630 - 7.0.53.v201406060720 - 7.0.53.v201406060720 - 7.0.53.v201406070630 + 7.0.53.v201406061610 + 7.0.53.v201406070630 + 7.0.53.v201406070630 + 7.0.53.v201406070630 + 7.0.53.v201406060720 + 7.0.53.v201406070630 + 7.0.53.v201406060720 + 7.0.53.v201406060720 + 7.0.53.v201406070630 0.0.3-SNAPSHOT 1.2.2 @@ -76,6 +68,7 @@ 1.4 0.2.5-SNAPSHOT etc/opendaylight/karaf + 05-clustering.xml 00-netty.xml 01-mdsal.xml 04-xsql.xml @@ -207,7 +200,7 @@ 1.0.0-SNAPSHOT 0.4.2-SNAPSHOT 1.2.0 - 1.2.2 + 1.2.2a 0.4.2-SNAPSHOT 0.0.2-SNAPSHOT 0.4.2-SNAPSHOT @@ -253,6 +246,16 @@ logback-core ${logback.version} + + com.codahale.metrics + metrics-core + ${codahale.metrics.version} + + + com.codahale.metrics + metrics-graphite + ${codahale.metrics.version} + com.fasterxml.jackson.core jackson-annotations @@ -1292,6 +1295,13 @@ sal-dom-xsql ${mdsal.version} + + org.opendaylight.controller + sal-dom-xsql-config + ${mdsal.version} + config + xml + org.opendaylight.controller sal-core-api @@ -1754,6 +1764,11 @@ yang-data-composite-node ${yangtools.version} + + org.opendaylight.yangtools + yang-data-codec-gson + ${yangtools.version} + diff --git a/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/impl/osgi/RefreshingSCPModuleInfoRegistryTest.java b/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/impl/osgi/RefreshingSCPModuleInfoRegistryTest.java new file mode 100644 index 0000000000..79391d4343 --- /dev/null +++ b/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/impl/osgi/RefreshingSCPModuleInfoRegistryTest.java @@ -0,0 +1,46 @@ +package org.opendaylight.controller.config.manager.impl.osgi; + +import org.junit.Test; +import org.mockito.Mockito; +import org.opendaylight.controller.config.manager.impl.osgi.mapping.RefreshingSCPModuleInfoRegistry; +import org.opendaylight.yangtools.concepts.ObjectRegistration; +import org.opendaylight.yangtools.sal.binding.generator.api.ModuleInfoRegistry; +import org.opendaylight.yangtools.yang.binding.YangModuleInfo; +import org.opendaylight.yangtools.yang.model.api.SchemaContextProvider; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceRegistration; + +import java.util.*; + +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; + +public class RefreshingSCPModuleInfoRegistryTest { + @Test + public void testConstructor() throws Exception { + ModuleInfoRegistry reg = mock(ModuleInfoRegistry.class); + SchemaContextProvider prov = mock(SchemaContextProvider.class); + doReturn("string").when(prov).toString(); + + BundleContext ctxt = mock(BundleContext.class); + Dictionary dict = new Hashtable(); + ServiceRegistration servReg = mock(ServiceRegistration.class); + doReturn(servReg).when(ctxt).registerService(Mockito.any(Class.class), Mockito.any(SchemaContextProvider.class), Mockito.any(Dictionary.class)); + doReturn(servReg).when(ctxt).registerService(Mockito.anyString(), Mockito.any(Object.class), Mockito.any(Dictionary.class)); + RefreshingSCPModuleInfoRegistry scpreg = new RefreshingSCPModuleInfoRegistry(reg, prov, ctxt); + + YangModuleInfo modInfo = mock(YangModuleInfo.class); + doNothing().when(servReg).setProperties(null); + doNothing().when(servReg).unregister(); + doReturn("").when(modInfo).toString(); + ObjectRegistration ymi = mock(ObjectRegistration.class); + doReturn(ymi).when(reg).registerModuleInfo(modInfo); + + scpreg.registerModuleInfo(modInfo); + scpreg.close(); + + Mockito.verify(servReg, Mockito.times(1)).setProperties(null); + Mockito.verify(servReg, Mockito.times(1)).unregister(); + } +} diff --git a/opendaylight/distribution/opendaylight-karaf-resources/src/main/resources/etc/custom.properties b/opendaylight/distribution/opendaylight-karaf-resources/src/main/resources/etc/custom.properties index c2ac77a5d6..a644bf6ee8 100644 --- a/opendaylight/distribution/opendaylight-karaf-resources/src/main/resources/etc/custom.properties +++ b/opendaylight/distribution/opendaylight-karaf-resources/src/main/resources/etc/custom.properties @@ -92,6 +92,11 @@ ovsdb.listenPort=6640 # default Openflow version = 1.0, we also support 1.3. # ovsdb.of.version=1.3 +# ovsdb can be configured with ml2 to perform l3 forwarding. When used in that scenario, the mac address of the default +# gateway --on the external subnet-- is expected to be resolved from its inet address. The config below overrides that +# specific arp/neighDiscovery lookup. +# ovsdb.l3gateway.mac=00:00:5E:00:02:01 + # TLS configuration # To enable TLS, set secureChannelEnabled=true and specify the location of controller Java KeyStore and TrustStore files. # The Java KeyStore contains controller's private key and certificate. The Java TrustStore contains the trusted certificate diff --git a/opendaylight/distribution/opendaylight/pom.xml b/opendaylight/distribution/opendaylight/pom.xml index fc8b4453ea..fcb452f422 100644 --- a/opendaylight/distribution/opendaylight/pom.xml +++ b/opendaylight/distribution/opendaylight/pom.xml @@ -634,10 +634,6 @@ org.opendaylight.controller.thirdparty net.sf.jung2 - - org.opendaylight.controller.thirdparty - org.apache.catalina.filters.CorsFilter - org.opendaylight.controller.thirdparty org.openflow.openflowj @@ -1070,10 +1066,6 @@ org.opendaylight.controller sal-restconf-broker - - org.opendaylight.controller - sal-remoterpc-connector - @@ -1259,6 +1251,10 @@ org.opendaylight.yangtools yang-parser-impl + + org.opendaylight.yangtools + yang-data-codec-gson + org.opendaylight.yangtools yang-data-composite-node @@ -1316,10 +1312,6 @@ jeromq 0.3.1 - - org.opendaylight.controller - sal-distributed-datastore - org.opendaylight.controller sal-clustering-config @@ -1375,8 +1367,8 @@ generate-resources ${project.build.directory}/configuration - sal-rest-connector-config,config-netty-config,md-sal-config,netconf-config,toaster-config,netconf-connector-config - **\/*.xml + sal-rest-connector-config,config-netty-config,md-sal-config,netconf-config,toaster-config,netconf-connector-config,sal-clustering-config + **\/*.xml,**/*.conf true false diff --git a/opendaylight/distribution/opendaylight/src/main/resources/configuration/config.ini b/opendaylight/distribution/opendaylight/src/main/resources/configuration/config.ini index b2fc3cb386..530e46e14a 100644 --- a/opendaylight/distribution/opendaylight/src/main/resources/configuration/config.ini +++ b/opendaylight/distribution/opendaylight/src/main/resources/configuration/config.ini @@ -116,6 +116,11 @@ ovsdb.listenPort=6640 # default Openflow version = 1.3, we also support 1.0. ovsdb.of.version=1.3 +# ovsdb can be configured with ml2 to perform l3 forwarding. When used in that scenario, the mac address of the default +# gateway --on the external subnet-- is expected to be resolved from its inet address. The config below overrides that +# specific arp/neighDiscovery lookup. +# ovsdb.l3gateway.mac=00:00:5E:00:02:01 + # TLS configuration # To enable TLS, set secureChannelEnabled=true and specify the location of controller Java KeyStore and TrustStore files. # The Java KeyStore contains controller's private key and certificate. The Java TrustStore contains the trusted certificate diff --git a/opendaylight/distribution/opendaylight/src/main/resources/run.sh b/opendaylight/distribution/opendaylight/src/main/resources/run.sh index 13be2336b6..b032469b7f 100755 --- a/opendaylight/distribution/opendaylight/src/main/resources/run.sh +++ b/opendaylight/distribution/opendaylight/src/main/resources/run.sh @@ -112,6 +112,7 @@ while true ; do -Xmx*) jvmMaxMemory="$1"; shift;; -D*) extraJVMOpts="${extraJVMOpts} $1"; shift;; -X*) extraJVMOpts="${extraJVMOpts} $1"; shift;; + -J*) extraJVMOpts="${extraJVMOpts} -$(echo "$1" | cut -d'J' -f2)"; shift;; -agentpath:*) agentPath="$1"; shift;; "") break ;; *) echo "Unknown option $1"; unknown_option=1; break ;; diff --git a/opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/NodeMapping.java b/opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/NodeMapping.java index be087abdb4..b873f8a9fe 100644 --- a/opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/NodeMapping.java +++ b/opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/NodeMapping.java @@ -14,6 +14,7 @@ import java.math.BigInteger; import java.util.Date; import java.util.HashSet; import java.util.List; +import java.util.regex.Pattern; import org.opendaylight.controller.sal.common.util.Arguments; import org.opendaylight.controller.sal.core.AdvertisedBandwidth; import org.opendaylight.controller.sal.core.Bandwidth; @@ -80,6 +81,12 @@ public final class NodeMapping { private final static Class NODECONNECTOR_CLASS = NodeConnector.class; + private final static Pattern COLON_NUMBERS_EOL = Pattern.compile(":[0-9]+$"); + + private final static Pattern NUMBERS_ONLY = Pattern.compile("[0-9]+"); + + private final static Pattern ALL_CHARS_TO_COLON = Pattern.compile("^.*:"); + private NodeMapping() { throw new UnsupportedOperationException("Utility class. Instantiation is not allowed."); } @@ -182,15 +189,16 @@ public final class NodeMapping { return org.opendaylight.controller.sal.core.NodeConnector.SPECIALNODECONNECTORID; } - String nodeConnectorIdStripped = nodeConnectorId.getValue().replaceFirst("^.*:", ""); - if (nodeConnectorIdStripped.matches("[0-9]+")) { + String nodeConnectorIdStripped = ALL_CHARS_TO_COLON.matcher(nodeConnectorId.getValue()).replaceFirst(""); + + if (NUMBERS_ONLY.matcher(nodeConnectorIdStripped).matches()) { Short nodeConnectorIdVal = null; try { nodeConnectorIdVal = Short.valueOf(nodeConnectorIdStripped); + return nodeConnectorIdVal; } catch (NumberFormatException e) { - LOG.warn("nodeConnectorId not supported (short): {}", nodeConnectorIdStripped, e); + LOG.warn("nodeConnectorId not supported (long): {}", nodeConnectorIdStripped, e); } - return nodeConnectorIdVal; } return nodeConnectorIdStripped; } @@ -198,7 +206,7 @@ public final class NodeMapping { public static NodeId toAdNodeId(final NodeConnectorId nodeConnectorId) { NodeId nodeId = null; if (nodeConnectorId != null) { - nodeId = new NodeId(nodeConnectorId.getValue().replaceFirst(":[0-9]+$", "")); + nodeId = new NodeId(COLON_NUMBERS_EOL.matcher(nodeConnectorId.getValue()).replaceFirst("")); } return nodeId; } diff --git a/opendaylight/md-sal/pom.xml b/opendaylight/md-sal/pom.xml index c184c44d7d..ce830eaa62 100644 --- a/opendaylight/md-sal/pom.xml +++ b/opendaylight/md-sal/pom.xml @@ -77,13 +77,13 @@ sal-dom-xsql + sal-dom-xsql-config sal-test-model sal-remoterpc-connector - sal-dom-xsql-config diff --git a/opendaylight/md-sal/sal-akka-raft/pom.xml b/opendaylight/md-sal/sal-akka-raft/pom.xml index 325005b239..98c81c267f 100644 --- a/opendaylight/md-sal/sal-akka-raft/pom.xml +++ b/opendaylight/md-sal/sal-akka-raft/pom.xml @@ -97,9 +97,8 @@ ${project.groupId}.${project.artifactId} - - - + org.opendaylight.cluster.raft + * diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/example/ExampleActor.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/example/ExampleActor.java index cbd7ca2d70..c4ff108611 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/example/ExampleActor.java +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/example/ExampleActor.java @@ -12,14 +12,21 @@ import akka.actor.ActorRef; import akka.actor.Props; import akka.japi.Creator; import com.google.common.base.Optional; +import com.google.protobuf.ByteString; import org.opendaylight.controller.cluster.example.messages.KeyValue; import org.opendaylight.controller.cluster.example.messages.KeyValueSaved; import org.opendaylight.controller.cluster.example.messages.PrintRole; import org.opendaylight.controller.cluster.example.messages.PrintState; import org.opendaylight.controller.cluster.raft.ConfigParams; import org.opendaylight.controller.cluster.raft.RaftActor; +import org.opendaylight.controller.cluster.raft.base.messages.CaptureSnapshotReply; import org.opendaylight.controller.cluster.raft.protobuff.client.messages.Payload; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; import java.util.HashMap; import java.util.Map; @@ -82,14 +89,63 @@ public class ExampleActor extends RaftActor { } } - @Override protected Object createSnapshot() { - return state; + @Override protected void createSnapshot() { + ByteString bs = null; + try { + bs = fromObject(state); + } catch (Exception e) { + LOG.error("Exception in creating snapshot", e); + } + getSelf().tell(new CaptureSnapshotReply(bs), null); } - @Override protected void applySnapshot(Object snapshot) { + @Override protected void applySnapshot(ByteString snapshot) { state.clear(); - state.putAll((HashMap) snapshot); - LOG.debug("Snapshot applied to state :" + ((HashMap) snapshot).size()); + try { + state.putAll((HashMap) toObject(snapshot)); + } catch (Exception e) { + LOG.error("Exception in applying snapshot", e); + } + LOG.debug("Snapshot applied to state :" + ((HashMap) state).size()); + } + + private ByteString fromObject(Object snapshot) throws Exception { + ByteArrayOutputStream b = null; + ObjectOutputStream o = null; + try { + b = new ByteArrayOutputStream(); + o = new ObjectOutputStream(b); + o.writeObject(snapshot); + byte[] snapshotBytes = b.toByteArray(); + return ByteString.copyFrom(snapshotBytes); + } finally { + if (o != null) { + o.flush(); + o.close(); + } + if (b != null) { + b.close(); + } + } + } + + private Object toObject(ByteString bs) throws ClassNotFoundException, IOException { + Object obj = null; + ByteArrayInputStream bis = null; + ObjectInputStream ois = null; + try { + bis = new ByteArrayInputStream(bs.toByteArray()); + ois = new ObjectInputStream(bis); + obj = ois.readObject(); + } finally { + if (bis != null) { + bis.close(); + } + if (ois != null) { + ois.close(); + } + } + return obj; } @Override protected void onStateChanged() { diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/example/ExampleConfigParamsImpl.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/example/ExampleConfigParamsImpl.java index d11377dbcb..6192cad230 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/example/ExampleConfigParamsImpl.java +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/example/ExampleConfigParamsImpl.java @@ -17,4 +17,9 @@ public class ExampleConfigParamsImpl extends DefaultConfigParamsImpl { public long getSnapshotBatchCount() { return 50; } + + @Override + public int getSnapshotChunkSize() { + return 50; + } } diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/example/TestDriver.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/example/TestDriver.java index fd6e192bf0..978ea91089 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/example/TestDriver.java +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/example/TestDriver.java @@ -109,6 +109,8 @@ public class TestDriver { td.printState(); } else if (command.startsWith("printNodes")) { td.printNodes(); + } else { + System.out.println("Invalid command:" + command); } } 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 b5b034afb9..b436bce500 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,8 @@ */ package org.opendaylight.controller.cluster.raft; +import com.google.protobuf.ByteString; + import java.util.ArrayList; import java.util.List; @@ -16,12 +18,18 @@ import java.util.List; */ public abstract class AbstractReplicatedLogImpl implements ReplicatedLog { - protected final List journal; - protected final Object snapshot; + protected List journal; + protected ByteString snapshot; protected long snapshotIndex = -1; protected long snapshotTerm = -1; - public AbstractReplicatedLogImpl(Object state, long snapshotIndex, + // to be used for rollback during save snapshot failure + protected List snapshottedJournal; + protected ByteString previousSnapshot; + protected long previousSnapshotIndex = -1; + protected long previousSnapshotTerm = -1; + + public AbstractReplicatedLogImpl(ByteString state, long snapshotIndex, long snapshotTerm, List unAppliedEntries) { this.snapshot = state; this.snapshotIndex = snapshotIndex; @@ -137,11 +145,11 @@ public abstract class AbstractReplicatedLogImpl implements ReplicatedLog { @Override public boolean isInSnapshot(long logEntryIndex) { - return logEntryIndex <= snapshotIndex; + return logEntryIndex <= snapshotIndex && snapshotIndex != -1; } @Override - public Object getSnapshot() { + public ByteString getSnapshot() { return snapshot; } @@ -160,4 +168,68 @@ public abstract class AbstractReplicatedLogImpl implements ReplicatedLog { @Override public abstract void removeFromAndPersist(long index); + + @Override + public void setSnapshotIndex(long snapshotIndex) { + this.snapshotIndex = snapshotIndex; + } + + @Override + public void setSnapshotTerm(long snapshotTerm) { + this.snapshotTerm = snapshotTerm; + } + + @Override + public void setSnapshot(ByteString snapshot) { + this.snapshot = snapshot; + } + + @Override + public void clear(int startIndex, int endIndex) { + journal.subList(startIndex, endIndex).clear(); + } + + @Override + public void snapshotPreCommit(ByteString snapshot, long snapshotCapturedIndex, long snapshotCapturedTerm) { + snapshottedJournal = new ArrayList<>(journal.size()); + + snapshottedJournal.addAll(journal.subList(0, (int)(snapshotCapturedIndex - snapshotIndex))); + clear(0, (int) (snapshotCapturedIndex - snapshotIndex)); + + previousSnapshotIndex = snapshotIndex; + setSnapshotIndex(snapshotCapturedIndex); + + previousSnapshotTerm = snapshotTerm; + setSnapshotTerm(snapshotCapturedTerm); + + previousSnapshot = getSnapshot(); + setSnapshot(snapshot); + } + + @Override + public void snapshotCommit() { + snapshottedJournal.clear(); + snapshottedJournal = null; + previousSnapshotIndex = -1; + previousSnapshotTerm = -1; + previousSnapshot = null; + } + + @Override + public void snapshotRollback() { + snapshottedJournal.addAll(journal); + journal.clear(); + journal = snapshottedJournal; + snapshottedJournal = null; + + snapshotIndex = previousSnapshotIndex; + previousSnapshotIndex = -1; + + snapshotTerm = previousSnapshotTerm; + previousSnapshotTerm = -1; + + snapshot = previousSnapshot; + previousSnapshot = null; + + } } 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 4c6434aec4..ed6439d8c3 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 @@ -52,4 +52,9 @@ public interface ConfigParams { * @return int */ public int getElectionTimeVariance(); + + /** + * The size (in bytes) of the snapshot chunk sent from Leader + */ + public int getSnapshotChunkSize(); } 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 6432fa4811..75c237f503 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 @@ -25,6 +25,8 @@ public class DefaultConfigParamsImpl implements ConfigParams { */ private static final int ELECTION_TIME_MAX_VARIANCE = 100; + private final int SNAPSHOT_CHUNK_SIZE = 2048 * 1000; //2MB + /** * The interval at which a heart beat message will be sent to the remote @@ -58,4 +60,9 @@ public class DefaultConfigParamsImpl implements ConfigParams { public int getElectionTimeVariance() { return ELECTION_TIME_MAX_VARIANCE; } + + @Override + public int getSnapshotChunkSize() { + return SNAPSHOT_CHUNK_SIZE; + } } 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 988789b401..296ce2d24a 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 @@ -19,10 +19,14 @@ import akka.persistence.SaveSnapshotSuccess; import akka.persistence.SnapshotOffer; import akka.persistence.SnapshotSelectionCriteria; import akka.persistence.UntypedPersistentActor; +import com.google.common.base.Optional; +import com.google.protobuf.ByteString; import org.opendaylight.controller.cluster.raft.base.messages.ApplySnapshot; import org.opendaylight.controller.cluster.raft.base.messages.ApplyState; +import org.opendaylight.controller.cluster.raft.base.messages.CaptureSnapshot; +import org.opendaylight.controller.cluster.raft.base.messages.CaptureSnapshotReply; import org.opendaylight.controller.cluster.raft.base.messages.Replicate; -import com.google.common.base.Optional; +import org.opendaylight.controller.cluster.raft.base.messages.SendHeartBeat; import org.opendaylight.controller.cluster.raft.behaviors.Candidate; import org.opendaylight.controller.cluster.raft.behaviors.Follower; import org.opendaylight.controller.cluster.raft.behaviors.Leader; @@ -31,10 +35,11 @@ import org.opendaylight.controller.cluster.raft.client.messages.AddRaftPeer; import org.opendaylight.controller.cluster.raft.client.messages.FindLeader; import org.opendaylight.controller.cluster.raft.client.messages.FindLeaderReply; import org.opendaylight.controller.cluster.raft.client.messages.RemoveRaftPeer; +import org.opendaylight.controller.cluster.raft.messages.AppendEntriesReply; import org.opendaylight.controller.cluster.raft.protobuff.client.messages.Payload; +import org.opendaylight.controller.protobuff.messages.cluster.raft.AppendEntriesMessages; import java.io.Serializable; -import java.util.List; import java.util.Map; /** @@ -98,6 +103,9 @@ public abstract class RaftActor extends UntypedPersistentActor { */ private ReplicatedLogImpl replicatedLog = new ReplicatedLogImpl(); + private CaptureSnapshot captureSnapshot = null; + + private volatile boolean hasSnapshotCaptureInitiated = false; public RaftActor(String id, Map peerAddresses) { this(id, peerAddresses, Optional.absent()); @@ -125,6 +133,7 @@ public abstract class RaftActor extends UntypedPersistentActor { replicatedLog = new ReplicatedLogImpl(snapshot); context.setReplicatedLog(replicatedLog); + context.setLastApplied(snapshot.getLastAppliedIndex()); LOG.debug("Applied snapshot to replicatedLog. " + "snapshotIndex={}, snapshotTerm={}, journal-size={}", @@ -132,7 +141,7 @@ public abstract class RaftActor extends UntypedPersistentActor { replicatedLog.size()); // Apply the snapshot to the actors state - applySnapshot(snapshot.getState()); + applySnapshot(ByteString.copyFrom(snapshot.getState())); } else if (message instanceof ReplicatedLogEntry) { replicatedLog.append((ReplicatedLogEntry) message); @@ -164,7 +173,17 @@ public abstract class RaftActor extends UntypedPersistentActor { applyState.getReplicatedLogEntry().getData()); } else if(message instanceof ApplySnapshot ) { - applySnapshot(((ApplySnapshot) message).getSnapshot()); + Snapshot snapshot = ((ApplySnapshot) message).getSnapshot(); + + LOG.debug("ApplySnapshot called on Follower Actor " + + "snapshotIndex:{}, snapshotTerm:{}", snapshot.getLastAppliedIndex(), + snapshot.getLastAppliedTerm()); + applySnapshot(ByteString.copyFrom(snapshot.getState())); + + //clears the followers log, sets the snapshot index to ensure adjusted-index works + replicatedLog = new ReplicatedLogImpl(snapshot); + context.setReplicatedLog(replicatedLog); + context.setLastApplied(snapshot.getLastAppliedIndex()); } else if (message instanceof FindLeader) { getSender().tell( @@ -174,13 +193,26 @@ public abstract class RaftActor extends UntypedPersistentActor { } else if (message instanceof SaveSnapshotSuccess) { SaveSnapshotSuccess success = (SaveSnapshotSuccess) message; + LOG.info("SaveSnapshotSuccess received for snapshot"); + + context.getReplicatedLog().snapshotCommit(); // TODO: Not sure if we want to be this aggressive with trimming stuff trimPersistentData(success.metadata().sequenceNr()); } else if (message instanceof SaveSnapshotFailure) { + SaveSnapshotFailure saveSnapshotFailure = (SaveSnapshotFailure) message; + + LOG.info("saveSnapshotFailure.metadata():{}", saveSnapshotFailure.metadata().toString()); + LOG.error(saveSnapshotFailure.cause(), "SaveSnapshotFailure received for snapshot Cause:"); - // TODO: Handle failure in saving the snapshot + context.getReplicatedLog().snapshotRollback(); + + LOG.info("Replicated Log rollbacked. Snapshot will be attempted in the next cycle." + + "snapshotIndex:{}, snapshotTerm:{}, log-size:{}", + context.getReplicatedLog().getSnapshotIndex(), + context.getReplicatedLog().getSnapshotTerm(), + context.getReplicatedLog().size()); } else if (message instanceof AddRaftPeer){ @@ -196,7 +228,25 @@ public abstract class RaftActor extends UntypedPersistentActor { RemoveRaftPeer rrp = (RemoveRaftPeer)message; context.removePeer(rrp.getName()); + } else if (message instanceof CaptureSnapshot) { + LOG.debug("CaptureSnapshot received by actor"); + CaptureSnapshot cs = (CaptureSnapshot)message; + captureSnapshot = cs; + createSnapshot(); + + } else if (message instanceof CaptureSnapshotReply){ + LOG.debug("CaptureSnapshotReply received by actor"); + CaptureSnapshotReply csr = (CaptureSnapshotReply) message; + + ByteString stateInBytes = csr.getSnapshot(); + LOG.debug("CaptureSnapshotReply stateInBytes size:{}", stateInBytes.size()); + handleCaptureSnapshotReply(stateInBytes); + } else { + if (!(message instanceof AppendEntriesMessages.AppendEntries) + && !(message instanceof AppendEntriesReply) && !(message instanceof SendHeartBeat)) { + LOG.debug("onReceiveCommand: message:" + message.getClass()); + } RaftState state = currentBehavior.handleMessage(getSender(), message); @@ -344,7 +394,7 @@ public abstract class RaftActor extends UntypedPersistentActor { * * @return The current state of the actor */ - protected abstract Object createSnapshot(); + protected abstract void createSnapshot(); /** * This method will be called by the RaftActor during recovery to @@ -356,7 +406,7 @@ public abstract class RaftActor extends UntypedPersistentActor { * * @param snapshot A snapshot of the state of the actor */ - protected abstract void applySnapshot(Object snapshot); + protected abstract void applySnapshot(ByteString snapshot); /** * This method will be called by the RaftActor when the state of the @@ -423,11 +473,39 @@ public abstract class RaftActor extends UntypedPersistentActor { return peerAddress; } + private void handleCaptureSnapshotReply(ByteString stateInBytes) { + // create a snapshot object from the state provided and save it + // when snapshot is saved async, SaveSnapshotSuccess is raised. + + Snapshot sn = Snapshot.create(stateInBytes.toByteArray(), + context.getReplicatedLog().getFrom(captureSnapshot.getLastAppliedIndex() + 1), + captureSnapshot.getLastIndex(), captureSnapshot.getLastTerm(), + captureSnapshot.getLastAppliedIndex(), captureSnapshot.getLastAppliedTerm()); + + saveSnapshot(sn); + + LOG.info("Persisting of snapshot done:{}", sn.getLogMessage()); + + //be greedy and remove entries from in-mem journal which are in the snapshot + // and update snapshotIndex and snapshotTerm without waiting for the success, + + context.getReplicatedLog().snapshotPreCommit(stateInBytes, + captureSnapshot.getLastAppliedIndex(), + captureSnapshot.getLastAppliedTerm()); + + LOG.info("Removed in-memory snapshotted entries, adjusted snaphsotIndex:{} " + + "and term:{}", captureSnapshot.getLastAppliedIndex(), + captureSnapshot.getLastAppliedTerm()); + + captureSnapshot = null; + hasSnapshotCaptureInitiated = false; + } + private class ReplicatedLogImpl extends AbstractReplicatedLogImpl { public ReplicatedLogImpl(Snapshot snapshot) { - super(snapshot.getState(), + super(ByteString.copyFrom(snapshot.getState()), snapshot.getLastAppliedIndex(), snapshot.getLastAppliedTerm(), snapshot.getUnAppliedEntries()); } @@ -476,8 +554,10 @@ public abstract class RaftActor extends UntypedPersistentActor { persist(replicatedLogEntry, new Procedure() { public void apply(ReplicatedLogEntry evt) throws Exception { - // FIXME : Tentatively create a snapshot every hundred thousand entries. To be tuned. - if (journal.size() > context.getConfigParams().getSnapshotBatchCount()) { + // when a snaphsot is being taken, captureSnapshot != null + if (hasSnapshotCaptureInitiated == false && + journal.size() % context.getConfigParams().getSnapshotBatchCount() == 0) { + LOG.info("Initiating Snapshot Capture.."); long lastAppliedIndex = -1; long lastAppliedTerm = -1; @@ -493,26 +573,11 @@ public abstract class RaftActor extends UntypedPersistentActor { LOG.debug("Snapshot Capture lastAppliedIndex:{}", lastAppliedIndex); LOG.debug("Snapshot Capture lastAppliedTerm:{}", lastAppliedTerm); - // create a snapshot object from the state provided and save it - // when snapshot is saved async, SaveSnapshotSuccess is raised. - Snapshot sn = Snapshot.create(createSnapshot(), - getFrom(context.getLastApplied() + 1), - lastIndex(), lastTerm(), lastAppliedIndex, - lastAppliedTerm); - saveSnapshot(sn); - - LOG.info("Persisting of snapshot done:{}", sn.getLogMessage()); - - //be greedy and remove entries from in-mem journal which are in the snapshot - // and update snapshotIndex and snapshotTerm without waiting for the success, - // TODO: damage-recovery to be done on failure - journal.subList(0, (int) (lastAppliedIndex - snapshotIndex)).clear(); - snapshotIndex = lastAppliedIndex; - snapshotTerm = lastAppliedTerm; - - LOG.info("Removed in-memory snapshotted entries, " + - "adjusted snaphsotIndex:{}" + - "and term:{}", snapshotIndex, lastAppliedTerm); + // send a CaptureSnapshot to self to make the expensive operation async. + getSelf().tell(new CaptureSnapshot( + lastIndex(), lastTerm(), lastAppliedIndex, lastAppliedTerm), + null); + hasSnapshotCaptureInitiated = true; } // Send message for replication if (clientActor != null) { @@ -542,65 +607,6 @@ public abstract class RaftActor extends UntypedPersistentActor { } - private static class Snapshot implements Serializable { - private final Object state; - private final List unAppliedEntries; - private final long lastIndex; - private final long lastTerm; - private final long lastAppliedIndex; - private final long lastAppliedTerm; - - private Snapshot(Object state, - List unAppliedEntries, long lastIndex, - long lastTerm, long lastAppliedIndex, long lastAppliedTerm) { - this.state = state; - this.unAppliedEntries = unAppliedEntries; - this.lastIndex = lastIndex; - this.lastTerm = lastTerm; - this.lastAppliedIndex = lastAppliedIndex; - this.lastAppliedTerm = lastAppliedTerm; - } - - - public static Snapshot create(Object state, - List entries, long lastIndex, long lastTerm, - long lastAppliedIndex, long lastAppliedTerm) { - return new Snapshot(state, entries, lastIndex, lastTerm, - lastAppliedIndex, lastAppliedTerm); - } - - public Object getState() { - return state; - } - - public List getUnAppliedEntries() { - return unAppliedEntries; - } - - public long getLastTerm() { - return lastTerm; - } - - public long getLastAppliedIndex() { - return lastAppliedIndex; - } - - public long getLastAppliedTerm() { - return lastAppliedTerm; - } - - public String getLogMessage() { - StringBuilder sb = new StringBuilder(); - return sb.append("Snapshot={") - .append("lastTerm:" + this.getLastTerm() + ", ") - .append("LastAppliedIndex:" + this.getLastAppliedIndex() + ", ") - .append("LastAppliedTerm:" + this.getLastAppliedTerm() + ", ") - .append("UnAppliedEntries size:" + this.getUnAppliedEntries().size() + "}") - .toString(); - - } - } - private class ElectionTermImpl implements ElectionTerm { /** * Identifier of the actor whose election term information this is diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/ReplicatedLog.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/ReplicatedLog.java index e6e160bc02..c17f5448c6 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/ReplicatedLog.java +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/ReplicatedLog.java @@ -8,6 +8,8 @@ package org.opendaylight.controller.cluster.raft; +import com.google.protobuf.ByteString; + import java.util.List; /** @@ -118,7 +120,7 @@ public interface ReplicatedLog { * * @return an object representing the snapshot if it exists. null otherwise */ - Object getSnapshot(); + ByteString getSnapshot(); /** * Get the index of the snapshot @@ -134,4 +136,49 @@ public interface ReplicatedLog { * otherwise */ long getSnapshotTerm(); + + /** + * sets the snapshot index in the replicated log + * @param snapshotIndex + */ + void setSnapshotIndex(long snapshotIndex); + + /** + * sets snapshot term + * @param snapshotTerm + */ + public void setSnapshotTerm(long snapshotTerm); + + /** + * sets the snapshot in bytes + * @param snapshot + */ + public void setSnapshot(ByteString snapshot); + + /** + * Clears the journal entries with startIndex(inclusive) and endIndex (exclusive) + * @param startIndex + * @param endIndex + */ + public void clear(int startIndex, int endIndex); + + /** + * Handles all the bookkeeping in order to perform a rollback in the + * event of SaveSnapshotFailure + * @param snapshot + * @param snapshotCapturedIndex + * @param snapshotCapturedTerm + */ + public void snapshotPreCommit(ByteString snapshot, + long snapshotCapturedIndex, long snapshotCapturedTerm); + + /** + * Sets the Replicated log to state after snapshot success. + */ + public void snapshotCommit(); + + /** + * Restores the replicated log to a state in the event of a save snapshot failure + */ + public void snapshotRollback(); } diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/SerializationUtils.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/SerializationUtils.java index 374e0fa9ba..2f5ba48f92 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/SerializationUtils.java +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/SerializationUtils.java @@ -9,12 +9,16 @@ package org.opendaylight.controller.cluster.raft; import org.opendaylight.controller.cluster.raft.messages.AppendEntries; +import org.opendaylight.controller.cluster.raft.messages.InstallSnapshot; public class SerializationUtils { public static Object fromSerializable(Object serializable){ if(serializable.getClass().equals(AppendEntries.SERIALIZABLE_CLASS)){ return AppendEntries.fromSerializable(serializable); + + } else if (serializable.getClass().equals(InstallSnapshot.SERIALIZABLE_CLASS)) { + return InstallSnapshot.fromSerializable(serializable); } return serializable; } diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/Snapshot.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/Snapshot.java new file mode 100644 index 0000000000..8e0fcca9f7 --- /dev/null +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/Snapshot.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.cluster.raft; + +import java.io.Serializable; +import java.util.List; + + +public class Snapshot implements Serializable { + private final byte[] state; + private final List unAppliedEntries; + private final long lastIndex; + private final long lastTerm; + private final long lastAppliedIndex; + private final long lastAppliedTerm; + + private Snapshot(byte[] state, + List unAppliedEntries, long lastIndex, + long lastTerm, long lastAppliedIndex, long lastAppliedTerm) { + this.state = state; + this.unAppliedEntries = unAppliedEntries; + this.lastIndex = lastIndex; + this.lastTerm = lastTerm; + this.lastAppliedIndex = lastAppliedIndex; + this.lastAppliedTerm = lastAppliedTerm; + } + + + public static Snapshot create(byte[] state, + List entries, long lastIndex, long lastTerm, + long lastAppliedIndex, long lastAppliedTerm) { + return new Snapshot(state, entries, lastIndex, lastTerm, + lastAppliedIndex, lastAppliedTerm); + } + + public byte[] getState() { + return state; + } + + public List getUnAppliedEntries() { + return unAppliedEntries; + } + + public long getLastTerm() { + return lastTerm; + } + + public long getLastAppliedIndex() { + return lastAppliedIndex; + } + + public long getLastAppliedTerm() { + return lastAppliedTerm; + } + + public long getLastIndex() { + return this.lastIndex; + } + + public String getLogMessage() { + StringBuilder sb = new StringBuilder(); + return sb.append("Snapshot={") + .append("lastTerm:" + this.getLastTerm() + ", ") + .append("lastIndex:" + this.getLastIndex() + ", ") + .append("LastAppliedIndex:" + this.getLastAppliedIndex() + ", ") + .append("LastAppliedTerm:" + this.getLastAppliedTerm() + ", ") + .append("UnAppliedEntries size:" + this.getUnAppliedEntries().size() + "}") + .toString(); + + } +} diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/base/messages/ApplySnapshot.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/base/messages/ApplySnapshot.java index 9739fb2f1b..c356804223 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/base/messages/ApplySnapshot.java +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/base/messages/ApplySnapshot.java @@ -8,16 +8,21 @@ package org.opendaylight.controller.cluster.raft.base.messages; +import org.opendaylight.controller.cluster.raft.Snapshot; + import java.io.Serializable; +/** + * Internal message, issued by follower to its actor + */ public class ApplySnapshot implements Serializable { - private final Object snapshot; + private final Snapshot snapshot; - public ApplySnapshot(Object snapshot) { + public ApplySnapshot(Snapshot snapshot) { this.snapshot = snapshot; } - public Object getSnapshot() { + public Snapshot getSnapshot() { return snapshot; } } diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/base/messages/CaptureSnapshot.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/base/messages/CaptureSnapshot.java new file mode 100644 index 0000000000..bb86e1a37d --- /dev/null +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/base/messages/CaptureSnapshot.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.cluster.raft.base.messages; + +public class CaptureSnapshot { + private long lastAppliedIndex; + private long lastAppliedTerm; + private long lastIndex; + private long lastTerm; + + public CaptureSnapshot(long lastIndex, long lastTerm, + long lastAppliedIndex, long lastAppliedTerm) { + this.lastIndex = lastIndex; + this.lastTerm = lastTerm; + this.lastAppliedIndex = lastAppliedIndex; + this.lastAppliedTerm = lastAppliedTerm; + } + + public long getLastAppliedIndex() { + return lastAppliedIndex; + } + + public long getLastAppliedTerm() { + return lastAppliedTerm; + } + + public long getLastIndex() { + return lastIndex; + } + + public long getLastTerm() { + return lastTerm; + } +} diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/base/messages/CaptureSnapshotReply.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/base/messages/CaptureSnapshotReply.java new file mode 100644 index 0000000000..96150db689 --- /dev/null +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/base/messages/CaptureSnapshotReply.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.cluster.raft.base.messages; + +import com.google.protobuf.ByteString; + +public class CaptureSnapshotReply { + private ByteString snapshot; + + public CaptureSnapshotReply(ByteString snapshot) { + this.snapshot = snapshot; + } + + public ByteString getSnapshot() { + return snapshot; + } + + public void setSnapshot(ByteString snapshot) { + this.snapshot = snapshot; + } +} diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/behaviors/AbstractRaftActorBehavior.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/behaviors/AbstractRaftActorBehavior.java index 251a13d583..7e896fed29 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/behaviors/AbstractRaftActorBehavior.java +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/behaviors/AbstractRaftActorBehavior.java @@ -305,6 +305,7 @@ public abstract class AbstractRaftActorBehavior implements RaftActorBehavior { * @param index a log index that is known to be committed */ protected void applyLogToStateMachine(final long index) { + long newLastApplied = context.getLastApplied(); // Now maybe we apply to the state machine for (long i = context.getLastApplied() + 1; i < index + 1; i++) { @@ -322,15 +323,19 @@ public abstract class AbstractRaftActorBehavior implements RaftActorBehavior { if (replicatedLogEntry != null) { actor().tell(new ApplyState(clientActor, identifier, replicatedLogEntry), actor()); + newLastApplied = i; } else { + //if one index is not present in the log, no point in looping + // around as the rest wont be present either context.getLogger().error( - "Missing index " + i + " from log. Cannot apply state."); + "Missing index {} from log. Cannot apply state. Ignoring {} to {}", i, i, index ); + break; } } // Send a local message to the local RaftActor (it's derived class to be // specific to apply the log to it's index) - context.getLogger().debug("Setting last applied to {}", index); - context.setLastApplied(index); + context.getLogger().debug("Setting last applied to {}", newLastApplied); + context.setLastApplied(newLastApplied); } protected Object fromSerializableMessage(Object serializable){ 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 54e0494b9d..610fdc987f 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 @@ -9,17 +9,22 @@ package org.opendaylight.controller.cluster.raft.behaviors; import akka.actor.ActorRef; +import com.google.protobuf.ByteString; 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.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.RequestVoteReply; +import java.util.ArrayList; + /** * The behavior of a RaftActor in the Follower state *

@@ -31,6 +36,8 @@ import org.opendaylight.controller.cluster.raft.messages.RequestVoteReply; * */ public class Follower extends AbstractRaftActorBehavior { + private ByteString snapshotChunksCollected = ByteString.EMPTY; + public Follower(RaftActorContext context) { super(context); @@ -106,6 +113,9 @@ public class Follower extends AbstractRaftActorBehavior { if (outOfSync) { // We found that the log was out of sync so just send a negative // reply and return + context.getLogger().debug("Follower is out-of-sync, " + + "so sending negative reply, lastIndex():{}, lastTerm():{}", + lastIndex(), lastTerm()); sender.tell( new AppendEntriesReply(context.getId(), currentTerm(), false, lastIndex(), lastTerm()), actor() @@ -191,7 +201,13 @@ public class Follower extends AbstractRaftActorBehavior { // If commitIndex > lastApplied: increment lastApplied, apply // log[lastApplied] to state machine (§5.3) - if (appendEntries.getLeaderCommit() > context.getLastApplied()) { + // check if there are any entries to be applied. last-applied can be equal to last-index + if (appendEntries.getLeaderCommit() > context.getLastApplied() && + context.getLastApplied() < lastIndex()) { + context.getLogger().debug("applyLogToStateMachine, " + + "appendEntries.getLeaderCommit():{}," + + "context.getLastApplied():{}, lastIndex():{}", + appendEntries.getLeaderCommit(), context.getLastApplied(), lastIndex()); applyLogToStateMachine(appendEntries.getLeaderCommit()); } @@ -234,7 +250,7 @@ public class Follower extends AbstractRaftActorBehavior { } else if (message instanceof InstallSnapshot) { InstallSnapshot installSnapshot = (InstallSnapshot) message; - actor().tell(new ApplySnapshot(installSnapshot.getData()), actor()); + handleInstallSnapshot(sender, installSnapshot); } scheduleElection(electionDuration()); @@ -242,6 +258,47 @@ public class Follower extends AbstractRaftActorBehavior { return super.handleMessage(sender, message); } + private void handleInstallSnapshot(ActorRef sender, InstallSnapshot installSnapshot) { + context.getLogger().debug("InstallSnapshot received by follower " + + "datasize:{} , Chunk:{}/{}", installSnapshot.getData().size(), + installSnapshot.getChunkIndex(), installSnapshot.getTotalChunks()); + + try { + if (installSnapshot.getChunkIndex() == installSnapshot.getTotalChunks()) { + // this is the last chunk, create a snapshot object and apply + + snapshotChunksCollected = snapshotChunksCollected.concat(installSnapshot.getData()); + context.getLogger().debug("Last chunk received: snapshotChunksCollected.size:{}", + snapshotChunksCollected.size()); + + Snapshot snapshot = Snapshot.create(snapshotChunksCollected.toByteArray(), + new ArrayList(), + installSnapshot.getLastIncludedIndex(), + installSnapshot.getLastIncludedTerm(), + installSnapshot.getLastIncludedIndex(), + installSnapshot.getLastIncludedTerm()); + + actor().tell(new ApplySnapshot(snapshot), actor()); + + } else { + // we have more to go + snapshotChunksCollected = snapshotChunksCollected.concat(installSnapshot.getData()); + context.getLogger().debug("Chunk={},snapshotChunksCollected.size:{}", + installSnapshot.getChunkIndex(), snapshotChunksCollected.size()); + } + + sender.tell(new InstallSnapshotReply( + currentTerm(), context.getId(), installSnapshot.getChunkIndex(), + true), actor()); + + } catch (Exception e) { + context.getLogger().error("Exception in InstallSnapshot of follower", e); + //send reply with success as false. The chunk will be sent again on failure + sender.tell(new InstallSnapshotReply(currentTerm(), context.getId(), + installSnapshot.getChunkIndex(), false), actor()); + } + } + @Override public void close() throws Exception { stopElection(); } 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 234f9db664..90948ffef7 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 @@ -12,6 +12,7 @@ import akka.actor.ActorRef; import akka.actor.ActorSelection; import akka.actor.Cancellable; import com.google.common.base.Preconditions; +import com.google.protobuf.ByteString; import org.opendaylight.controller.cluster.raft.ClientRequestTracker; import org.opendaylight.controller.cluster.raft.ClientRequestTrackerImpl; import org.opendaylight.controller.cluster.raft.FollowerLogInformation; @@ -30,6 +31,7 @@ import org.opendaylight.controller.cluster.raft.messages.RaftRPC; import org.opendaylight.controller.cluster.raft.messages.RequestVoteReply; import scala.concurrent.duration.FiniteDuration; +import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -64,8 +66,9 @@ import java.util.concurrent.atomic.AtomicLong; public class Leader extends AbstractRaftActorBehavior { - private final Map followerToLog = + protected final Map followerToLog = new HashMap(); + protected final Map mapFollowerToSnapshot = new HashMap<>(); private final Set followers; @@ -246,16 +249,48 @@ public class Leader extends AbstractRaftActorBehavior { return super.handleMessage(sender, message); } - private void handleInstallSnapshotReply(InstallSnapshotReply message) { - InstallSnapshotReply reply = message; + private void handleInstallSnapshotReply(InstallSnapshotReply reply) { String followerId = reply.getFollowerId(); - FollowerLogInformation followerLogInformation = - followerToLog.get(followerId); + FollowerToSnapshot followerToSnapshot = + mapFollowerToSnapshot.get(followerId); + + if (followerToSnapshot != null && + followerToSnapshot.getChunkIndex() == reply.getChunkIndex()) { + + if (reply.isSuccess()) { + if(followerToSnapshot.isLastChunk(reply.getChunkIndex())) { + //this was the last chunk reply + context.getLogger().debug("InstallSnapshotReply received, " + + "last chunk received, Chunk:{}. Follower:{} Setting nextIndex:{}", + reply.getChunkIndex(), followerId, + context.getReplicatedLog().getSnapshotIndex() + 1); + + FollowerLogInformation followerLogInformation = + followerToLog.get(followerId); + followerLogInformation.setMatchIndex( + context.getReplicatedLog().getSnapshotIndex()); + followerLogInformation.setNextIndex( + context.getReplicatedLog().getSnapshotIndex() + 1); + mapFollowerToSnapshot.remove(followerId); + context.getLogger().debug("followerToLog.get(followerId).getNextIndex().get()=" + + followerToLog.get(followerId).getNextIndex().get()); + + } else { + followerToSnapshot.markSendStatus(true); + } + } else { + context.getLogger().info("InstallSnapshotReply received, " + + "sending snapshot chunk failed, Will retry, Chunk:{}", + reply.getChunkIndex()); + followerToSnapshot.markSendStatus(false); + } - followerLogInformation - .setMatchIndex(context.getReplicatedLog().getSnapshotIndex()); - followerLogInformation - .setNextIndex(context.getReplicatedLog().getSnapshotIndex() + 1); + } else { + context.getLogger().error("ERROR!!" + + "FollowerId in InstallSnapshotReply not known to Leader" + + " or Chunk Index in InstallSnapshotReply not matching {} != {}", + followerToSnapshot.getChunkIndex(), reply.getChunkIndex() ); + } } private void replicate(Replicate replicate) { @@ -282,30 +317,56 @@ public class Leader extends AbstractRaftActorBehavior { private void sendAppendEntries() { // Send an AppendEntries to all followers for (String followerId : followers) { - ActorSelection followerActor = - context.getPeerActorSelection(followerId); + ActorSelection followerActor = context.getPeerActorSelection(followerId); if (followerActor != null) { - FollowerLogInformation followerLogInformation = - followerToLog.get(followerId); - - long nextIndex = followerLogInformation.getNextIndex().get(); - + FollowerLogInformation followerLogInformation = followerToLog.get(followerId); + long followerNextIndex = followerLogInformation.getNextIndex().get(); List entries = Collections.emptyList(); - if (context.getReplicatedLog().isPresent(nextIndex)) { - // FIXME : Sending one entry at a time - entries = - context.getReplicatedLog().getFrom(nextIndex, 1); + if (mapFollowerToSnapshot.get(followerId) != null) { + if (mapFollowerToSnapshot.get(followerId).canSendNextChunk()) { + sendSnapshotChunk(followerActor, followerId); + } + + } else { + + if (context.getReplicatedLog().isPresent(followerNextIndex)) { + // FIXME : Sending one entry at a time + entries = context.getReplicatedLog().getFrom(followerNextIndex, 1); + + followerActor.tell( + new AppendEntries(currentTerm(), context.getId(), + prevLogIndex(followerNextIndex), + prevLogTerm(followerNextIndex), entries, + context.getCommitIndex()).toSerializable(), + actor() + ); + + } else { + // if the followers next index is not present in the leaders log, then snapshot should be sent + long leaderSnapShotIndex = context.getReplicatedLog().getSnapshotIndex(); + long leaderLastIndex = context.getReplicatedLog().lastIndex(); + if (followerNextIndex >= 0 && leaderLastIndex >= followerNextIndex ) { + // if the follower is just not starting and leader's index + // is more than followers index + context.getLogger().debug("SendInstallSnapshot to follower:{}," + + "follower-nextIndex:{}, leader-snapshot-index:{}, " + + "leader-last-index:{}", followerId, + followerNextIndex, leaderSnapShotIndex, leaderLastIndex); + + actor().tell(new SendInstallSnapshot(), actor()); + } else { + followerActor.tell( + new AppendEntries(currentTerm(), context.getId(), + prevLogIndex(followerNextIndex), + prevLogTerm(followerNextIndex), entries, + context.getCommitIndex()).toSerializable(), + actor() + ); + } + } } - - followerActor.tell( - new AppendEntries(currentTerm(), context.getId(), - prevLogIndex(nextIndex), - prevLogTerm(nextIndex), entries, - context.getCommitIndex()).toSerializable(), - actor() - ); } } } @@ -326,21 +387,55 @@ public class Leader extends AbstractRaftActorBehavior { long nextIndex = followerLogInformation.getNextIndex().get(); - if (!context.getReplicatedLog().isPresent(nextIndex) && context - .getReplicatedLog().isInSnapshot(nextIndex)) { - followerActor.tell( - new InstallSnapshot(currentTerm(), context.getId(), - context.getReplicatedLog().getSnapshotIndex(), - context.getReplicatedLog().getSnapshotTerm(), - context.getReplicatedLog().getSnapshot() - ), - actor() - ); + if (!context.getReplicatedLog().isPresent(nextIndex) && + context.getReplicatedLog().isInSnapshot(nextIndex)) { + sendSnapshotChunk(followerActor, followerId); } } } } + /** + * Sends a snapshot chunk to a given follower + * InstallSnapshot should qualify as a heartbeat too. + */ + private void sendSnapshotChunk(ActorSelection followerActor, String followerId) { + try { + followerActor.tell( + new InstallSnapshot(currentTerm(), context.getId(), + context.getReplicatedLog().getSnapshotIndex(), + context.getReplicatedLog().getSnapshotTerm(), + getNextSnapshotChunk(followerId, + context.getReplicatedLog().getSnapshot()), + mapFollowerToSnapshot.get(followerId).incrementChunkIndex(), + mapFollowerToSnapshot.get(followerId).getTotalChunks() + ).toSerializable(), + actor() + ); + context.getLogger().info("InstallSnapshot sent to follower {}, Chunk: {}/{}", + followerActor.path(), mapFollowerToSnapshot.get(followerId).getChunkIndex(), + mapFollowerToSnapshot.get(followerId).getTotalChunks()); + } catch (IOException e) { + context.getLogger().error("InstallSnapshot failed for Leader.", e); + } + } + + /** + * Acccepts snaphot as ByteString, enters into map for future chunks + * creates and return a ByteString chunk + */ + private ByteString getNextSnapshotChunk(String followerId, ByteString snapshotBytes) throws IOException { + FollowerToSnapshot followerToSnapshot = mapFollowerToSnapshot.get(followerId); + if (followerToSnapshot == null) { + followerToSnapshot = new FollowerToSnapshot(snapshotBytes); + mapFollowerToSnapshot.put(followerId, followerToSnapshot); + } + ByteString nextChunk = followerToSnapshot.getNextChunk(); + context.getLogger().debug("Leader's snapshot nextChunk size:{}", nextChunk.size()); + + return nextChunk; + } + private RaftState sendHeartBeat() { if (followers.size() > 0) { sendAppendEntries(); @@ -410,4 +505,97 @@ public class Leader extends AbstractRaftActorBehavior { return context.getId(); } + /** + * Encapsulates the snapshot bytestring and handles the logic of sending + * snapshot chunks + */ + protected class FollowerToSnapshot { + private ByteString snapshotBytes; + private int offset = 0; + // the next snapshot chunk is sent only if the replyReceivedForOffset matches offset + private int replyReceivedForOffset; + // if replyStatus is false, the previous chunk is attempted + private boolean replyStatus = false; + private int chunkIndex; + private int totalChunks; + + public FollowerToSnapshot(ByteString snapshotBytes) { + this.snapshotBytes = snapshotBytes; + replyReceivedForOffset = -1; + chunkIndex = 1; + int size = snapshotBytes.size(); + totalChunks = ( size / context.getConfigParams().getSnapshotChunkSize()) + + ((size % context.getConfigParams().getSnapshotChunkSize()) > 0 ? 1 : 0); + context.getLogger().debug("Snapshot {} bytes, total chunks to send:{}", + size, totalChunks); + } + + public ByteString getSnapshotBytes() { + return snapshotBytes; + } + + public int incrementOffset() { + if(replyStatus) { + // if prev chunk failed, we would want to sent the same chunk again + offset = offset + context.getConfigParams().getSnapshotChunkSize(); + } + return offset; + } + + public int incrementChunkIndex() { + if (replyStatus) { + // if prev chunk failed, we would want to sent the same chunk again + chunkIndex = chunkIndex + 1; + } + return chunkIndex; + } + + public int getChunkIndex() { + return chunkIndex; + } + + public int getTotalChunks() { + return totalChunks; + } + + public boolean canSendNextChunk() { + // we only send a false if a chunk is sent but we have not received a reply yet + return replyReceivedForOffset == offset; + } + + public boolean isLastChunk(int chunkIndex) { + return totalChunks == chunkIndex; + } + + public void markSendStatus(boolean success) { + if (success) { + // if the chunk sent was successful + replyReceivedForOffset = offset; + replyStatus = true; + } else { + // if the chunk sent was failure + replyReceivedForOffset = offset; + replyStatus = false; + } + } + + public ByteString getNextChunk() { + int snapshotLength = getSnapshotBytes().size(); + int start = incrementOffset(); + int size = context.getConfigParams().getSnapshotChunkSize(); + if (context.getConfigParams().getSnapshotChunkSize() > snapshotLength) { + size = snapshotLength; + } else { + if ((start + context.getConfigParams().getSnapshotChunkSize()) > snapshotLength) { + size = snapshotLength - start; + } + } + + context.getLogger().debug("length={}, offset={},size={}", + snapshotLength, start, size); + return getSnapshotBytes().substring(start, start + size); + + } + } + } 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 888854fa71..9d40fa3d9e 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 @@ -8,19 +8,29 @@ package org.opendaylight.controller.cluster.raft.messages; +import com.google.protobuf.ByteString; +import org.opendaylight.controller.cluster.raft.protobuff.messages.InstallSnapshotMessages; + public class InstallSnapshot extends AbstractRaftRPC { + public static final Class SERIALIZABLE_CLASS = InstallSnapshotMessages.InstallSnapshot.class; + private final String leaderId; private final long lastIncludedIndex; private final long lastIncludedTerm; - private final Object data; + private final ByteString data; + private final int chunkIndex; + private final int totalChunks; - public InstallSnapshot(long term, String leaderId, long lastIncludedIndex, long lastIncludedTerm, Object data) { + public InstallSnapshot(long term, String leaderId, long lastIncludedIndex, + long lastIncludedTerm, ByteString data, int chunkIndex, int totalChunks) { super(term); this.leaderId = leaderId; this.lastIncludedIndex = lastIncludedIndex; this.lastIncludedTerm = lastIncludedTerm; this.data = data; + this.chunkIndex = chunkIndex; + this.totalChunks = totalChunks; } public String getLeaderId() { @@ -35,7 +45,38 @@ public class InstallSnapshot extends AbstractRaftRPC { return lastIncludedTerm; } - public Object getData() { + public ByteString getData() { return data; } + + public int getChunkIndex() { + return chunkIndex; + } + + public int getTotalChunks() { + return totalChunks; + } + + public Object toSerializable(){ + return InstallSnapshotMessages.InstallSnapshot.newBuilder() + .setLeaderId(this.getLeaderId()) + .setChunkIndex(this.getChunkIndex()) + .setData(this.getData()) + .setLastIncludedIndex(this.getLastIncludedIndex()) + .setLastIncludedTerm(this.getLastIncludedTerm()) + .setTotalChunks(this.getTotalChunks()).build(); + + } + + public static InstallSnapshot fromSerializable (Object o) { + InstallSnapshotMessages.InstallSnapshot from = + (InstallSnapshotMessages.InstallSnapshot) o; + + InstallSnapshot installSnapshot = new InstallSnapshot(from.getTerm(), + from.getLeaderId(), from.getLastIncludedIndex(), + from.getLastIncludedTerm(), from.getData(), + from.getChunkIndex(), from.getTotalChunks()); + + return installSnapshot; + } } diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/InstallSnapshotReply.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/InstallSnapshotReply.java index 85b89b70ae..d293a47c8e 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/InstallSnapshotReply.java +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/InstallSnapshotReply.java @@ -13,13 +13,26 @@ public class InstallSnapshotReply extends AbstractRaftRPC { // The followerId - this will be used to figure out which follower is // responding private final String followerId; + private final int chunkIndex; + private boolean success; - protected InstallSnapshotReply(long term, String followerId) { + public InstallSnapshotReply(long term, String followerId, int chunkIndex, + boolean success) { super(term); this.followerId = followerId; + this.chunkIndex = chunkIndex; + this.success = success; } public String getFollowerId() { return followerId; } + + public int getChunkIndex() { + return chunkIndex; + } + + public boolean isSuccess() { + return success; + } } diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/protobuff/messages/InstallSnapshotMessages.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/protobuff/messages/InstallSnapshotMessages.java new file mode 100644 index 0000000000..e801ae1c10 --- /dev/null +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/protobuff/messages/InstallSnapshotMessages.java @@ -0,0 +1,1015 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: InstallSnapshot.proto + +package org.opendaylight.controller.cluster.raft.protobuff.messages; + +public final class InstallSnapshotMessages { + private InstallSnapshotMessages() {} + public static void registerAllExtensions( + com.google.protobuf.ExtensionRegistry registry) { + } + public interface InstallSnapshotOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional int64 term = 1; + /** + * optional int64 term = 1; + */ + boolean hasTerm(); + /** + * optional int64 term = 1; + */ + long getTerm(); + + // optional string leaderId = 2; + /** + * optional string leaderId = 2; + */ + boolean hasLeaderId(); + /** + * optional string leaderId = 2; + */ + java.lang.String getLeaderId(); + /** + * optional string leaderId = 2; + */ + com.google.protobuf.ByteString + getLeaderIdBytes(); + + // optional int64 lastIncludedIndex = 3; + /** + * optional int64 lastIncludedIndex = 3; + */ + boolean hasLastIncludedIndex(); + /** + * optional int64 lastIncludedIndex = 3; + */ + long getLastIncludedIndex(); + + // optional int64 lastIncludedTerm = 4; + /** + * optional int64 lastIncludedTerm = 4; + */ + boolean hasLastIncludedTerm(); + /** + * optional int64 lastIncludedTerm = 4; + */ + long getLastIncludedTerm(); + + // optional bytes data = 5; + /** + * optional bytes data = 5; + */ + boolean hasData(); + /** + * optional bytes data = 5; + */ + com.google.protobuf.ByteString getData(); + + // optional int32 chunkIndex = 6; + /** + * optional int32 chunkIndex = 6; + */ + boolean hasChunkIndex(); + /** + * optional int32 chunkIndex = 6; + */ + int getChunkIndex(); + + // optional int32 totalChunks = 7; + /** + * optional int32 totalChunks = 7; + */ + boolean hasTotalChunks(); + /** + * optional int32 totalChunks = 7; + */ + int getTotalChunks(); + } + /** + * Protobuf type {@code org.opendaylight.controller.cluster.raft.InstallSnapshot} + */ + public static final class InstallSnapshot extends + com.google.protobuf.GeneratedMessage + implements InstallSnapshotOrBuilder { + // Use InstallSnapshot.newBuilder() to construct. + private InstallSnapshot(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private InstallSnapshot(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final InstallSnapshot defaultInstance; + public static InstallSnapshot getDefaultInstance() { + return defaultInstance; + } + + public InstallSnapshot getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private InstallSnapshot( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 8: { + bitField0_ |= 0x00000001; + term_ = input.readInt64(); + break; + } + case 18: { + bitField0_ |= 0x00000002; + leaderId_ = input.readBytes(); + break; + } + case 24: { + bitField0_ |= 0x00000004; + lastIncludedIndex_ = input.readInt64(); + break; + } + case 32: { + bitField0_ |= 0x00000008; + lastIncludedTerm_ = input.readInt64(); + break; + } + case 42: { + bitField0_ |= 0x00000010; + data_ = input.readBytes(); + break; + } + case 48: { + bitField0_ |= 0x00000020; + chunkIndex_ = input.readInt32(); + break; + } + case 56: { + bitField0_ |= 0x00000040; + totalChunks_ = input.readInt32(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.opendaylight.controller.cluster.raft.protobuff.messages.InstallSnapshotMessages.internal_static_org_opendaylight_controller_cluster_raft_InstallSnapshot_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.opendaylight.controller.cluster.raft.protobuff.messages.InstallSnapshotMessages.internal_static_org_opendaylight_controller_cluster_raft_InstallSnapshot_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.opendaylight.controller.cluster.raft.protobuff.messages.InstallSnapshotMessages.InstallSnapshot.class, org.opendaylight.controller.cluster.raft.protobuff.messages.InstallSnapshotMessages.InstallSnapshot.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public InstallSnapshot parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new InstallSnapshot(input, extensionRegistry); + } + }; + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + private int bitField0_; + // optional int64 term = 1; + public static final int TERM_FIELD_NUMBER = 1; + private long term_; + /** + * optional int64 term = 1; + */ + public boolean hasTerm() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional int64 term = 1; + */ + public long getTerm() { + return term_; + } + + // optional string leaderId = 2; + public static final int LEADERID_FIELD_NUMBER = 2; + private java.lang.Object leaderId_; + /** + * optional string leaderId = 2; + */ + public boolean hasLeaderId() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional string leaderId = 2; + */ + public java.lang.String getLeaderId() { + java.lang.Object ref = leaderId_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + leaderId_ = s; + } + return s; + } + } + /** + * optional string leaderId = 2; + */ + public com.google.protobuf.ByteString + getLeaderIdBytes() { + java.lang.Object ref = leaderId_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + leaderId_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + // optional int64 lastIncludedIndex = 3; + public static final int LASTINCLUDEDINDEX_FIELD_NUMBER = 3; + private long lastIncludedIndex_; + /** + * optional int64 lastIncludedIndex = 3; + */ + public boolean hasLastIncludedIndex() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional int64 lastIncludedIndex = 3; + */ + public long getLastIncludedIndex() { + return lastIncludedIndex_; + } + + // optional int64 lastIncludedTerm = 4; + public static final int LASTINCLUDEDTERM_FIELD_NUMBER = 4; + private long lastIncludedTerm_; + /** + * optional int64 lastIncludedTerm = 4; + */ + public boolean hasLastIncludedTerm() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional int64 lastIncludedTerm = 4; + */ + public long getLastIncludedTerm() { + return lastIncludedTerm_; + } + + // optional bytes data = 5; + public static final int DATA_FIELD_NUMBER = 5; + private com.google.protobuf.ByteString data_; + /** + * optional bytes data = 5; + */ + public boolean hasData() { + return ((bitField0_ & 0x00000010) == 0x00000010); + } + /** + * optional bytes data = 5; + */ + public com.google.protobuf.ByteString getData() { + return data_; + } + + // optional int32 chunkIndex = 6; + public static final int CHUNKINDEX_FIELD_NUMBER = 6; + private int chunkIndex_; + /** + * optional int32 chunkIndex = 6; + */ + public boolean hasChunkIndex() { + return ((bitField0_ & 0x00000020) == 0x00000020); + } + /** + * optional int32 chunkIndex = 6; + */ + public int getChunkIndex() { + return chunkIndex_; + } + + // optional int32 totalChunks = 7; + public static final int TOTALCHUNKS_FIELD_NUMBER = 7; + private int totalChunks_; + /** + * optional int32 totalChunks = 7; + */ + public boolean hasTotalChunks() { + return ((bitField0_ & 0x00000040) == 0x00000040); + } + /** + * optional int32 totalChunks = 7; + */ + public int getTotalChunks() { + return totalChunks_; + } + + private void initFields() { + term_ = 0L; + leaderId_ = ""; + lastIncludedIndex_ = 0L; + lastIncludedTerm_ = 0L; + data_ = com.google.protobuf.ByteString.EMPTY; + chunkIndex_ = 0; + totalChunks_ = 0; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeInt64(1, term_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeBytes(2, getLeaderIdBytes()); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeInt64(3, lastIncludedIndex_); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + output.writeInt64(4, lastIncludedTerm_); + } + if (((bitField0_ & 0x00000010) == 0x00000010)) { + output.writeBytes(5, data_); + } + if (((bitField0_ & 0x00000020) == 0x00000020)) { + output.writeInt32(6, chunkIndex_); + } + if (((bitField0_ & 0x00000040) == 0x00000040)) { + output.writeInt32(7, totalChunks_); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeInt64Size(1, term_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(2, getLeaderIdBytes()); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.CodedOutputStream + .computeInt64Size(3, lastIncludedIndex_); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + size += com.google.protobuf.CodedOutputStream + .computeInt64Size(4, lastIncludedTerm_); + } + if (((bitField0_ & 0x00000010) == 0x00000010)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(5, data_); + } + if (((bitField0_ & 0x00000020) == 0x00000020)) { + size += com.google.protobuf.CodedOutputStream + .computeInt32Size(6, chunkIndex_); + } + if (((bitField0_ & 0x00000040) == 0x00000040)) { + size += com.google.protobuf.CodedOutputStream + .computeInt32Size(7, totalChunks_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @java.lang.Override + protected java.lang.Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.opendaylight.controller.cluster.raft.protobuff.messages.InstallSnapshotMessages.InstallSnapshot parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.opendaylight.controller.cluster.raft.protobuff.messages.InstallSnapshotMessages.InstallSnapshot parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.opendaylight.controller.cluster.raft.protobuff.messages.InstallSnapshotMessages.InstallSnapshot parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.opendaylight.controller.cluster.raft.protobuff.messages.InstallSnapshotMessages.InstallSnapshot parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.opendaylight.controller.cluster.raft.protobuff.messages.InstallSnapshotMessages.InstallSnapshot parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.opendaylight.controller.cluster.raft.protobuff.messages.InstallSnapshotMessages.InstallSnapshot parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.opendaylight.controller.cluster.raft.protobuff.messages.InstallSnapshotMessages.InstallSnapshot parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.opendaylight.controller.cluster.raft.protobuff.messages.InstallSnapshotMessages.InstallSnapshot parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.opendaylight.controller.cluster.raft.protobuff.messages.InstallSnapshotMessages.InstallSnapshot parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.opendaylight.controller.cluster.raft.protobuff.messages.InstallSnapshotMessages.InstallSnapshot parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.opendaylight.controller.cluster.raft.protobuff.messages.InstallSnapshotMessages.InstallSnapshot prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code org.opendaylight.controller.cluster.raft.InstallSnapshot} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.opendaylight.controller.cluster.raft.protobuff.messages.InstallSnapshotMessages.InstallSnapshotOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.opendaylight.controller.cluster.raft.protobuff.messages.InstallSnapshotMessages.internal_static_org_opendaylight_controller_cluster_raft_InstallSnapshot_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.opendaylight.controller.cluster.raft.protobuff.messages.InstallSnapshotMessages.internal_static_org_opendaylight_controller_cluster_raft_InstallSnapshot_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.opendaylight.controller.cluster.raft.protobuff.messages.InstallSnapshotMessages.InstallSnapshot.class, org.opendaylight.controller.cluster.raft.protobuff.messages.InstallSnapshotMessages.InstallSnapshot.Builder.class); + } + + // Construct using org.opendaylight.controller.cluster.raft.protobuff.messages.InstallSnapshotMessages.InstallSnapshot.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + term_ = 0L; + bitField0_ = (bitField0_ & ~0x00000001); + leaderId_ = ""; + bitField0_ = (bitField0_ & ~0x00000002); + lastIncludedIndex_ = 0L; + bitField0_ = (bitField0_ & ~0x00000004); + lastIncludedTerm_ = 0L; + bitField0_ = (bitField0_ & ~0x00000008); + data_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000010); + chunkIndex_ = 0; + bitField0_ = (bitField0_ & ~0x00000020); + totalChunks_ = 0; + bitField0_ = (bitField0_ & ~0x00000040); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.opendaylight.controller.cluster.raft.protobuff.messages.InstallSnapshotMessages.internal_static_org_opendaylight_controller_cluster_raft_InstallSnapshot_descriptor; + } + + public org.opendaylight.controller.cluster.raft.protobuff.messages.InstallSnapshotMessages.InstallSnapshot getDefaultInstanceForType() { + return org.opendaylight.controller.cluster.raft.protobuff.messages.InstallSnapshotMessages.InstallSnapshot.getDefaultInstance(); + } + + public org.opendaylight.controller.cluster.raft.protobuff.messages.InstallSnapshotMessages.InstallSnapshot build() { + org.opendaylight.controller.cluster.raft.protobuff.messages.InstallSnapshotMessages.InstallSnapshot result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.opendaylight.controller.cluster.raft.protobuff.messages.InstallSnapshotMessages.InstallSnapshot buildPartial() { + org.opendaylight.controller.cluster.raft.protobuff.messages.InstallSnapshotMessages.InstallSnapshot result = new org.opendaylight.controller.cluster.raft.protobuff.messages.InstallSnapshotMessages.InstallSnapshot(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.term_ = term_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.leaderId_ = leaderId_; + if (((from_bitField0_ & 0x00000004) == 0x00000004)) { + to_bitField0_ |= 0x00000004; + } + result.lastIncludedIndex_ = lastIncludedIndex_; + if (((from_bitField0_ & 0x00000008) == 0x00000008)) { + to_bitField0_ |= 0x00000008; + } + result.lastIncludedTerm_ = lastIncludedTerm_; + if (((from_bitField0_ & 0x00000010) == 0x00000010)) { + to_bitField0_ |= 0x00000010; + } + result.data_ = data_; + if (((from_bitField0_ & 0x00000020) == 0x00000020)) { + to_bitField0_ |= 0x00000020; + } + result.chunkIndex_ = chunkIndex_; + if (((from_bitField0_ & 0x00000040) == 0x00000040)) { + to_bitField0_ |= 0x00000040; + } + result.totalChunks_ = totalChunks_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.opendaylight.controller.cluster.raft.protobuff.messages.InstallSnapshotMessages.InstallSnapshot) { + return mergeFrom((org.opendaylight.controller.cluster.raft.protobuff.messages.InstallSnapshotMessages.InstallSnapshot)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.opendaylight.controller.cluster.raft.protobuff.messages.InstallSnapshotMessages.InstallSnapshot other) { + if (other == org.opendaylight.controller.cluster.raft.protobuff.messages.InstallSnapshotMessages.InstallSnapshot.getDefaultInstance()) return this; + if (other.hasTerm()) { + setTerm(other.getTerm()); + } + if (other.hasLeaderId()) { + bitField0_ |= 0x00000002; + leaderId_ = other.leaderId_; + onChanged(); + } + if (other.hasLastIncludedIndex()) { + setLastIncludedIndex(other.getLastIncludedIndex()); + } + if (other.hasLastIncludedTerm()) { + setLastIncludedTerm(other.getLastIncludedTerm()); + } + if (other.hasData()) { + setData(other.getData()); + } + if (other.hasChunkIndex()) { + setChunkIndex(other.getChunkIndex()); + } + if (other.hasTotalChunks()) { + setTotalChunks(other.getTotalChunks()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.opendaylight.controller.cluster.raft.protobuff.messages.InstallSnapshotMessages.InstallSnapshot parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.opendaylight.controller.cluster.raft.protobuff.messages.InstallSnapshotMessages.InstallSnapshot) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional int64 term = 1; + private long term_ ; + /** + * optional int64 term = 1; + */ + public boolean hasTerm() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional int64 term = 1; + */ + public long getTerm() { + return term_; + } + /** + * optional int64 term = 1; + */ + public Builder setTerm(long value) { + bitField0_ |= 0x00000001; + term_ = value; + onChanged(); + return this; + } + /** + * optional int64 term = 1; + */ + public Builder clearTerm() { + bitField0_ = (bitField0_ & ~0x00000001); + term_ = 0L; + onChanged(); + return this; + } + + // optional string leaderId = 2; + private java.lang.Object leaderId_ = ""; + /** + * optional string leaderId = 2; + */ + public boolean hasLeaderId() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional string leaderId = 2; + */ + public java.lang.String getLeaderId() { + java.lang.Object ref = leaderId_; + if (!(ref instanceof java.lang.String)) { + java.lang.String s = ((com.google.protobuf.ByteString) ref) + .toStringUtf8(); + leaderId_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * optional string leaderId = 2; + */ + public com.google.protobuf.ByteString + getLeaderIdBytes() { + java.lang.Object ref = leaderId_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + leaderId_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string leaderId = 2; + */ + public Builder setLeaderId( + java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + leaderId_ = value; + onChanged(); + return this; + } + /** + * optional string leaderId = 2; + */ + public Builder clearLeaderId() { + bitField0_ = (bitField0_ & ~0x00000002); + leaderId_ = getDefaultInstance().getLeaderId(); + onChanged(); + return this; + } + /** + * optional string leaderId = 2; + */ + public Builder setLeaderIdBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + leaderId_ = value; + onChanged(); + return this; + } + + // optional int64 lastIncludedIndex = 3; + private long lastIncludedIndex_ ; + /** + * optional int64 lastIncludedIndex = 3; + */ + public boolean hasLastIncludedIndex() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional int64 lastIncludedIndex = 3; + */ + public long getLastIncludedIndex() { + return lastIncludedIndex_; + } + /** + * optional int64 lastIncludedIndex = 3; + */ + public Builder setLastIncludedIndex(long value) { + bitField0_ |= 0x00000004; + lastIncludedIndex_ = value; + onChanged(); + return this; + } + /** + * optional int64 lastIncludedIndex = 3; + */ + public Builder clearLastIncludedIndex() { + bitField0_ = (bitField0_ & ~0x00000004); + lastIncludedIndex_ = 0L; + onChanged(); + return this; + } + + // optional int64 lastIncludedTerm = 4; + private long lastIncludedTerm_ ; + /** + * optional int64 lastIncludedTerm = 4; + */ + public boolean hasLastIncludedTerm() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional int64 lastIncludedTerm = 4; + */ + public long getLastIncludedTerm() { + return lastIncludedTerm_; + } + /** + * optional int64 lastIncludedTerm = 4; + */ + public Builder setLastIncludedTerm(long value) { + bitField0_ |= 0x00000008; + lastIncludedTerm_ = value; + onChanged(); + return this; + } + /** + * optional int64 lastIncludedTerm = 4; + */ + public Builder clearLastIncludedTerm() { + bitField0_ = (bitField0_ & ~0x00000008); + lastIncludedTerm_ = 0L; + onChanged(); + return this; + } + + // optional bytes data = 5; + private com.google.protobuf.ByteString data_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes data = 5; + */ + public boolean hasData() { + return ((bitField0_ & 0x00000010) == 0x00000010); + } + /** + * optional bytes data = 5; + */ + public com.google.protobuf.ByteString getData() { + return data_; + } + /** + * optional bytes data = 5; + */ + public Builder setData(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000010; + data_ = value; + onChanged(); + return this; + } + /** + * optional bytes data = 5; + */ + public Builder clearData() { + bitField0_ = (bitField0_ & ~0x00000010); + data_ = getDefaultInstance().getData(); + onChanged(); + return this; + } + + // optional int32 chunkIndex = 6; + private int chunkIndex_ ; + /** + * optional int32 chunkIndex = 6; + */ + public boolean hasChunkIndex() { + return ((bitField0_ & 0x00000020) == 0x00000020); + } + /** + * optional int32 chunkIndex = 6; + */ + public int getChunkIndex() { + return chunkIndex_; + } + /** + * optional int32 chunkIndex = 6; + */ + public Builder setChunkIndex(int value) { + bitField0_ |= 0x00000020; + chunkIndex_ = value; + onChanged(); + return this; + } + /** + * optional int32 chunkIndex = 6; + */ + public Builder clearChunkIndex() { + bitField0_ = (bitField0_ & ~0x00000020); + chunkIndex_ = 0; + onChanged(); + return this; + } + + // optional int32 totalChunks = 7; + private int totalChunks_ ; + /** + * optional int32 totalChunks = 7; + */ + public boolean hasTotalChunks() { + return ((bitField0_ & 0x00000040) == 0x00000040); + } + /** + * optional int32 totalChunks = 7; + */ + public int getTotalChunks() { + return totalChunks_; + } + /** + * optional int32 totalChunks = 7; + */ + public Builder setTotalChunks(int value) { + bitField0_ |= 0x00000040; + totalChunks_ = value; + onChanged(); + return this; + } + /** + * optional int32 totalChunks = 7; + */ + public Builder clearTotalChunks() { + bitField0_ = (bitField0_ & ~0x00000040); + totalChunks_ = 0; + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:org.opendaylight.controller.cluster.raft.InstallSnapshot) + } + + static { + defaultInstance = new InstallSnapshot(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:org.opendaylight.controller.cluster.raft.InstallSnapshot) + } + + private static com.google.protobuf.Descriptors.Descriptor + internal_static_org_opendaylight_controller_cluster_raft_InstallSnapshot_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_org_opendaylight_controller_cluster_raft_InstallSnapshot_fieldAccessorTable; + + public static com.google.protobuf.Descriptors.FileDescriptor + getDescriptor() { + return descriptor; + } + private static com.google.protobuf.Descriptors.FileDescriptor + descriptor; + static { + java.lang.String[] descriptorData = { + "\n\025InstallSnapshot.proto\022(org.opendayligh" + + "t.controller.cluster.raft\"\235\001\n\017InstallSna" + + "pshot\022\014\n\004term\030\001 \001(\003\022\020\n\010leaderId\030\002 \001(\t\022\031\n" + + "\021lastIncludedIndex\030\003 \001(\003\022\030\n\020lastIncluded" + + "Term\030\004 \001(\003\022\014\n\004data\030\005 \001(\014\022\022\n\nchunkIndex\030\006" + + " \001(\005\022\023\n\013totalChunks\030\007 \001(\005BX\n;org.openday" + + "light.controller.cluster.raft.protobuff." + + "messagesB\027InstallSnapshotMessagesH\001" + }; + com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner = + new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() { + public com.google.protobuf.ExtensionRegistry assignDescriptors( + com.google.protobuf.Descriptors.FileDescriptor root) { + descriptor = root; + internal_static_org_opendaylight_controller_cluster_raft_InstallSnapshot_descriptor = + getDescriptor().getMessageTypes().get(0); + internal_static_org_opendaylight_controller_cluster_raft_InstallSnapshot_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_org_opendaylight_controller_cluster_raft_InstallSnapshot_descriptor, + new java.lang.String[] { "Term", "LeaderId", "LastIncludedIndex", "LastIncludedTerm", "Data", "ChunkIndex", "TotalChunks", }); + return null; + } + }; + com.google.protobuf.Descriptors.FileDescriptor + .internalBuildGeneratedFileFrom(descriptorData, + new com.google.protobuf.Descriptors.FileDescriptor[] { + }, assigner); + } + + // @@protoc_insertion_point(outer_class_scope) +} diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/resources/InstallSnapshot.proto b/opendaylight/md-sal/sal-akka-raft/src/main/resources/InstallSnapshot.proto new file mode 100644 index 0000000000..14f821b5e2 --- /dev/null +++ b/opendaylight/md-sal/sal-akka-raft/src/main/resources/InstallSnapshot.proto @@ -0,0 +1,15 @@ +package org.opendaylight.controller.cluster.raft; + +option java_package = "org.opendaylight.controller.cluster.raft.protobuff.messages"; +option java_outer_classname = "InstallSnapshotMessages"; +option optimize_for = SPEED; + +message InstallSnapshot { + optional int64 term = 1; + optional string leaderId = 2; + optional int64 lastIncludedIndex = 3; + optional int64 lastIncludedTerm = 4; + optional bytes data = 5; + optional int32 chunkIndex = 6; + optional int32 totalChunks = 7; +} 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 ea3c9e759d..ca34a34ca4 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 @@ -14,17 +14,14 @@ import akka.actor.ActorSystem; import akka.actor.Props; import akka.event.Logging; import akka.event.LoggingAdapter; +import com.google.common.base.Preconditions; import com.google.protobuf.GeneratedMessage; import org.opendaylight.controller.cluster.raft.protobuff.client.messages.Payload; import org.opendaylight.controller.protobuff.messages.cluster.raft.AppendEntriesMessages; import org.opendaylight.controller.protobuff.messages.cluster.raft.test.MockPayloadMessages; -import com.google.common.base.Preconditions; import java.io.Serializable; -import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; -import java.util.List; import java.util.Map; public class MockRaftActorContext implements RaftActorContext { @@ -37,6 +34,7 @@ public class MockRaftActorContext implements RaftActorContext { private final ElectionTerm electionTerm; private ReplicatedLog replicatedLog; private Map peerAddresses = new HashMap(); + private ConfigParams configParams; public MockRaftActorContext(){ electionTerm = null; @@ -79,6 +77,8 @@ public class MockRaftActorContext implements RaftActorContext { } }; + configParams = new DefaultConfigParamsImpl(); + initReplicatedLog(); } @@ -179,118 +179,21 @@ public class MockRaftActorContext implements RaftActorContext { @Override public ConfigParams getConfigParams() { - return new DefaultConfigParamsImpl(); + return configParams; } - public static class SimpleReplicatedLog implements ReplicatedLog { - private final List log = new ArrayList<>(); - - @Override public ReplicatedLogEntry get(long index) { - if(index >= log.size() || index < 0){ - return null; - } - return log.get((int) index); - } - - @Override public ReplicatedLogEntry last() { - if(log.size() == 0){ - return null; - } - return log.get(log.size()-1); - } - - @Override public long lastIndex() { - if(log.size() == 0){ - return -1; - } - - return last().getIndex(); - } - - @Override public long lastTerm() { - if(log.size() == 0){ - return -1; - } - - return last().getTerm(); - } - - @Override public void removeFrom(long index) { - if(index >= log.size() || index < 0){ - return; - } - - log.subList((int) index, log.size()).clear(); - //log.remove((int) index); - } - - @Override public void removeFromAndPersist(long index) { - removeFrom(index); - } - - @Override public void append(ReplicatedLogEntry replicatedLogEntry) { - log.add(replicatedLogEntry); - } + public void setConfigParams(ConfigParams configParams) { + this.configParams = configParams; + } + public static class SimpleReplicatedLog extends AbstractReplicatedLogImpl { @Override public void appendAndPersist( ReplicatedLogEntry replicatedLogEntry) { append(replicatedLogEntry); } - @Override public List getFrom(long index) { - if(index >= log.size() || index < 0){ - return Collections.EMPTY_LIST; - } - List entries = new ArrayList<>(); - for(int i=(int) index ; i < log.size() ; i++) { - entries.add(get(i)); - } - return entries; - } - - @Override public List getFrom(long index, int max) { - if(index >= log.size() || index < 0){ - return Collections.EMPTY_LIST; - } - List entries = new ArrayList<>(); - int maxIndex = (int) index + max; - if(maxIndex > log.size()){ - maxIndex = log.size(); - } - - for(int i=(int) index ; i < maxIndex ; i++) { - entries.add(get(i)); - } - return entries; - - } - - @Override public long size() { - return log.size(); - } - - @Override public boolean isPresent(long index) { - if(index >= log.size() || index < 0){ - return false; - } - - return true; - } - - @Override public boolean isInSnapshot(long index) { - return false; - } - - @Override public Object getSnapshot() { - return null; - } - - @Override public long getSnapshotIndex() { - return -1; - } - - @Override public long getSnapshotTerm() { - return -1; + @Override public void removeFromAndPersist(long index) { + removeFrom(index); } } 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 ff0ffeb271..12123db129 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 @@ -6,6 +6,7 @@ import akka.actor.Props; import akka.event.Logging; import akka.japi.Creator; import akka.testkit.JavaTestKit; +import com.google.protobuf.ByteString; import org.junit.Test; import org.opendaylight.controller.cluster.raft.client.messages.FindLeader; import org.opendaylight.controller.cluster.raft.client.messages.FindLeaderReply; @@ -39,11 +40,11 @@ public class RaftActorTest extends AbstractActorTest { Object data) { } - @Override protected Object createSnapshot() { + @Override protected void createSnapshot() { throw new UnsupportedOperationException("createSnapshot"); } - @Override protected void applySnapshot(Object snapshot) { + @Override protected void applySnapshot(ByteString snapshot) { throw new UnsupportedOperationException("applySnapshot"); } 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 c5a81aa1c9..227d1effa7 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 @@ -158,17 +158,18 @@ public class FollowerTest extends AbstractRaftActorBehaviorTest { createActorContext(); context.setLastApplied(100); - setLastLogEntry((MockRaftActorContext) context, 0, 0, new MockRaftActorContext.MockPayload("")); + setLastLogEntry((MockRaftActorContext) context, 1, 100, new MockRaftActorContext.MockPayload("")); + ((MockRaftActorContext) context).getReplicatedLog().setSnapshotIndex(99); List entries = Arrays.asList( - (ReplicatedLogEntry) new MockRaftActorContext.MockReplicatedLogEntry(100, 101, + (ReplicatedLogEntry) new MockRaftActorContext.MockReplicatedLogEntry(2, 101, new MockRaftActorContext.MockPayload("foo")) ); // The new commitIndex is 101 AppendEntries appendEntries = - new AppendEntries(100, "leader-1", 0, 0, entries, 101); + new AppendEntries(2, "leader-1", 100, 1, entries, 101); RaftState raftState = createBehavior(context).handleMessage(getRef(), appendEntries); 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 17c22a134a..73c9f96b82 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 @@ -1,24 +1,40 @@ package org.opendaylight.controller.cluster.raft.behaviors; import akka.actor.ActorRef; +import akka.actor.ActorSystem; import akka.actor.Props; import akka.testkit.JavaTestKit; -import junit.framework.Assert; +import com.google.protobuf.ByteString; +import org.junit.Assert; import org.junit.Test; +import org.opendaylight.controller.cluster.raft.DefaultConfigParamsImpl; +import org.opendaylight.controller.cluster.raft.FollowerLogInformation; +import org.opendaylight.controller.cluster.raft.FollowerLogInformationImpl; 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.ReplicatedLogImplEntry; +import org.opendaylight.controller.cluster.raft.SerializationUtils; import org.opendaylight.controller.cluster.raft.base.messages.ApplyState; 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.messages.AppendEntries; +import org.opendaylight.controller.cluster.raft.messages.InstallSnapshot; +import org.opendaylight.controller.cluster.raft.messages.InstallSnapshotReply; +import org.opendaylight.controller.cluster.raft.protobuff.messages.InstallSnapshotMessages; import org.opendaylight.controller.cluster.raft.utils.DoNothingActor; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectOutputStream; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.atomic.AtomicLong; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; public class LeaderTest extends AbstractRaftActorBehaviorTest { @@ -82,8 +98,6 @@ public class LeaderTest extends AbstractRaftActorBehaviorTest { assertEquals("match", out); } - - }; }}; } @@ -194,18 +208,372 @@ public class LeaderTest extends AbstractRaftActorBehaviorTest { assertEquals("match", out); } + }; + }}; + } + + @Test + public void testSendInstallSnapshot() { + new LeaderTestKit(getSystem()) {{ + + new Within(duration("1 seconds")) { + protected void run() { + ActorRef followerActor = getTestActor(); + + Map peerAddresses = new HashMap(); + peerAddresses.put(followerActor.path().toString(), + followerActor.path().toString()); + + + MockRaftActorContext actorContext = + (MockRaftActorContext) createActorContext(getRef()); + 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().setSnapshot( + toByteString(leadersSnapshot)); + actorContext.getReplicatedLog().setSnapshotIndex(snapshotIndex); + actorContext.getReplicatedLog().setSnapshotTerm(snapshotTerm); + + MockLeader leader = new MockLeader(actorContext); + // set the follower info in leader + leader.addToFollowerToLog(followerActor.path().toString(), followersLastIndex, -1); + + // new entry + ReplicatedLogImplEntry entry = + new ReplicatedLogImplEntry(newEntryIndex, currentTerm, + new MockRaftActorContext.MockPayload("D")); + + // this should invoke a sendinstallsnapshot as followersLastIndex < snapshotIndex + RaftState raftState = leader.handleMessage( + senderActor, new Replicate(null, "state-id", entry)); + + assertEquals(RaftState.Leader, raftState); + + // we might receive some heartbeat messages, so wait till we SendInstallSnapshot + Boolean[] matches = new ReceiveWhile(Boolean.class, duration("2 seconds")) { + @Override + protected Boolean match(Object o) throws Exception { + if (o instanceof SendInstallSnapshot) { + return true; + } + return false; + } + }.get(); + + boolean sendInstallSnapshotReceived = false; + for (Boolean b: matches) { + sendInstallSnapshotReceived = b | sendInstallSnapshotReceived; + } + + assertTrue(sendInstallSnapshotReceived); + + } + }; + }}; + } + + @Test + public void testInstallSnapshot() { + new LeaderTestKit(getSystem()) {{ + + new Within(duration("1 seconds")) { + protected void run() { + ActorRef followerActor = getTestActor(); + + Map peerAddresses = new HashMap(); + peerAddresses.put(followerActor.path().toString(), + followerActor.path().toString()); + + MockRaftActorContext actorContext = + (MockRaftActorContext) createActorContext(); + 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().setSnapshot(toByteString(leadersSnapshot)); + actorContext.getReplicatedLog().setSnapshotIndex(snapshotIndex); + actorContext.getReplicatedLog().setSnapshotTerm(snapshotTerm); + + actorContext.getTermInformation().update(currentTerm, leaderActor.path().toString()); + + MockLeader leader = new MockLeader(actorContext); + // set the follower info in leader + leader.addToFollowerToLog(followerActor.path().toString(), followersLastIndex, -1); + + // new entry + ReplicatedLogImplEntry entry = + new ReplicatedLogImplEntry(newEntryIndex, currentTerm, + new MockRaftActorContext.MockPayload("D")); + + + RaftState raftState = leader.handleMessage(senderActor, new SendInstallSnapshot()); + + assertEquals(RaftState.Leader, raftState); + + // 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 + 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); + } + }; + }}; + } + + @Test + public void testHandleInstallSnapshotReplyLastChunk() { + new LeaderTestKit(getSystem()) {{ + new Within(duration("1 seconds")) { + protected void run() { + ActorRef followerActor = getTestActor(); + + Map peerAddresses = new HashMap(); + peerAddresses.put(followerActor.path().toString(), + followerActor.path().toString()); + + MockRaftActorContext actorContext = + (MockRaftActorContext) createActorContext(); + actorContext.setPeerAddresses(peerAddresses); + + final int followersLastIndex = 2; + final int snapshotIndex = 3; + final int newEntryIndex = 4; + final int snapshotTerm = 1; + final int currentTerm = 2; + + MockLeader leader = new MockLeader(actorContext); + // set the follower info in leader + leader.addToFollowerToLog(followerActor.path().toString(), followersLastIndex, -1); + + Map leadersSnapshot = new HashMap<>(); + leadersSnapshot.put("1", "A"); + leadersSnapshot.put("2", "B"); + leadersSnapshot.put("3", "C"); + + // set the snapshot variables in replicatedlog + actorContext.getReplicatedLog().setSnapshot( + toByteString(leadersSnapshot)); + actorContext.getReplicatedLog().setSnapshotIndex(snapshotIndex); + actorContext.getReplicatedLog().setSnapshotTerm(snapshotTerm); + actorContext.getTermInformation().update(currentTerm, leaderActor.path().toString()); + + ByteString bs = toByteString(leadersSnapshot); + leader.createFollowerToSnapshot(followerActor.path().toString(), bs); + while(!leader.getFollowerToSnapshot().isLastChunk(leader.getFollowerToSnapshot().getChunkIndex())) { + leader.getFollowerToSnapshot().getNextChunk(); + leader.getFollowerToSnapshot().incrementChunkIndex(); + } + + //clears leaders log + actorContext.getReplicatedLog().removeFrom(0); + RaftState raftState = leader.handleMessage(senderActor, + new InstallSnapshotReply(currentTerm, followerActor.path().toString(), + leader.getFollowerToSnapshot().getChunkIndex(), true)); + assertEquals(RaftState.Leader, raftState); + + assertEquals(leader.mapFollowerToSnapshot.size(), 0); + assertEquals(leader.followerToLog.size(), 1); + assertNotNull(leader.followerToLog.get(followerActor.path().toString())); + FollowerLogInformation fli = leader.followerToLog.get(followerActor.path().toString()); + assertEquals(snapshotIndex, fli.getMatchIndex().get()); + assertEquals(snapshotIndex, fli.getMatchIndex().get()); + assertEquals(snapshotIndex + 1, fli.getNextIndex().get()); + } }; }}; } + @Test + public void testFollowerToSnapshotLogic() { + + MockRaftActorContext actorContext = (MockRaftActorContext) createActorContext(); + + actorContext.setConfigParams(new DefaultConfigParamsImpl() { + @Override + public int getSnapshotChunkSize() { + return 50; + } + }); + + MockLeader leader = new MockLeader(actorContext); + + Map leadersSnapshot = new HashMap<>(); + leadersSnapshot.put("1", "A"); + leadersSnapshot.put("2", "B"); + leadersSnapshot.put("3", "C"); + + ByteString bs = toByteString(leadersSnapshot); + byte[] barray = bs.toByteArray(); + + leader.createFollowerToSnapshot("followerId", bs); + assertEquals(bs.size(), barray.length); + + int chunkIndex=0; + for (int i=0; i < barray.length; i = i + 50) { + int j = i + 50; + chunkIndex++; + + if (i + 50 > barray.length) { + j = barray.length; + } + + ByteString chunk = leader.getFollowerToSnapshot().getNextChunk(); + assertEquals("bytestring size not matching for chunk:"+ chunkIndex, j-i, chunk.size()); + assertEquals("chunkindex not matching", chunkIndex, leader.getFollowerToSnapshot().getChunkIndex()); + + leader.getFollowerToSnapshot().markSendStatus(true); + if (!leader.getFollowerToSnapshot().isLastChunk(chunkIndex)) { + leader.getFollowerToSnapshot().incrementChunkIndex(); + } + } + + assertEquals("totalChunks not matching", chunkIndex, leader.getFollowerToSnapshot().getTotalChunks()); + } + + @Override protected RaftActorBehavior createBehavior( RaftActorContext actorContext) { return new Leader(actorContext); } @Override protected RaftActorContext createActorContext() { - return new MockRaftActorContext("test", getSystem(), leaderActor); + return createActorContext(leaderActor); + } + + protected RaftActorContext createActorContext(ActorRef actorRef) { + return new MockRaftActorContext("test", getSystem(), actorRef); + } + + 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; + } + + private static class LeaderTestKit extends JavaTestKit { + + private LeaderTestKit(ActorSystem actorSystem) { + super(actorSystem); + } + + protected void waitForLogMessage(final Class logLevel, ActorRef subject, String logMessage){ + // Wait for a specific log message to show up + final boolean result = + new JavaTestKit.EventFilter(logLevel + ) { + @Override + protected Boolean run() { + return true; + } + }.from(subject.path().toString()) + .message(logMessage) + .occurrences(1).exec(); + + Assert.assertEquals(true, result); + + } + } + + class MockLeader extends Leader { + + FollowerToSnapshot fts; + + public MockLeader(RaftActorContext context){ + super(context); + } + + public void addToFollowerToLog(String followerId, long nextIndex, long matchIndex) { + FollowerLogInformation followerLogInformation = + new FollowerLogInformationImpl(followerId, + new AtomicLong(nextIndex), + new AtomicLong(matchIndex)); + followerToLog.put(followerId, followerLogInformation); + } + + public FollowerToSnapshot getFollowerToSnapshot() { + return fts; + } + + public void createFollowerToSnapshot(String followerId, ByteString bs ) { + fts = new FollowerToSnapshot(bs); + mapFollowerToSnapshot.put(followerId, fts); + + } } } diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/AbstractForwardedDataBroker.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/AbstractForwardedDataBroker.java index 53f96e44f4..d4b1d84aa7 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/AbstractForwardedDataBroker.java +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/AbstractForwardedDataBroker.java @@ -145,6 +145,11 @@ public abstract class AbstractForwardedDataBroker implements Delegator> change) { bindingDataChangeListener.onDataChanged(new TranslatedDataChangeEvent(change, path)); } + + @Override + public String toString() { + return bindingDataChangeListener.getClass().getName(); + } } private class TranslatedDataChangeEvent implements AsyncDataChangeEvent, DataObject> { diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingTranslatedTransactionChain.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingTranslatedTransactionChain.java index 2d8e51cff9..d16170ba48 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingTranslatedTransactionChain.java +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingTranslatedTransactionChain.java @@ -7,11 +7,10 @@ */ package org.opendaylight.controller.md.sal.binding.impl; -import java.util.Map; -import java.util.WeakHashMap; - -import javax.annotation.concurrent.GuardedBy; - +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.BindingTransactionChain; import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction; import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction; @@ -19,6 +18,7 @@ import org.opendaylight.controller.md.sal.binding.api.WriteTransaction; import org.opendaylight.controller.md.sal.common.api.data.AsyncTransaction; import org.opendaylight.controller.md.sal.common.api.data.TransactionChain; import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener; +import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException; import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker; import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction; import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction; @@ -26,20 +26,19 @@ import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction; import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain; import org.opendaylight.yangtools.concepts.Delegator; -import com.google.common.base.Preconditions; - class BindingTranslatedTransactionChain implements BindingTransactionChain, Delegator { private final DOMTransactionChain delegate; - - @GuardedBy("this") - private final Map, AsyncTransaction> delegateTxToBindingTx = new WeakHashMap<>(); private final BindingToNormalizedNodeCodec codec; + private final DelegateChainListener delegatingListener; + private final TransactionChainListener listener; public BindingTranslatedTransactionChain(final DOMDataBroker chainFactory, final BindingToNormalizedNodeCodec codec, final TransactionChainListener listener) { Preconditions.checkNotNull(chainFactory, "DOM Transaction chain factory must not be null"); - this.delegate = chainFactory.createTransactionChain(new ListenerInvoker(listener)); + this.delegatingListener = new DelegateChainListener(); + this.listener = listener; + this.delegate = chainFactory.createTransactionChain(listener); this.codec = codec; } @@ -52,56 +51,79 @@ class BindingTranslatedTransactionChain implements BindingTransactionChain, Dele public ReadOnlyTransaction newReadOnlyTransaction() { DOMDataReadOnlyTransaction delegateTx = delegate.newReadOnlyTransaction(); ReadOnlyTransaction bindingTx = new BindingDataReadTransactionImpl(delegateTx, codec); - putDelegateToBinding(delegateTx, bindingTx); return bindingTx; } @Override public ReadWriteTransaction newReadWriteTransaction() { DOMDataReadWriteTransaction delegateTx = delegate.newReadWriteTransaction(); - ReadWriteTransaction bindingTx = new BindingDataReadWriteTransactionImpl(delegateTx, codec); - putDelegateToBinding(delegateTx, bindingTx); + ReadWriteTransaction bindingTx = new BindingDataReadWriteTransactionImpl(delegateTx, codec) { + + @Override + public CheckedFuture submit() { + return listenForFailure(this,super.submit()); + } + + }; return bindingTx; } @Override public WriteTransaction newWriteOnlyTransaction() { - DOMDataWriteTransaction delegateTx = delegate.newWriteOnlyTransaction(); - WriteTransaction bindingTx = new BindingDataWriteTransactionImpl<>(delegateTx, codec); - putDelegateToBinding(delegateTx, bindingTx); + final DOMDataWriteTransaction delegateTx = delegate.newWriteOnlyTransaction(); + WriteTransaction bindingTx = new BindingDataWriteTransactionImpl(delegateTx, codec) { + + @Override + public CheckedFuture submit() { + return listenForFailure(this,super.submit()); + }; + + }; return bindingTx; } - @Override - public void close() { - delegate.close(); + protected CheckedFuture listenForFailure( + final WriteTransaction tx, CheckedFuture future) { + Futures.addCallback(future, new FutureCallback() { + @Override + public void onFailure(Throwable t) { + failTransactionChain(tx,t); + } + + @Override + public void onSuccess(Void result) { + // Intentionally NOOP + } + }); + + return future; } - private synchronized void putDelegateToBinding(final AsyncTransaction domTx, - final AsyncTransaction bindingTx) { - final Object previous = delegateTxToBindingTx.put(domTx, bindingTx); - Preconditions.checkState(previous == null, "DOM Transaction %s has already associated binding transation %s",domTx,previous); + protected void failTransactionChain(WriteTransaction tx, Throwable t) { + // We asume correct state change for underlaying transaction + // chain, so we are not changing any of our internal state + // to mark that we failed. + this.delegatingListener.onTransactionChainFailed(this, tx, t); } - private synchronized AsyncTransaction getBindingTransaction(final AsyncTransaction transaction) { - return delegateTxToBindingTx.get(transaction); + @Override + public void close() { + delegate.close(); } - private final class ListenerInvoker implements TransactionChainListener { - - private final TransactionChainListener listener; - - public ListenerInvoker(final TransactionChainListener listener) { - this.listener = Preconditions.checkNotNull(listener, "Listener must not be null."); - } + private final class DelegateChainListener implements TransactionChainListener { @Override public void onTransactionChainFailed(final TransactionChain chain, final AsyncTransaction transaction, final Throwable cause) { - Preconditions.checkState(delegate.equals(chain), - "Illegal state - listener for %s was invoked for incorrect chain %s.", delegate, chain); - AsyncTransaction bindingTx = getBindingTransaction(transaction); - listener.onTransactionChainFailed(chain, bindingTx, cause); + /* + * Intentionally NOOP, callback for failure, since we + * are also listening on each transaction for failure. + * + * by listening on submit future for Binding transaction + * in order to provide Binding transaction (which was seen by client + * of this transaction chain, instead of + */ } @Override diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/connect/dom/DomToBindingRpcForwarder.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/connect/dom/DomToBindingRpcForwarder.java index ab6d56c15a..6be5f2d481 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/connect/dom/DomToBindingRpcForwarder.java +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/connect/dom/DomToBindingRpcForwarder.java @@ -3,6 +3,12 @@ package org.opendaylight.controller.sal.binding.impl.connect.dom; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkState; +import com.google.common.base.Function; +import com.google.common.collect.FluentIterable; +import com.google.common.collect.ImmutableSet; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; + import java.lang.ref.WeakReference; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; @@ -21,13 +27,13 @@ import org.opendaylight.controller.sal.core.api.RpcImplementation; import org.opendaylight.controller.sal.core.api.RpcProvisionRegistry; import org.opendaylight.yangtools.concepts.CompositeObjectRegistration; import org.opendaylight.yangtools.concepts.ObjectRegistration; +import org.opendaylight.yangtools.util.ClassLoaderUtils; import org.opendaylight.yangtools.yang.binding.BaseIdentity; import org.opendaylight.yangtools.yang.binding.BindingMapping; import org.opendaylight.yangtools.yang.binding.DataObject; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.opendaylight.yangtools.yang.binding.RpcService; import org.opendaylight.yangtools.yang.binding.util.BindingReflections; -import org.opendaylight.yangtools.yang.binding.util.ClassLoaderUtils; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.common.RpcResult; import org.opendaylight.yangtools.yang.data.api.CompositeNode; @@ -35,12 +41,6 @@ import org.opendaylight.yangtools.yang.data.impl.codec.BindingIndependentMapping import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.common.base.Function; -import com.google.common.collect.FluentIterable; -import com.google.common.collect.ImmutableSet; -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListenableFuture; - class DomToBindingRpcForwarder implements RpcImplementation, InvocationHandler { private final Logger LOG = LoggerFactory.getLogger(DomToBindingRpcForwarder.class); @@ -65,8 +65,8 @@ class DomToBindingRpcForwarder implements RpcImplementation, InvocationHandler { static { try { EQUALS_METHOD = Object.class.getMethod("equals", Object.class); - } catch (Exception e) { - throw new RuntimeException(e); + } catch (NoSuchMethodException | SecurityException e) { + throw new ExceptionInInitializerError(e); } } @@ -94,7 +94,7 @@ class DomToBindingRpcForwarder implements RpcImplementation, InvocationHandler { } /** - * Constructor for Routed RPC Forwareder. + * Constructor for Routed RPC Forwarder. * * @param service * @param context diff --git a/opendaylight/md-sal/sal-binding-it/src/main/java/org/opendaylight/controller/test/sal/binding/it/TestHelper.java b/opendaylight/md-sal/sal-binding-it/src/main/java/org/opendaylight/controller/test/sal/binding/it/TestHelper.java index dba2908e9c..efd35ccfa6 100644 --- a/opendaylight/md-sal/sal-binding-it/src/main/java/org/opendaylight/controller/test/sal/binding/it/TestHelper.java +++ b/opendaylight/md-sal/sal-binding-it/src/main/java/org/opendaylight/controller/test/sal/binding/it/TestHelper.java @@ -100,6 +100,7 @@ public class TestHelper { mavenBundle("org.javassist", "javassist").versionAsInProject(), // // mavenBundle(YANGTOOLS, "yang-data-api").versionAsInProject(), // // + mavenBundle(YANGTOOLS, "yang-data-util").versionAsInProject(), // // mavenBundle(YANGTOOLS, "yang-data-impl").versionAsInProject(), // // mavenBundle(YANGTOOLS, "yang-model-api").versionAsInProject(), // // mavenBundle(YANGTOOLS, "yang-model-util").versionAsInProject(), // // diff --git a/opendaylight/md-sal/sal-clustering-commons/pom.xml b/opendaylight/md-sal/sal-clustering-commons/pom.xml index 2bf1a55973..a3619ec4d2 100644 --- a/opendaylight/md-sal/sal-clustering-commons/pom.xml +++ b/opendaylight/md-sal/sal-clustering-commons/pom.xml @@ -199,14 +199,17 @@ jsr305 2.0.1 - com.codahale.metrics metrics-core - 3.0.1 + + + com.codahale.metrics + metrics-graphite + org.jacoco @@ -236,7 +239,18 @@ - + + org.apache.felix + maven-bundle-plugin + true + + + ${project.groupId}.${project.artifactId} + org.opendaylight.controller.cluster.*,org.opendaylight.common.actor,org.opendaylight.common.reporting,org.opendaylight.controller.protobuff.*,org.opendaylight.controller.xml.* + * + + + + - diff --git a/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/common/actor/MeteredBoundedMailbox.java b/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/common/actor/MeteredBoundedMailbox.java index 646431522e..c6d3625ac3 100644 --- a/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/common/actor/MeteredBoundedMailbox.java +++ b/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/common/actor/MeteredBoundedMailbox.java @@ -11,9 +11,8 @@ package org.opendaylight.controller.common.actor; import akka.actor.ActorPath; import akka.actor.ActorRef; import akka.actor.ActorSystem; -import akka.dispatch.BoundedMailbox; +import akka.dispatch.BoundedDequeBasedMailbox; import akka.dispatch.MailboxType; -import akka.dispatch.MessageQueue; import akka.dispatch.ProducesMessageQueue; import com.codahale.metrics.Gauge; import com.codahale.metrics.MetricRegistry; @@ -24,7 +23,7 @@ import scala.concurrent.duration.FiniteDuration; import java.util.concurrent.TimeUnit; -public class MeteredBoundedMailbox implements MailboxType, ProducesMessageQueue { +public class MeteredBoundedMailbox implements MailboxType, ProducesMessageQueue { private MeteredMessageQueue queue; private Integer capacity; @@ -33,16 +32,18 @@ public class MeteredBoundedMailbox implements MailboxType, ProducesMessageQueue< private MetricsReporter reporter; private final String QUEUE_SIZE = "queue-size"; + private final String CAPACITY = "mailbox-capacity"; + private final String TIMEOUT = "mailbox-push-timeout-time"; private final Long DEFAULT_TIMEOUT = 10L; public MeteredBoundedMailbox(ActorSystem.Settings settings, Config config) { Preconditions.checkArgument( config.hasPath("mailbox-capacity"), "Missing configuration [mailbox-capacity]" ); - this.capacity = config.getInt("mailbox-capacity"); + this.capacity = config.getInt(CAPACITY); Preconditions.checkArgument( this.capacity > 0, "mailbox-capacity must be > 0"); Long timeout = -1L; - if ( config.hasPath("mailbox-push-timeout-time") ){ - timeout = config.getDuration("mailbox-push-timeout-time", TimeUnit.NANOSECONDS); + if ( config.hasPath(TIMEOUT) ){ + timeout = config.getDuration(TIMEOUT, TimeUnit.NANOSECONDS); } else { timeout = DEFAULT_TIMEOUT; } @@ -54,7 +55,7 @@ public class MeteredBoundedMailbox implements MailboxType, ProducesMessageQueue< @Override - public MessageQueue create(final scala.Option owner, scala.Option system) { + public MeteredMessageQueue create(final scala.Option owner, scala.Option system) { this.queue = new MeteredMessageQueue(this.capacity, this.pushTimeOut); monitorQueueSize(owner, this.queue); return this.queue; @@ -65,14 +66,15 @@ public class MeteredBoundedMailbox implements MailboxType, ProducesMessageQueue< return; //there's no actor to monitor } actorPath = owner.get().path(); - MetricRegistry registry = reporter.getMetricsRegistry(); + String actorInstanceId = Integer.toString(owner.get().hashCode()); - String actorName = registry.name(actorPath.toString(), QUEUE_SIZE); + MetricRegistry registry = reporter.getMetricsRegistry(); + String actorName = registry.name(actorPath.toString(), actorInstanceId, QUEUE_SIZE); if (registry.getMetrics().containsKey(actorName)) return; //already registered - reporter.getMetricsRegistry().register(actorName, + registry.register(actorName, new Gauge() { @Override public Integer getValue() { @@ -82,7 +84,7 @@ public class MeteredBoundedMailbox implements MailboxType, ProducesMessageQueue< } - public static class MeteredMessageQueue extends BoundedMailbox.MessageQueue { + public static class MeteredMessageQueue extends BoundedDequeBasedMailbox.MessageQueue { public MeteredMessageQueue(int capacity, FiniteDuration pushTimeOut) { super(capacity, pushTimeOut); diff --git a/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/xml/codec/XmlUtils.java b/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/xml/codec/XmlUtils.java index 5848561676..ea8f4a3ef1 100644 --- a/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/xml/codec/XmlUtils.java +++ b/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/xml/codec/XmlUtils.java @@ -75,9 +75,13 @@ public class XmlUtils { */ public static String inputCompositeNodeToXml(CompositeNode cNode, SchemaContext schemaContext){ LOG.debug("Converting input composite node to xml {}", cNode); - if (cNode == null) return BLANK; + if (cNode == null) { + return BLANK; + } - if(schemaContext == null) return BLANK; + if(schemaContext == null) { + return BLANK; + } Document domTree = null; try { @@ -108,9 +112,13 @@ public class XmlUtils { */ public static String outputCompositeNodeToXml(CompositeNode cNode, SchemaContext schemaContext){ LOG.debug("Converting output composite node to xml {}", cNode); - if (cNode == null) return BLANK; + if (cNode == null) { + return BLANK; + } - if(schemaContext == null) return BLANK; + if(schemaContext == null) { + return BLANK; + } Document domTree = null; try { @@ -150,7 +158,9 @@ public class XmlUtils { } public static CompositeNode xmlToCompositeNode(String xml){ - if (xml==null || xml.length()==0) return null; + if (xml==null || xml.length()==0) { + return null; + } Node dataTree; try { @@ -179,11 +189,17 @@ public class XmlUtils { */ public static CompositeNode inputXmlToCompositeNode(QName rpc, String xml, SchemaContext schemaContext){ LOG.debug("Converting input xml to composite node {}", xml); - if (xml==null || xml.length()==0) return null; + if (xml==null || xml.length()==0) { + return null; + } - if(rpc == null) return null; + if(rpc == null) { + return null; + } - if(schemaContext == null) return null; + if(schemaContext == null) { + return null; + } CompositeNode compositeNode = null; try { @@ -213,7 +229,7 @@ public class XmlUtils { LOG.debug("Converted xml input to list of nodes {}", dataNodes); final CompositeNodeBuilder it = ImmutableCompositeNode.builder(); - it.setQName(input); + it.setQName(rpc); it.add(ImmutableCompositeNode.create(input, dataNodes)); compositeNode = it.toInstance(); break; diff --git a/opendaylight/md-sal/sal-clustering-commons/src/test/java/org/opendaylight/controller/common/actor/MeteredBoundedMailboxTest.java b/opendaylight/md-sal/sal-clustering-commons/src/test/java/org/opendaylight/controller/common/actor/MeteredBoundedMailboxTest.java index 1e60803f05..0f96c1002d 100644 --- a/opendaylight/md-sal/sal-clustering-commons/src/test/java/org/opendaylight/controller/common/actor/MeteredBoundedMailboxTest.java +++ b/opendaylight/md-sal/sal-clustering-commons/src/test/java/org/opendaylight/controller/common/actor/MeteredBoundedMailboxTest.java @@ -44,7 +44,8 @@ public class MeteredBoundedMailboxTest { actorSystem.eventStream().subscribe(mockReceiver.getRef(), DeadLetter.class); - final FiniteDuration TEN_SEC = new FiniteDuration(10, TimeUnit.SECONDS); + final FiniteDuration TWENTY_SEC = new FiniteDuration(20, TimeUnit.SECONDS); + String boundedMailBox = actorSystem.name() + ".bounded-mailbox"; ActorRef pingPongActor = actorSystem.actorOf(PingPongActor.props(lock).withMailbox(boundedMailBox), "pingpongactor"); @@ -59,11 +60,11 @@ public class MeteredBoundedMailboxTest { pingPongActor.tell("ping", mockReceiver.getRef()); } - mockReceiver.expectMsgClass(TEN_SEC, DeadLetter.class); + mockReceiver.expectMsgClass(TWENTY_SEC, DeadLetter.class); lock.unlock(); - Object[] eleven = mockReceiver.receiveN(11, TEN_SEC); + Object[] eleven = mockReceiver.receiveN(11, TWENTY_SEC); } /** @@ -89,8 +90,12 @@ public class MeteredBoundedMailboxTest { @Override public void onReceive(Object message) throws Exception { lock.lock(); - if ("ping".equals(message)) - getSender().tell("pong", getSelf()); + try { + if ("ping".equals(message)) + getSender().tell("pong", getSelf()); + } finally { + lock.unlock(); + } } } } \ No newline at end of file diff --git a/opendaylight/md-sal/sal-clustering-config/pom.xml b/opendaylight/md-sal/sal-clustering-config/pom.xml index 31b658d1d7..91c0b5caa1 100644 --- a/opendaylight/md-sal/sal-clustering-config/pom.xml +++ b/opendaylight/md-sal/sal-clustering-config/pom.xml @@ -36,6 +36,21 @@ xml config + + ${project.build.directory}/classes/initial/akka.conf + xml + akkaconf + + + ${project.build.directory}/classes/initial/module-shards.conf + xml + moduleshardconf + + + ${project.build.directory}/classes/initial/modules.conf + xml + moduleconf + 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 0535179aad..5a2116b50f 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 @@ -1,5 +1,10 @@ odl-cluster-data { + bounded-mailbox { + mailbox-type = "org.opendaylight.controller.common.actor.MeteredBoundedMailbox" + mailbox-capacity = 1000 + mailbox-push-timeout-time = 100ms + } akka { actor { provider = "akka.cluster.ClusterActorRefProvider" @@ -16,7 +21,7 @@ odl-cluster-data { remote { log-remote-lifecycle-events = off netty.tcp { - hostname = "" + hostname = "127.0.0.1" port = 2550 maximum-frame-size = 419430400 send-buffer-size = 52428800 @@ -25,14 +30,24 @@ odl-cluster-data { } cluster { - seed-nodes = ["akka.tcp://opendaylight-cluster-data@:2550"] + seed-nodes = ["akka.tcp://opendaylight-cluster-data@127.0.0.1:2550"] auto-down-unreachable-after = 10s + + roles = [ + "member-1" + ] + } } } odl-cluster-rpc { + bounded-mailbox { + mailbox-type = "org.opendaylight.controller.common.actor.MeteredBoundedMailbox" + mailbox-capacity = 1000 + mailbox-push-timeout-time = 100ms + } akka { actor { provider = "akka.cluster.ClusterActorRefProvider" @@ -41,13 +56,13 @@ odl-cluster-rpc { remote { log-remote-lifecycle-events = off netty.tcp { - hostname = "" + hostname = "127.0.0.1" port = 2551 } } cluster { - seed-nodes = ["akka.tcp://opendaylight-cluster-rpc@:2551"] + seed-nodes = ["akka.tcp://opendaylight-cluster-rpc@127.0.0.1:2551"] auto-down-unreachable-after = 10s } diff --git a/opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/md/sal/common/util/jmx/AbstractMXBean.java b/opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/md/sal/common/util/jmx/AbstractMXBean.java new file mode 100644 index 0000000000..a2db29d1e8 --- /dev/null +++ b/opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/md/sal/common/util/jmx/AbstractMXBean.java @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2014 Brocade Communications 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.common.util.jmx; + +import java.lang.management.ManagementFactory; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.management.InstanceNotFoundException; +import javax.management.MBeanRegistrationException; +import javax.management.MBeanServer; +import javax.management.MalformedObjectNameException; +import javax.management.ObjectName; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.annotations.Beta; + +/** + * Abstract base for an MXBean implementation class. + *

+ * This class is not intended for use outside of MD-SAL and its part of private + * implementation (still exported as public to be reused across MD-SAL implementation + * components) and may be removed in subsequent + * releases. + * + * @author Thomas Pantelis + */ +@Beta +public abstract class AbstractMXBean { + + private static final Logger LOG = LoggerFactory.getLogger(AbstractMXBean.class); + + public static String BASE_JMX_PREFIX = "org.opendaylight.controller:"; + + private final MBeanServer server = ManagementFactory.getPlatformMBeanServer(); + + private final String mBeanName; + private final String mBeanType; + private final String mBeanCategory; + + /** + * Constructor. + * + * @param mBeanName Used as the name property in the bean's ObjectName. + * @param mBeanType Used as the type property in the bean's ObjectName. + * @param mBeanCategory Used as the Category property in the bean's ObjectName. + */ + protected AbstractMXBean(@Nonnull String mBeanName, @Nonnull String mBeanType, + @Nullable String mBeanCategory) { + this.mBeanName = mBeanName; + this.mBeanType = mBeanType; + this.mBeanCategory = mBeanCategory; + } + + private ObjectName getMBeanObjectName() throws MalformedObjectNameException { + StringBuilder builder = new StringBuilder(BASE_JMX_PREFIX) + .append("type=").append(getMBeanType()); + + if(getMBeanCategory() != null) { + builder.append(",Category=").append(getMBeanCategory()); + } + + builder.append(",name=").append(getMBeanName()); + return new ObjectName(builder.toString()); + } + + /** + * Registers this bean with the platform MBean server with the domain defined by + * {@link #BASE_JMX_PREFIX}. + * + * @return true is successfully registered, false otherwise. + */ + public boolean registerMBean() { + boolean registered = false; + try { + // Object to identify MBean + final ObjectName mbeanName = this.getMBeanObjectName(); + + LOG.debug("Register MBean {}", mbeanName); + + // unregistered if already registered + if(server.isRegistered(mbeanName)) { + + LOG.debug("MBean {} found to be already registered", mbeanName); + + try { + unregisterMBean(mbeanName); + } catch(Exception e) { + + LOG.warn("unregister mbean {} resulted in exception {} ", mbeanName, e); + } + } + server.registerMBean(this, mbeanName); + registered = true; + + LOG.debug("MBean {} registered successfully", mbeanName.getCanonicalName()); + } catch(Exception e) { + + LOG.error("registration failed {}", e); + + } + return registered; + } + + /** + * Unregisters this bean with the platform MBean server. + * + * @return true is successfully unregistered, false otherwise. + */ + public boolean unregisterMBean() { + boolean unregister = false; + try { + ObjectName mbeanName = this.getMBeanObjectName(); + unregisterMBean(mbeanName); + unregister = true; + } catch(Exception e) { + + LOG.error("Failed when unregistering MBean {}", e); + } + + return unregister; + } + + private void unregisterMBean(ObjectName mbeanName) throws MBeanRegistrationException, + InstanceNotFoundException { + server.unregisterMBean(mbeanName); + } + + /** + * Returns the name property of the bean's ObjectName. + */ + public String getMBeanName() { + return mBeanName; + } + + /** + * Returns the type property of the bean's ObjectName. + */ + public String getMBeanType() { + return mBeanType; + } + + /** + * Returns the Category property of the bean's ObjectName. + */ + public String getMBeanCategory() { + return mBeanCategory; + } +} diff --git a/opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/md/sal/common/util/jmx/QueuedNotificationManagerMXBean.java b/opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/md/sal/common/util/jmx/QueuedNotificationManagerMXBean.java new file mode 100644 index 0000000000..9646adc773 --- /dev/null +++ b/opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/md/sal/common/util/jmx/QueuedNotificationManagerMXBean.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2014 Brocade Communications 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.common.util.jmx; + +import java.util.List; + +import org.opendaylight.yangtools.util.concurrent.ListenerNotificationQueueStats; + +/** + * MXBean interface for {@link QueuedNotificationManager} statistic metrics. + * + * @author Thomas Pantelis + */ +public interface QueuedNotificationManagerMXBean { + + /** + * Returns a list of stat instances for each current listener notification task in progress. + */ + List getCurrentListenerQueueStats(); + + /** + * Returns the configured maximum listener queue size. + */ + int getMaxListenerQueueSize(); +} diff --git a/opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/md/sal/common/util/jmx/QueuedNotificationManagerMXBeanImpl.java b/opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/md/sal/common/util/jmx/QueuedNotificationManagerMXBeanImpl.java new file mode 100644 index 0000000000..e6148fcab2 --- /dev/null +++ b/opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/md/sal/common/util/jmx/QueuedNotificationManagerMXBeanImpl.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2014 Brocade Communications 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.common.util.jmx; + +import java.util.List; + +import org.opendaylight.yangtools.util.concurrent.ListenerNotificationQueueStats; +import org.opendaylight.yangtools.util.concurrent.QueuedNotificationManager; + +import com.google.common.base.Preconditions; + +/** + * Implementation of the QueuedNotificationManagerMXBean interface. + * + *

+ * This class is not intended for use outside of MD-SAL and its part of private + * implementation (still exported as public to be reused across MD-SAL implementation + * components) and may be removed in subsequent + * releases. + * + * @author Thomas Pantelis + */ +public class QueuedNotificationManagerMXBeanImpl extends AbstractMXBean + implements QueuedNotificationManagerMXBean { + + private final QueuedNotificationManager manager; + + public QueuedNotificationManagerMXBeanImpl( QueuedNotificationManager manager, + String mBeanName, String mBeanType, String mBeanCategory ) { + super(mBeanName, mBeanType, mBeanCategory); + this.manager = Preconditions.checkNotNull( manager ); + } + + @Override + public List getCurrentListenerQueueStats() { + return manager.getListenerNotificationQueueStats(); + } + + @Override + public int getMaxListenerQueueSize() { + return manager.getMaxQueueCapacity(); + } + + public QueuedNotificationManagerStats toQueuedNotificationManagerStats() { + return new QueuedNotificationManagerStats( getMaxListenerQueueSize(), + getCurrentListenerQueueStats() ); + } +} diff --git a/opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/md/sal/common/util/jmx/QueuedNotificationManagerStats.java b/opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/md/sal/common/util/jmx/QueuedNotificationManagerStats.java new file mode 100644 index 0000000000..c6e50060a1 --- /dev/null +++ b/opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/md/sal/common/util/jmx/QueuedNotificationManagerStats.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2014 Brocade Communications 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.common.util.jmx; + +import java.beans.ConstructorProperties; +import java.util.List; + +import org.opendaylight.yangtools.util.concurrent.ListenerNotificationQueueStats; + +/** + * A bean class that holds various QueuedNotificationManager statistic metrics. This class is + * suitable for mapping to the MXBean CompositeDataSupport type. + * + *

+ * This class is not intended for use outside of MD-SAL and its part of private + * implementation (still exported as public to be reused across MD-SAL implementation + * components) and may be removed in subsequent + * releases. + * @author Thomas Pantelis + * @see QueuedNotificationManagerMXBeanImpl + */ +public class QueuedNotificationManagerStats { + + private final int maxListenerQueueSize; + private final List currentListenerQueueStats; + + @ConstructorProperties({"maxListenerQueueSize","currentListenerQueueStats"}) + public QueuedNotificationManagerStats( int maxListenerQueueSize, + List currentListenerQueueStats ) { + super(); + this.maxListenerQueueSize = maxListenerQueueSize; + this.currentListenerQueueStats = currentListenerQueueStats; + } + + public List getCurrentListenerQueueStats() { + return currentListenerQueueStats; + } + + public int getMaxListenerQueueSize() { + return maxListenerQueueSize; + } +} diff --git a/opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/md/sal/common/util/jmx/ThreadExecutorStats.java b/opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/md/sal/common/util/jmx/ThreadExecutorStats.java new file mode 100644 index 0000000000..0a766c013d --- /dev/null +++ b/opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/md/sal/common/util/jmx/ThreadExecutorStats.java @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2014 Brocade Communications 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.common.util.jmx; + +import java.beans.ConstructorProperties; + +/** + * A bean class that holds various thread executor statistic metrics. This class is suitable for + * mapping to the MXBean CompositeDataSupport type; + * + * @author Thomas Pantelis + * @see ThreadExecutorStatsMXBeanImpl + */ +public class ThreadExecutorStats { + + private final long activeThreadCount; + private final long completedTaskCount; + private final long currentQueueSize; + private final long maxThreadPoolSize; + private final long totalTaskCount; + private final long largestThreadPoolSize; + private final long maxQueueSize; + private final long currentThreadPoolSize; + + // The following fields are defined as Long because they may be null if we can't a value + // from the underlying executor. + private final Long largestQueueSize; + private final Long rejectedTaskCount; + + @ConstructorProperties({"activeThreadCount","currentThreadPoolSize","largestThreadPoolSize", + "maxThreadPoolSize","currentQueueSize","largestQueueSize","maxQueueSize", + "completedTaskCount","totalTaskCount","rejectedTaskCount"}) + public ThreadExecutorStats(long activeThreadCount, long currentThreadPoolSize, + long largestThreadPoolSize, long maxThreadPoolSize, long currentQueueSize, + Long largestQueueSize, long maxQueueSize, long completedTaskCount, + long totalTaskCount, Long rejectedTaskCount) { + this.activeThreadCount = activeThreadCount; + this.currentThreadPoolSize = currentThreadPoolSize; + this.largestQueueSize = largestQueueSize; + this.largestThreadPoolSize = largestThreadPoolSize; + this.maxThreadPoolSize = maxThreadPoolSize; + this.currentQueueSize = currentQueueSize; + this.maxQueueSize = maxQueueSize; + this.completedTaskCount = completedTaskCount; + this.totalTaskCount = totalTaskCount; + this.rejectedTaskCount = rejectedTaskCount; + } + + public long getActiveThreadCount() { + return activeThreadCount; + } + + public long getCompletedTaskCount() { + return completedTaskCount; + } + + public Long getRejectedTaskCount() { + return rejectedTaskCount; + } + + public long getCurrentQueueSize() { + return currentQueueSize; + } + + public Long getLargestQueueSize() { + return largestQueueSize; + } + + public long getMaxThreadPoolSize() { + return maxThreadPoolSize; + } + + public long getTotalTaskCount() { + return totalTaskCount; + } + + public long getLargestThreadPoolSize() { + return largestThreadPoolSize; + } + + public long getMaxQueueSize() { + return maxQueueSize; + } + + public long getCurrentThreadPoolSize() { + return currentThreadPoolSize; + } +} \ No newline at end of file diff --git a/opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/md/sal/common/util/jmx/ThreadExecutorStatsMXBean.java b/opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/md/sal/common/util/jmx/ThreadExecutorStatsMXBean.java new file mode 100644 index 0000000000..d30bf88468 --- /dev/null +++ b/opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/md/sal/common/util/jmx/ThreadExecutorStatsMXBean.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2014 Brocade Communications 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.common.util.jmx; + +/** + * MXBean interface for thread executor statistic metrics. + * + * @author Thomas Pantelis + */ +public interface ThreadExecutorStatsMXBean { + + /** + * Returns the current thread pool size. + */ + long getCurrentThreadPoolSize(); + + /** + * Returns the largest thread pool size. + */ + long getLargestThreadPoolSize(); + + /** + * Returns the maximum thread pool size. + */ + long getMaxThreadPoolSize(); + + /** + * Returns the current queue size. + */ + long getCurrentQueueSize(); + + /** + * Returns the largest queue size, if available. + */ + Long getLargestQueueSize(); + + /** + * Returns the maximum queue size. + */ + long getMaxQueueSize(); + + /** + * Returns the active thread count. + */ + long getActiveThreadCount(); + + /** + * Returns the completed task count. + */ + long getCompletedTaskCount(); + + /** + * Returns the total task count. + */ + long getTotalTaskCount(); + + /** + * Returns the rejected task count, if available. + */ + Long getRejectedTaskCount(); +} diff --git a/opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/md/sal/common/util/jmx/ThreadExecutorStatsMXBeanImpl.java b/opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/md/sal/common/util/jmx/ThreadExecutorStatsMXBeanImpl.java new file mode 100644 index 0000000000..b67855d731 --- /dev/null +++ b/opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/md/sal/common/util/jmx/ThreadExecutorStatsMXBeanImpl.java @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2014 Brocade Communications 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.common.util.jmx; + +import com.google.common.base.Preconditions; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.Executor; +import java.util.concurrent.RejectedExecutionHandler; +import java.util.concurrent.ThreadPoolExecutor; +import javax.annotation.Nullable; +import org.opendaylight.yangtools.util.concurrent.CountingRejectedExecutionHandler; +import org.opendaylight.yangtools.util.concurrent.TrackingLinkedBlockingQueue; + +/** + * MXBean implementation of the ThreadExecutorStatsMXBean interface that retrieves statistics + * from a backing {@link java.util.concurrent.ExecutorService}. + * + * @author Thomas Pantelis + */ +public class ThreadExecutorStatsMXBeanImpl extends AbstractMXBean + implements ThreadExecutorStatsMXBean { + + private final ThreadPoolExecutor executor; + + /** + * Constructs an instance for the given {@link Executor}. + * + * @param executor the backing {@link Executor} + * @param mBeanName Used as the name property in the bean's ObjectName. + * @param mBeanType Used as the type property in the bean's ObjectName. + * @param mBeanCategory Used as the Category property in the bean's ObjectName. + */ + public ThreadExecutorStatsMXBeanImpl(Executor executor, String mBeanName, + String mBeanType, @Nullable String mBeanCategory) { + super(mBeanName, mBeanType, mBeanCategory); + + Preconditions.checkArgument(executor instanceof ThreadPoolExecutor, + "The ExecutorService of type {} is not an instanceof ThreadPoolExecutor", + executor.getClass()); + this.executor = (ThreadPoolExecutor)executor; + } + + @Override + public long getCurrentThreadPoolSize() { + return executor.getPoolSize(); + } + + @Override + public long getLargestThreadPoolSize() { + return executor.getLargestPoolSize(); + } + + @Override + public long getMaxThreadPoolSize() { + return executor.getMaximumPoolSize(); + } + + @Override + public long getCurrentQueueSize() { + return executor.getQueue().size(); + } + + @Override + public Long getLargestQueueSize() { + BlockingQueue queue = executor.getQueue(); + if(queue instanceof TrackingLinkedBlockingQueue) { + return Long.valueOf(((TrackingLinkedBlockingQueue)queue).getLargestQueueSize()); + } + + return null; + } + + @Override + public long getMaxQueueSize() { + long queueSize = executor.getQueue().size(); + return executor.getQueue().remainingCapacity() + queueSize; + } + + @Override + public long getActiveThreadCount() { + return executor.getActiveCount(); + } + + @Override + public long getCompletedTaskCount() { + return executor.getCompletedTaskCount(); + } + + @Override + public long getTotalTaskCount() { + return executor.getTaskCount(); + } + + @Override + public Long getRejectedTaskCount() { + RejectedExecutionHandler rejectedHandler = executor.getRejectedExecutionHandler(); + if(rejectedHandler instanceof CountingRejectedExecutionHandler) { + return Long.valueOf(((CountingRejectedExecutionHandler)rejectedHandler) + .getRejectedTaskCount()); + } + + return null; + } + + /** + * Returns a {@link ThreadExecutorStats} instance containing a snapshot of the statistic + * metrics. + */ + public ThreadExecutorStats toThreadExecutorStats() { + return new ThreadExecutorStats(getActiveThreadCount(), getCurrentThreadPoolSize(), + getLargestThreadPoolSize(), getMaxThreadPoolSize(), getCurrentQueueSize(), + getLargestQueueSize(), getMaxQueueSize(), getCompletedTaskCount(), + getTotalTaskCount(), getRejectedTaskCount()); + } +} diff --git a/opendaylight/md-sal/sal-distributed-datastore/pom.xml b/opendaylight/md-sal/sal-distributed-datastore/pom.xml index 4669fbdb61..82998226b6 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/pom.xml +++ b/opendaylight/md-sal/sal-distributed-datastore/pom.xml @@ -45,6 +45,11 @@ akka-slf4j_${scala.version} + + com.typesafe.akka + akka-osgi_${scala.version} + + @@ -138,7 +143,11 @@ com.codahale.metrics metrics-core - 3.0.1 + + + + com.codahale.metrics + metrics-graphite @@ -173,7 +182,8 @@ ${project.groupId}.${project.artifactId} - !*snappy;!org.jboss.*;!com.jcraft.*;* + !*snappy;!org.jboss.*;!com.jcraft.*;!*jetty*;!sun.security.*;* + diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ActorSystemFactory.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ActorSystemFactory.java index 15c0548761..b326d61fc6 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ActorSystemFactory.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ActorSystemFactory.java @@ -10,24 +10,55 @@ package org.opendaylight.controller.cluster.datastore; import akka.actor.ActorSystem; import akka.actor.Props; -import com.google.common.base.Function; +import akka.osgi.BundleDelegatingClassLoader; +import com.google.common.base.Preconditions; +import com.typesafe.config.Config; import com.typesafe.config.ConfigFactory; +import org.osgi.framework.BundleContext; -import javax.annotation.Nullable; +import java.io.File; public class ActorSystemFactory { - private static final ActorSystem actorSystem = (new Function(){ - - @Nullable @Override public ActorSystem apply(@Nullable Void aVoid) { - ActorSystem system = - ActorSystem.create("opendaylight-cluster-data", ConfigFactory - .load().getConfig("odl-cluster-data")); - system.actorOf(Props.create(TerminationMonitor.class), "termination-monitor"); - return system; - } - }).apply(null); + + public static final String AKKA_CONF_PATH = "./configuration/initial/akka.conf"; + public static final String ACTOR_SYSTEM_NAME = "opendaylight-cluster-data"; + public static final String CONFIGURATION_NAME = "odl-cluster-data"; + + private static volatile ActorSystem actorSystem = null; public static final ActorSystem getInstance(){ return actorSystem; } + + /** + * This method should be called only once during initialization + * + * @param bundleContext + */ + public static final ActorSystem createInstance(final BundleContext bundleContext) { + if(actorSystem == null) { + // Create an OSGi bundle classloader for actor system + BundleDelegatingClassLoader classLoader = new BundleDelegatingClassLoader(bundleContext.getBundle(), + Thread.currentThread().getContextClassLoader()); + synchronized (ActorSystemFactory.class) { + // Double check + + if (actorSystem == null) { + ActorSystem system = ActorSystem.create(ACTOR_SYSTEM_NAME, + ConfigFactory.load(readAkkaConfiguration()).getConfig(CONFIGURATION_NAME), classLoader); + system.actorOf(Props.create(TerminationMonitor.class), "termination-monitor"); + actorSystem = system; + } + } + } + + return actorSystem; + } + + + private static final Config readAkkaConfiguration(){ + File defaultConfigFile = new File(AKKA_CONF_PATH); + Preconditions.checkState(defaultConfigFile.exists(), "akka.conf is missing"); + return ConfigFactory.parseFile(defaultConfigFile); + } } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardContext.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DatastoreContext.java similarity index 87% rename from opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardContext.java rename to opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DatastoreContext.java index 02bff77af8..af8a987c73 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardContext.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DatastoreContext.java @@ -8,30 +8,29 @@ package org.opendaylight.controller.cluster.datastore; -import java.util.concurrent.TimeUnit; - -import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStoreConfigProperties; import com.google.common.base.Preconditions; - +import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStoreConfigProperties; import scala.concurrent.duration.Duration; +import java.util.concurrent.TimeUnit; + /** * Contains contextual data for shards. * * @author Thomas Pantelis */ -public class ShardContext { +public class DatastoreContext { private final InMemoryDOMDataStoreConfigProperties dataStoreProperties; private final Duration shardTransactionIdleTimeout; - public ShardContext() { + public DatastoreContext() { this.dataStoreProperties = null; this.shardTransactionIdleTimeout = Duration.create(10, TimeUnit.MINUTES); } - public ShardContext(InMemoryDOMDataStoreConfigProperties dataStoreProperties, - Duration shardTransactionIdleTimeout) { + public DatastoreContext(InMemoryDOMDataStoreConfigProperties dataStoreProperties, + Duration shardTransactionIdleTimeout) { this.dataStoreProperties = Preconditions.checkNotNull(dataStoreProperties); this.shardTransactionIdleTimeout = Preconditions.checkNotNull(shardTransactionIdleTimeout); } @@ -43,4 +42,6 @@ public class ShardContext { public Duration getShardTransactionIdleTimeout() { return shardTransactionIdleTimeout; } + + } 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 51f3735f81..0a137e07df 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 @@ -46,7 +46,7 @@ public class DistributedDataStore implements DOMStore, SchemaContextListener, Au private static final Logger LOG = LoggerFactory.getLogger(DistributedDataStore.class); private final ActorContext actorContext; - private final ShardContext shardContext; + private final DatastoreContext datastoreContext; public DistributedDataStore(ActorSystem actorSystem, String type, ClusterWrapper cluster, Configuration configuration, DistributedDataStoreProperties dataStoreProperties) { @@ -60,21 +60,25 @@ public class DistributedDataStore implements DOMStore, SchemaContextListener, Au LOG.info("Creating ShardManager : {}", shardManagerId); - shardContext = new ShardContext(InMemoryDOMDataStoreConfigProperties.create( + datastoreContext = new DatastoreContext(InMemoryDOMDataStoreConfigProperties.create( dataStoreProperties.getMaxShardDataChangeExecutorPoolSize(), dataStoreProperties.getMaxShardDataChangeExecutorQueueSize(), dataStoreProperties.getMaxShardDataChangeListenerQueueSize()), Duration.create(dataStoreProperties.getShardTransactionIdleTimeoutInMinutes(), TimeUnit.MINUTES)); - actorContext = new ActorContext(actorSystem, actorSystem - .actorOf(ShardManager.props(type, cluster, configuration, shardContext), - shardManagerId ), cluster, configuration); + actorContext + = new ActorContext( + actorSystem, actorSystem.actorOf( + ShardManager.props(type, cluster, configuration, datastoreContext). + withMailbox(ActorContext.MAILBOX), shardManagerId ), cluster, configuration); + + actorContext.setOperationTimeout(dataStoreProperties.getOperationTimeoutInSeconds()); } public DistributedDataStore(ActorContext actorContext) { this.actorContext = Preconditions.checkNotNull(actorContext, "actorContext should not be null"); - this.shardContext = new ShardContext(); + this.datastoreContext = new DatastoreContext(); } @@ -96,8 +100,7 @@ public class DistributedDataStore implements DOMStore, SchemaContextListener, Au String shardName = ShardStrategyFactory.getStrategy(path).findShard(path); Object result = actorContext.executeLocalShardOperation(shardName, - new RegisterChangeListener(path, dataChangeListenerActor.path(), scope), - ActorContext.ASK_DURATION); + new RegisterChangeListener(path, dataChangeListenerActor.path(), scope)); if (result != null) { RegisterChangeListenerReply reply = (RegisterChangeListenerReply) result; diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DistributedDataStoreFactory.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DistributedDataStoreFactory.java index 65a39a60e6..72b593f010 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DistributedDataStoreFactory.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DistributedDataStoreFactory.java @@ -12,12 +12,13 @@ import akka.actor.ActorSystem; import org.opendaylight.controller.cluster.datastore.shardstrategy.ShardStrategyFactory; import org.opendaylight.controller.sal.core.api.model.SchemaService; +import org.osgi.framework.BundleContext; public class DistributedDataStoreFactory { public static DistributedDataStore createInstance(String name, SchemaService schemaService, - DistributedDataStoreProperties dataStoreProperties) { + DistributedDataStoreProperties dataStoreProperties, BundleContext bundleContext) { - ActorSystem actorSystem = ActorSystemFactory.getInstance(); + ActorSystem actorSystem = ActorSystemFactory.createInstance(bundleContext); Configuration config = new ConfigurationImpl("module-shards.conf", "modules.conf"); final DistributedDataStore dataStore = new DistributedDataStore(actorSystem, name, new ClusterWrapperImpl(actorSystem), diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DistributedDataStoreProperties.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DistributedDataStoreProperties.java index eb6a536138..df3245ffb2 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DistributedDataStoreProperties.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DistributedDataStoreProperties.java @@ -18,21 +18,24 @@ public class DistributedDataStoreProperties { private final int maxShardDataChangeExecutorQueueSize; private final int maxShardDataChangeExecutorPoolSize; private final int shardTransactionIdleTimeoutInMinutes; + private final int operationTimeoutInSeconds; public DistributedDataStoreProperties() { maxShardDataChangeListenerQueueSize = 1000; maxShardDataChangeExecutorQueueSize = 1000; maxShardDataChangeExecutorPoolSize = 20; shardTransactionIdleTimeoutInMinutes = 10; + operationTimeoutInSeconds = 5; } public DistributedDataStoreProperties(int maxShardDataChangeListenerQueueSize, int maxShardDataChangeExecutorQueueSize, int maxShardDataChangeExecutorPoolSize, - int shardTransactionIdleTimeoutInMinutes) { + int shardTransactionIdleTimeoutInMinutes, int operationTimeoutInSeconds) { this.maxShardDataChangeListenerQueueSize = maxShardDataChangeListenerQueueSize; this.maxShardDataChangeExecutorQueueSize = maxShardDataChangeExecutorQueueSize; this.maxShardDataChangeExecutorPoolSize = maxShardDataChangeExecutorPoolSize; this.shardTransactionIdleTimeoutInMinutes = shardTransactionIdleTimeoutInMinutes; + this.operationTimeoutInSeconds = operationTimeoutInSeconds; } public int getMaxShardDataChangeListenerQueueSize() { @@ -50,4 +53,8 @@ public class DistributedDataStoreProperties { public int getShardTransactionIdleTimeoutInMinutes() { return shardTransactionIdleTimeoutInMinutes; } + + public int getOperationTimeoutInSeconds() { + return operationTimeoutInSeconds; + } } 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 abcde747b9..6a6a181b6c 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 @@ -14,6 +14,7 @@ import akka.actor.Props; import akka.event.Logging; import akka.event.LoggingAdapter; import akka.japi.Creator; +import akka.persistence.RecoveryFailure; import akka.serialization.Serialization; import com.google.common.base.Optional; @@ -21,7 +22,7 @@ import com.google.common.base.Preconditions; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; - +import com.google.protobuf.ByteString; import org.opendaylight.controller.cluster.datastore.identifiers.ShardIdentifier; import org.opendaylight.controller.cluster.datastore.identifiers.ShardTransactionIdentifier; import org.opendaylight.controller.cluster.datastore.jmx.mbeans.shard.ShardMBeanFactory; @@ -96,16 +97,17 @@ public class Shard extends RaftActor { private final List dataChangeListeners = new ArrayList<>(); - private final ShardContext shardContext; + private final DatastoreContext datastoreContext; + private SchemaContext schemaContext; private Shard(ShardIdentifier name, Map peerAddresses, - ShardContext shardContext) { + DatastoreContext datastoreContext) { super(name.toString(), mapPeerAddresses(peerAddresses), Optional.of(configParams)); this.name = name; - this.shardContext = shardContext; + this.datastoreContext = datastoreContext; String setting = System.getProperty("shard.persistent"); @@ -114,10 +116,11 @@ public class Shard extends RaftActor { LOG.info("Shard created : {} persistent : {}", name, persistent); store = InMemoryDOMDataStoreFactory.create(name.toString(), null, - shardContext.getDataStoreProperties()); + datastoreContext.getDataStoreProperties()); shardMBean = ShardMBeanFactory.getShardStatsMBean(name.toString()); + } private static Map mapPeerAddresses( @@ -134,16 +137,27 @@ public class Shard extends RaftActor { public static Props props(final ShardIdentifier name, final Map peerAddresses, - ShardContext shardContext) { + DatastoreContext datastoreContext) { Preconditions.checkNotNull(name, "name should not be null"); Preconditions.checkNotNull(peerAddresses, "peerAddresses should not be null"); - Preconditions.checkNotNull(shardContext, "shardContext should not be null"); + Preconditions.checkNotNull(datastoreContext, "shardContext should not be null"); - return Props.create(new ShardCreator(name, peerAddresses, shardContext)); + return Props.create(new ShardCreator(name, peerAddresses, datastoreContext)); + } + + @Override public void onReceiveRecover(Object message) { + LOG.debug("onReceiveRecover: Received message {} from {}", message.getClass().toString(), + getSender()); + + if (message instanceof RecoveryFailure){ + LOG.error(((RecoveryFailure) message).cause(), "Recovery failed because of this cause"); + } else { + super.onReceiveRecover(message); + } } @Override public void onReceiveCommand(Object message) { - LOG.debug("Received message {} from {}", message.getClass().toString(), + LOG.debug("onReceiveCommand: Received message {} from {}", message.getClass().toString(), getSender()); if (message.getClass() @@ -185,7 +199,7 @@ public class Shard extends RaftActor { return getContext().actorOf( ShardTransaction.props(store.newReadOnlyTransaction(), getSelf(), - schemaContext, shardContext), transactionId.toString()); + schemaContext,datastoreContext, name.toString()), transactionId.toString()); } else if (createTransaction.getTransactionType() == TransactionProxy.TransactionType.READ_WRITE.ordinal()) { @@ -194,7 +208,7 @@ public class Shard extends RaftActor { return getContext().actorOf( ShardTransaction.props(store.newReadWriteTransaction(), getSelf(), - schemaContext, shardContext), transactionId.toString()); + schemaContext, datastoreContext,name.toString()), transactionId.toString()); } else if (createTransaction.getTransactionType() @@ -204,7 +218,7 @@ public class Shard extends RaftActor { return getContext().actorOf( ShardTransaction.props(store.newWriteOnlyTransaction(), getSelf(), - schemaContext, shardContext), transactionId.toString()); + schemaContext, datastoreContext, name.toString()), transactionId.toString()); } else { throw new IllegalArgumentException( "Shard="+name + ":CreateTransaction message has unidentified transaction type=" @@ -226,7 +240,8 @@ public class Shard extends RaftActor { .tell(new CreateTransactionReply( Serialization.serializedActorPath(transactionActor), createTransaction.getTransactionId()).toSerializable(), - getSelf()); + getSelf() + ); } private void commit(final ActorRef sender, Object serialized) { @@ -264,9 +279,9 @@ public class Shard extends RaftActor { Futures.addCallback(future, new FutureCallback() { @Override public void onSuccess(Void v) { - sender.tell(new CommitTransactionReply().toSerializable(),self); - shardMBean.incrementCommittedTransactionCount(); - shardMBean.setLastCommittedTransactionTime(new Date()); + sender.tell(new CommitTransactionReply().toSerializable(), self); + shardMBean.incrementCommittedTransactionCount(); + shardMBean.setLastCommittedTransactionTime(new Date()); } @Override @@ -343,7 +358,7 @@ public class Shard extends RaftActor { private void createTransactionChain() { DOMStoreTransactionChain chain = store.createTransactionChain(); ActorRef transactionChain = getContext().actorOf( - ShardTransactionChain.props(chain, schemaContext, shardContext)); + ShardTransactionChain.props(chain, schemaContext, datastoreContext,name.toString() )); getSender().tell(new CreateTransactionChainReply(transactionChain.path()).toSerializable(), getSelf()); } @@ -363,7 +378,6 @@ public class Shard extends RaftActor { identifier, clientActor.path().toString()); } - } else { LOG.error("Unknown state received {}", data); } @@ -381,11 +395,11 @@ public class Shard extends RaftActor { } - @Override protected Object createSnapshot() { + @Override protected void createSnapshot() { throw new UnsupportedOperationException("createSnapshot"); } - @Override protected void applySnapshot(Object snapshot) { + @Override protected void applySnapshot(ByteString snapshot) { throw new UnsupportedOperationException("applySnapshot"); } @@ -423,18 +437,18 @@ public class Shard extends RaftActor { final ShardIdentifier name; final Map peerAddresses; - final ShardContext shardContext; + final DatastoreContext datastoreContext; ShardCreator(ShardIdentifier name, Map peerAddresses, - ShardContext shardContext) { + DatastoreContext datastoreContext) { this.name = name; this.peerAddresses = peerAddresses; - this.shardContext = shardContext; + this.datastoreContext = datastoreContext; } @Override public Shard create() throws Exception { - return new Shard(name, peerAddresses, shardContext); + return new Shard(name, peerAddresses, datastoreContext); } } } 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 c9b7c07e9a..e51d49bff2 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 @@ -33,6 +33,7 @@ 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.ActorContext; import scala.concurrent.duration.Duration; import java.util.ArrayList; @@ -71,19 +72,19 @@ public class ShardManager extends AbstractUntypedActor { private ShardManagerInfoMBean mBean; - private final ShardContext shardContext; + private final DatastoreContext datastoreContext; /** * @param type defines the kind of data that goes into shards created by this shard manager. Examples of type would be * configuration or operational */ private ShardManager(String type, ClusterWrapper cluster, Configuration configuration, - ShardContext shardContext) { + DatastoreContext datastoreContext) { this.type = Preconditions.checkNotNull(type, "type should not be null"); this.cluster = Preconditions.checkNotNull(cluster, "cluster should not be null"); this.configuration = Preconditions.checkNotNull(configuration, "configuration should not be null"); - this.shardContext = shardContext; + this.datastoreContext = datastoreContext; // Subscribe this actor to cluster member events cluster.subscribeToMemberEvents(getSelf()); @@ -96,13 +97,13 @@ public class ShardManager extends AbstractUntypedActor { public static Props props(final String type, final ClusterWrapper cluster, final Configuration configuration, - final ShardContext shardContext) { + final DatastoreContext datastoreContext) { Preconditions.checkNotNull(type, "type should not be null"); Preconditions.checkNotNull(cluster, "cluster should not be null"); Preconditions.checkNotNull(configuration, "configuration should not be null"); - return Props.create(new ShardManagerCreator(type, cluster, configuration, shardContext)); + return Props.create(new ShardManagerCreator(type, cluster, configuration, datastoreContext)); } @Override @@ -244,8 +245,9 @@ public class ShardManager extends AbstractUntypedActor { ShardIdentifier shardId = getShardIdentifier(memberName, shardName); Map peerAddresses = getPeerAddresses(shardName); ActorRef actor = getContext() - .actorOf(Shard.props(shardId, peerAddresses, shardContext), - shardId.toString()); + .actorOf(Shard.props(shardId, peerAddresses, datastoreContext). + withMailbox(ActorContext.MAILBOX), shardId.toString()); + localShardActorNames.add(shardId.toString()); localShards.put(shardName, new ShardInformation(shardName, actor, peerAddresses)); } @@ -352,19 +354,19 @@ public class ShardManager extends AbstractUntypedActor { final String type; final ClusterWrapper cluster; final Configuration configuration; - final ShardContext shardContext; + final DatastoreContext datastoreContext; ShardManagerCreator(String type, ClusterWrapper cluster, - Configuration configuration, ShardContext shardContext) { + Configuration configuration, DatastoreContext datastoreContext) { this.type = type; this.cluster = cluster; this.configuration = configuration; - this.shardContext = shardContext; + this.datastoreContext = datastoreContext; } @Override public ShardManager create() throws Exception { - return new ShardManager(type, cluster, configuration, shardContext); + return new ShardManager(type, cluster, configuration, datastoreContext); } } } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardReadTransaction.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardReadTransaction.java index c54d3739b2..91d629432f 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardReadTransaction.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardReadTransaction.java @@ -26,8 +26,8 @@ public class ShardReadTransaction extends ShardTransaction { private final DOMStoreReadTransaction transaction; public ShardReadTransaction(DOMStoreReadTransaction transaction, ActorRef shardActor, - SchemaContext schemaContext) { - super(shardActor, schemaContext); + SchemaContext schemaContext,String shardName) { + super(shardActor, schemaContext, shardName); this.transaction = transaction; } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardReadWriteTransaction.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardReadWriteTransaction.java index 1dc9ce0a3e..bd71c27fd6 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardReadWriteTransaction.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardReadWriteTransaction.java @@ -30,8 +30,8 @@ public class ShardReadWriteTransaction extends ShardTransaction { private final DOMStoreReadWriteTransaction transaction; public ShardReadWriteTransaction(DOMStoreReadWriteTransaction transaction, ActorRef shardActor, - SchemaContext schemaContext) { - super(shardActor, schemaContext); + SchemaContext schemaContext,String shardName) { + super(shardActor, schemaContext, shardName); this.transaction = transaction; } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardTransaction.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardTransaction.java index 365960713d..3b0e0934d9 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardTransaction.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardTransaction.java @@ -13,11 +13,10 @@ import akka.actor.PoisonPill; import akka.actor.Props; import akka.actor.ReceiveTimeout; import akka.japi.Creator; - import com.google.common.base.Optional; import com.google.common.util.concurrent.CheckedFuture; - import org.opendaylight.controller.cluster.datastore.exceptions.UnknownMessageException; +import org.opendaylight.controller.cluster.datastore.jmx.mbeans.shard.ShardMBeanFactory; import org.opendaylight.controller.cluster.datastore.messages.CloseTransaction; import org.opendaylight.controller.cluster.datastore.messages.CloseTransactionReply; import org.opendaylight.controller.cluster.datastore.messages.DataExists; @@ -74,18 +73,22 @@ public abstract class ShardTransaction extends AbstractUntypedActor { private final ActorRef shardActor; protected final SchemaContext schemaContext; + private final String shardName; + private final MutableCompositeModification modification = new MutableCompositeModification(); - protected ShardTransaction(ActorRef shardActor, SchemaContext schemaContext) { + protected ShardTransaction(ActorRef shardActor, SchemaContext schemaContext, + String shardName) { this.shardActor = shardActor; this.schemaContext = schemaContext; + this.shardName = shardName; } public static Props props(DOMStoreTransaction transaction, ActorRef shardActor, - SchemaContext schemaContext, ShardContext shardContext) { + SchemaContext schemaContext,DatastoreContext datastoreContext, String shardName) { return Props.create(new ShardTransactionCreator(transaction, shardActor, schemaContext, - shardContext)); + datastoreContext, shardName)); } protected abstract DOMStoreTransaction getDOMStoreTransaction(); @@ -134,7 +137,8 @@ public abstract class ShardTransaction extends AbstractUntypedActor { sender.tell(new ReadDataReply(schemaContext,null).toSerializable(), self); } } catch (Exception e) { - sender.tell(new akka.actor.Status.Failure(e),self); + ShardMBeanFactory.getShardStatsMBean(shardName).incrementFailedReadTransactionsCount(); + sender.tell(new akka.actor.Status.Failure(e), self); } } @@ -192,7 +196,7 @@ public abstract class ShardTransaction extends AbstractUntypedActor { protected void readyTransaction(DOMStoreWriteTransaction transaction, ReadyTransaction message) { DOMStoreThreePhaseCommitCohort cohort = transaction.ready(); ActorRef cohortActor = getContext().actorOf( - ThreePhaseCommitCohort.props(cohort, shardActor, modification), "cohort"); + ThreePhaseCommitCohort.props(cohort, shardActor, modification, shardName), "cohort"); getSender() .tell(new ReadyTransactionReply(cohortActor.path()).toSerializable(), getSelf()); @@ -205,14 +209,16 @@ public abstract class ShardTransaction extends AbstractUntypedActor { final DOMStoreTransaction transaction; final ActorRef shardActor; final SchemaContext schemaContext; - final ShardContext shardContext; + final DatastoreContext datastoreContext; + final String shardName; ShardTransactionCreator(DOMStoreTransaction transaction, ActorRef shardActor, - SchemaContext schemaContext, ShardContext actorContext) { + SchemaContext schemaContext, DatastoreContext datastoreContext, String shardName) { this.transaction = transaction; this.shardActor = shardActor; - this.shardContext = actorContext; + this.shardName = shardName; this.schemaContext = schemaContext; + this.datastoreContext = datastoreContext; } @Override @@ -220,16 +226,16 @@ public abstract class ShardTransaction extends AbstractUntypedActor { ShardTransaction tx; if(transaction instanceof DOMStoreReadWriteTransaction) { tx = new ShardReadWriteTransaction((DOMStoreReadWriteTransaction)transaction, - shardActor, schemaContext); + shardActor, schemaContext, shardName); } else if(transaction instanceof DOMStoreReadTransaction) { tx = new ShardReadTransaction((DOMStoreReadTransaction)transaction, shardActor, - schemaContext); + schemaContext, shardName); } else { tx = new ShardWriteTransaction((DOMStoreWriteTransaction)transaction, - shardActor, schemaContext); + shardActor, schemaContext, shardName); } - tx.getContext().setReceiveTimeout(shardContext.getShardTransactionIdleTimeout()); + tx.getContext().setReceiveTimeout(datastoreContext.getShardTransactionIdleTimeout()); return tx; } } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardTransactionChain.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardTransactionChain.java index 42bd257ad1..e7a181865e 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardTransactionChain.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardTransactionChain.java @@ -25,14 +25,16 @@ import org.opendaylight.yangtools.yang.model.api.SchemaContext; public class ShardTransactionChain extends AbstractUntypedActor { private final DOMStoreTransactionChain chain; - private final ShardContext shardContext; + private final DatastoreContext datastoreContext; private final SchemaContext schemaContext; + private final String shardName; public ShardTransactionChain(DOMStoreTransactionChain chain, SchemaContext schemaContext, - ShardContext shardContext) { + DatastoreContext datastoreContext,String shardName) { this.chain = chain; - this.shardContext = shardContext; + this.datastoreContext = datastoreContext; this.schemaContext = schemaContext; + this.shardName = shardName; } @Override @@ -58,17 +60,17 @@ public class ShardTransactionChain extends AbstractUntypedActor { TransactionProxy.TransactionType.READ_ONLY.ordinal()) { return getContext().actorOf( ShardTransaction.props( chain.newReadOnlyTransaction(), getShardActor(), - schemaContext, shardContext), transactionId); + schemaContext, datastoreContext,shardName), transactionId); } else if (createTransaction.getTransactionType() == TransactionProxy.TransactionType.READ_WRITE.ordinal()) { return getContext().actorOf( ShardTransaction.props( chain.newReadWriteTransaction(), getShardActor(), - schemaContext, shardContext), transactionId); + schemaContext, datastoreContext,shardName), transactionId); } else if (createTransaction.getTransactionType() == TransactionProxy.TransactionType.WRITE_ONLY.ordinal()) { return getContext().actorOf( ShardTransaction.props( chain.newWriteOnlyTransaction(), getShardActor(), - schemaContext, shardContext), transactionId); + schemaContext, datastoreContext,shardName), transactionId); } else { throw new IllegalArgumentException ( "CreateTransaction message has unidentified transaction type=" + @@ -85,27 +87,30 @@ public class ShardTransactionChain extends AbstractUntypedActor { } public static Props props(DOMStoreTransactionChain chain, SchemaContext schemaContext, - ShardContext shardContext) { - return Props.create(new ShardTransactionChainCreator(chain, schemaContext, shardContext)); + DatastoreContext datastoreContext, String shardName) { + return Props.create(new ShardTransactionChainCreator(chain, schemaContext, datastoreContext, shardName)); } private static class ShardTransactionChainCreator implements Creator { private static final long serialVersionUID = 1L; final DOMStoreTransactionChain chain; - final ShardContext shardContext; + final DatastoreContext datastoreContext; final SchemaContext schemaContext; + final String shardName; + ShardTransactionChainCreator(DOMStoreTransactionChain chain, SchemaContext schemaContext, - ShardContext shardContext) { + DatastoreContext datastoreContext, String shardName) { this.chain = chain; - this.shardContext = shardContext; + this.datastoreContext = datastoreContext; this.schemaContext = schemaContext; + this.shardName = shardName; } @Override public ShardTransactionChain create() throws Exception { - return new ShardTransactionChain(chain, schemaContext, shardContext); + return new ShardTransactionChain(chain, schemaContext, datastoreContext,shardName); } } } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardWriteTransaction.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardWriteTransaction.java index 8b4d576163..41c46c3375 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardWriteTransaction.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardWriteTransaction.java @@ -28,8 +28,8 @@ public class ShardWriteTransaction extends ShardTransaction { private final DOMStoreWriteTransaction transaction; public ShardWriteTransaction(DOMStoreWriteTransaction transaction, ActorRef shardActor, - SchemaContext schemaContext) { - super(shardActor, schemaContext); + SchemaContext schemaContext,String shardName) { + super(shardActor, schemaContext, shardName); this.transaction = transaction; } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ThreePhaseCommitCohort.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ThreePhaseCommitCohort.java index d0c29294cb..5a6d0eca5c 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ThreePhaseCommitCohort.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ThreePhaseCommitCohort.java @@ -19,6 +19,7 @@ import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; +import org.opendaylight.controller.cluster.datastore.jmx.mbeans.shard.ShardMBeanFactory; import org.opendaylight.controller.cluster.datastore.messages.AbortTransaction; import org.opendaylight.controller.cluster.datastore.messages.AbortTransactionReply; import org.opendaylight.controller.cluster.datastore.messages.CanCommitTransaction; @@ -34,21 +35,25 @@ public class ThreePhaseCommitCohort extends AbstractUntypedActor { private final DOMStoreThreePhaseCommitCohort cohort; private final ActorRef shardActor; private final CompositeModification modification; + private final String shardName; public ThreePhaseCommitCohort(DOMStoreThreePhaseCommitCohort cohort, - ActorRef shardActor, CompositeModification modification) { + ActorRef shardActor, CompositeModification modification,String shardName) { this.cohort = cohort; this.shardActor = shardActor; this.modification = modification; + this.shardName = shardName; } private final LoggingAdapter log = Logging.getLogger(getContext().system(), this); public static Props props(final DOMStoreThreePhaseCommitCohort cohort, - final ActorRef shardActor, final CompositeModification modification) { - return Props.create(new ThreePhaseCommitCohortCreator(cohort, shardActor, modification)); + final ActorRef shardActor, final CompositeModification modification, + String shardName) { + return Props.create(new ThreePhaseCommitCohortCreator(cohort, shardActor, modification, + shardName)); } @Override @@ -78,9 +83,10 @@ public class ThreePhaseCommitCohort extends AbstractUntypedActor { Futures.addCallback(future, new FutureCallback() { @Override public void onSuccess(Void v) { + ShardMBeanFactory.getShardStatsMBean(shardName).incrementAbortTransactionsCount(); sender .tell(new AbortTransactionReply().toSerializable(), - self); + self); } @Override @@ -148,17 +154,19 @@ public class ThreePhaseCommitCohort extends AbstractUntypedActor { final DOMStoreThreePhaseCommitCohort cohort; final ActorRef shardActor; final CompositeModification modification; + final String shardName; ThreePhaseCommitCohortCreator(DOMStoreThreePhaseCommitCohort cohort, - ActorRef shardActor, CompositeModification modification) { + ActorRef shardActor, CompositeModification modification, String shardName) { this.cohort = cohort; this.shardActor = shardActor; this.modification = modification; + this.shardName = shardName; } @Override public ThreePhaseCommitCohort create() throws Exception { - return new ThreePhaseCommitCohort(cohort, shardActor, modification); + return new ThreePhaseCommitCohort(cohort, shardActor, modification, shardName); } } } 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 c557118b1e..a5be69531d 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 @@ -151,8 +151,7 @@ public class ThreePhaseCommitCohortProxy implements DOMStoreThreePhaseCommitCoho ActorSelection cohort = actorContext.actorSelection(actorPath); - futureList.add(actorContext.executeRemoteOperationAsync(cohort, message, - ActorContext.ASK_DURATION)); + futureList.add(actorContext.executeRemoteOperationAsync(cohort, message)); } return Futures.sequence(futureList, actorContext.getActorSystem().dispatcher()); 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 f447f3c718..a8b20c030e 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 @@ -249,7 +249,7 @@ public class TransactionProxy implements DOMStoreReadWriteTransaction { Preconditions.checkState(transactionType != TransactionType.READ_ONLY, "Modification operation on read-only transaction is not allowed"); Preconditions.checkState(!inReadyState, - "Transaction is sealed - further modifications are allowed"); + "Transaction is sealed - further modifications are not allowed"); } @Override @@ -353,8 +353,7 @@ public class TransactionProxy implements DOMStoreReadWriteTransaction { try { Object response = actorContext.executeShardOperation(shardName, - new CreateTransaction(identifier.toString(),this.transactionType.ordinal() ).toSerializable(), - ActorContext.ASK_DURATION); + new CreateTransaction(identifier.toString(),this.transactionType.ordinal() ).toSerializable()); if (response.getClass().equals(CreateTransactionReply.SERIALIZABLE_CLASS)) { CreateTransactionReply reply = CreateTransactionReply.fromSerializable(response); @@ -472,7 +471,7 @@ public class TransactionProxy implements DOMStoreReadWriteTransaction { // Send the ReadyTransaction message to the Tx actor. final Future replyFuture = actorContext.executeRemoteOperationAsync(getActor(), - new ReadyTransaction().toSerializable(), ActorContext.ASK_DURATION); + new ReadyTransaction().toSerializable()); // Combine all the previously recorded put/merge/delete operation reply Futures and the // ReadyTransactionReply Future into one Future. If any one fails then the combined @@ -532,23 +531,21 @@ public class TransactionProxy implements DOMStoreReadWriteTransaction { public void deleteData(YangInstanceIdentifier path) { LOG.debug("Tx {} deleteData called path = {}", identifier, path); recordedOperationFutures.add(actorContext.executeRemoteOperationAsync(getActor(), - new DeleteData(path).toSerializable(), ActorContext.ASK_DURATION )); + new DeleteData(path).toSerializable() )); } @Override public void mergeData(YangInstanceIdentifier path, NormalizedNode data) { LOG.debug("Tx {} mergeData called path = {}", identifier, path); recordedOperationFutures.add(actorContext.executeRemoteOperationAsync(getActor(), - new MergeData(path, data, schemaContext).toSerializable(), - ActorContext.ASK_DURATION)); + new MergeData(path, data, schemaContext).toSerializable())); } @Override public void writeData(YangInstanceIdentifier path, NormalizedNode data) { LOG.debug("Tx {} writeData called path = {}", identifier, path); recordedOperationFutures.add(actorContext.executeRemoteOperationAsync(getActor(), - new WriteData(path, data, schemaContext).toSerializable(), - ActorContext.ASK_DURATION)); + new WriteData(path, data, schemaContext).toSerializable())); } @Override @@ -612,6 +609,7 @@ public class TransactionProxy implements DOMStoreReadWriteTransaction { returnFuture.setException(new ReadFailedException( "Error reading data for path " + path, failure)); + } else { LOG.debug("Tx {} read operation succeeded", identifier, failure); @@ -633,7 +631,7 @@ public class TransactionProxy implements DOMStoreReadWriteTransaction { }; Future readFuture = actorContext.executeRemoteOperationAsync(getActor(), - new ReadData(path).toSerializable(), ActorContext.ASK_DURATION); + new ReadData(path).toSerializable()); readFuture.onComplete(onComplete, actorContext.getActorSystem().dispatcher()); } @@ -714,7 +712,7 @@ public class TransactionProxy implements DOMStoreReadWriteTransaction { }; Future future = actorContext.executeRemoteOperationAsync(getActor(), - new DataExists(path).toSerializable(), ActorContext.ASK_DURATION); + new DataExists(path).toSerializable()); future.onComplete(onComplete, actorContext.getActorSystem().dispatcher()); } } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/jmx/mbeans/AbstractBaseMBean.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/jmx/mbeans/AbstractBaseMBean.java index a5d7b77a64..3c46935d98 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/jmx/mbeans/AbstractBaseMBean.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/jmx/mbeans/AbstractBaseMBean.java @@ -24,7 +24,7 @@ import java.lang.management.ManagementFactory; /** * All MBeans should extend this class that help in registering and * unregistering the MBeans. - * + * @author Basheeruddin */ diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/jmx/mbeans/shard/ShardMBeanFactory.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/jmx/mbeans/shard/ShardMBeanFactory.java index afca87f0df..2a409c0300 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/jmx/mbeans/shard/ShardMBeanFactory.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/jmx/mbeans/shard/ShardMBeanFactory.java @@ -1,11 +1,18 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ package org.opendaylight.controller.cluster.datastore.jmx.mbeans.shard; import java.util.HashMap; import java.util.Map; /** - * @author: syedbahm - * Date: 7/16/14 + * @author Basheeruddin syedbahm@cisco.com + * */ public class ShardMBeanFactory { private static Map shardMBeans = diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/jmx/mbeans/shard/ShardStats.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/jmx/mbeans/shard/ShardStats.java index c6c1579ce3..22ad8e7f5a 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/jmx/mbeans/shard/ShardStats.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/jmx/mbeans/shard/ShardStats.java @@ -1,3 +1,11 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + package org.opendaylight.controller.cluster.datastore.jmx.mbeans.shard; import org.opendaylight.controller.cluster.datastore.jmx.mbeans.AbstractBaseMBean; @@ -6,37 +14,41 @@ import java.text.SimpleDateFormat; import java.util.Date; /** - * @author: syedbahm + * @author Basheeruddin syedbahm@cisco.com */ public class ShardStats extends AbstractBaseMBean implements ShardStatsMBean { private final String shardName; - private Long committedTransactionsCount = 0L; + private long committedTransactionsCount = 0L; - private Long readOnlyTransactionCount = 0L; + private long readOnlyTransactionCount = 0L; - private Long writeOnlyTransactionCount = 0L; + private long writeOnlyTransactionCount = 0L; - private Long readWriteTransactionCount = 0L; + private long readWriteTransactionCount = 0L; private String leader; private String raftState; - private Long lastLogTerm = -1L; + private long lastLogTerm = -1L; - private Long lastLogIndex = -1L; + private long lastLogIndex = -1L; - private Long currentTerm = -1L; + private long currentTerm = -1L; - private Long commitIndex = -1L; + private long commitIndex = -1L; - private Long lastApplied = -1L; + private long lastApplied = -1L; private Date lastCommittedTransactionTime = new Date(0L); - private Long failedTransactionsCount = 0L; + private long failedTransactionsCount = 0L; + + private long failedReadTransactionsCount = 0L; + + private long abortTransactionsCount = 0L; private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); @@ -52,7 +64,7 @@ public class ShardStats extends AbstractBaseMBean implements ShardStatsMBean { } @Override - public Long getCommittedTransactionsCount() { + public long getCommittedTransactionsCount() { return committedTransactionsCount; } @@ -64,35 +76,35 @@ public class ShardStats extends AbstractBaseMBean implements ShardStatsMBean { return raftState; } - @Override public Long getReadOnlyTransactionCount() { + @Override public long getReadOnlyTransactionCount() { return readOnlyTransactionCount; } - @Override public Long getWriteOnlyTransactionCount() { + @Override public long getWriteOnlyTransactionCount() { return writeOnlyTransactionCount; } - @Override public Long getReadWriteTransactionCount() { + @Override public long getReadWriteTransactionCount() { return readWriteTransactionCount; } - @Override public Long getLastLogIndex() { + @Override public long getLastLogIndex() { return lastLogIndex; } - @Override public Long getLastLogTerm() { + @Override public long getLastLogTerm() { return lastLogTerm; } - @Override public Long getCurrentTerm() { + @Override public long getCurrentTerm() { return currentTerm; } - @Override public Long getCommitIndex() { + @Override public long getCommitIndex() { return commitIndex; } - @Override public Long getLastApplied() { + @Override public long getLastApplied() { return lastApplied; } @@ -102,26 +114,44 @@ public class ShardStats extends AbstractBaseMBean implements ShardStatsMBean { return sdf.format(lastCommittedTransactionTime); } - @Override public Long getFailedTransactionsCount() { + @Override public long getFailedTransactionsCount() { return failedTransactionsCount; } - public Long incrementCommittedTransactionCount() { + @Override public long getFailedReadTransactionsCount() { + return failedReadTransactionsCount; + } + + @Override public long getAbortTransactionsCount() { + return abortTransactionsCount; + } + + public long incrementCommittedTransactionCount() { return committedTransactionsCount++; } - public Long incrementReadOnlyTransactionCount() { + public long incrementReadOnlyTransactionCount() { return readOnlyTransactionCount++; } - public Long incrementWriteOnlyTransactionCount() { + public long incrementWriteOnlyTransactionCount() { return writeOnlyTransactionCount++; } - public Long incrementReadWriteTransactionCount() { + public long incrementReadWriteTransactionCount() { return readWriteTransactionCount++; } + public long incrementFailedTransactionsCount() { + return failedTransactionsCount++; + } + + public long incrementFailedReadTransactionsCount() { + return failedReadTransactionsCount++; + } + + public long incrementAbortTransactionsCount () { return abortTransactionsCount++;} + public void setLeader(String leader) { this.leader = leader; } @@ -130,23 +160,23 @@ public class ShardStats extends AbstractBaseMBean implements ShardStatsMBean { this.raftState = raftState; } - public void setLastLogTerm(Long lastLogTerm) { + public void setLastLogTerm(long lastLogTerm) { this.lastLogTerm = lastLogTerm; } - public void setLastLogIndex(Long lastLogIndex) { + public void setLastLogIndex(long lastLogIndex) { this.lastLogIndex = lastLogIndex; } - public void setCurrentTerm(Long currentTerm) { + public void setCurrentTerm(long currentTerm) { this.currentTerm = currentTerm; } - public void setCommitIndex(Long commitIndex) { + public void setCommitIndex(long commitIndex) { this.commitIndex = commitIndex; } - public void setLastApplied(Long lastApplied) { + public void setLastApplied(long lastApplied) { this.lastApplied = lastApplied; } @@ -171,8 +201,28 @@ public class ShardStats extends AbstractBaseMBean implements ShardStatsMBean { return JMX_CATEGORY_SHARD; } + /** + * resets the counters related to transactions + */ + + public void resetTransactionCounters(){ + committedTransactionsCount = 0L; + + readOnlyTransactionCount = 0L; + + writeOnlyTransactionCount = 0L; + + readWriteTransactionCount = 0L; + + lastCommittedTransactionTime = new Date(0L); + + failedTransactionsCount = 0L; + + failedReadTransactionsCount = 0L; + + abortTransactionsCount = 0L; - public void incrementFailedTransactionsCount() { - this.failedTransactionsCount++; } + + } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/jmx/mbeans/shard/ShardStatsMBean.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/jmx/mbeans/shard/ShardStatsMBean.java index b8b220ee82..c16f8421bf 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/jmx/mbeans/shard/ShardStatsMBean.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/jmx/mbeans/shard/ShardStatsMBean.java @@ -6,30 +6,36 @@ package org.opendaylight.controller.cluster.datastore.jmx.mbeans.shard; public interface ShardStatsMBean { String getShardName(); - Long getCommittedTransactionsCount(); + long getCommittedTransactionsCount(); String getLeader(); String getRaftState(); - Long getReadOnlyTransactionCount(); + long getReadOnlyTransactionCount(); - Long getWriteOnlyTransactionCount(); + long getWriteOnlyTransactionCount(); - Long getReadWriteTransactionCount(); + long getReadWriteTransactionCount(); - Long getLastLogIndex(); + long getLastLogIndex(); - Long getLastLogTerm(); + long getLastLogTerm(); - Long getCurrentTerm(); + long getCurrentTerm(); - Long getCommitIndex(); + long getCommitIndex(); - Long getLastApplied(); + long getLastApplied(); String getLastCommittedTransactionTime(); - Long getFailedTransactionsCount(); + long getFailedTransactionsCount(); + + long getFailedReadTransactionsCount(); + + long getAbortTransactionsCount(); + + void resetTransactionCounters(); } 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 f76430f5a1..b87dc4f608 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,16 +47,17 @@ public class ActorContext { private static final Logger LOG = LoggerFactory.getLogger(ActorContext.class); - public static final FiniteDuration ASK_DURATION = - Duration.create(5, TimeUnit.SECONDS); - public static final Duration AWAIT_DURATION = - Duration.create(5, TimeUnit.SECONDS); + private static final FiniteDuration DEFAULT_OPER_DURATION = Duration.create(5, TimeUnit.SECONDS); + + public static final String MAILBOX = "bounded-mailbox"; private final ActorSystem actorSystem; private final ActorRef shardManager; private final ClusterWrapper clusterWrapper; private final Configuration configuration; private volatile SchemaContext schemaContext; + private FiniteDuration operationDuration = DEFAULT_OPER_DURATION; + private Timeout operationTimeout = new Timeout(operationDuration); public ActorContext(ActorSystem actorSystem, ActorRef shardManager, ClusterWrapper clusterWrapper, @@ -91,6 +92,11 @@ public class ActorContext { } } + public void setOperationTimeout(int timeoutInSeconds) { + operationDuration = Duration.create(timeoutInSeconds, TimeUnit.SECONDS); + operationTimeout = new Timeout(operationDuration); + } + public SchemaContext getSchemaContext() { return schemaContext; } @@ -115,7 +121,7 @@ public class ActorContext { */ public ActorRef findLocalShard(String shardName) { Object result = executeLocalOperation(shardManager, - new FindLocalShard(shardName), ASK_DURATION); + new FindLocalShard(shardName)); if (result instanceof LocalShardFound) { LocalShardFound found = (LocalShardFound) result; @@ -131,7 +137,7 @@ public class ActorContext { public String findPrimaryPath(String shardName) { Object result = executeLocalOperation(shardManager, - new FindPrimary(shardName).toSerializable(), ASK_DURATION); + new FindPrimary(shardName).toSerializable()); if (result.getClass().equals(PrimaryFound.SERIALIZABLE_CLASS)) { PrimaryFound found = PrimaryFound.fromSerializable(result); @@ -149,16 +155,13 @@ public class ActorContext { * * @param actor * @param message - * @param duration * @return The response of the operation */ - public Object executeLocalOperation(ActorRef actor, Object message, - FiniteDuration duration) { - Future future = - ask(actor, message, new Timeout(duration)); + public Object executeLocalOperation(ActorRef actor, Object message) { + Future future = ask(actor, message, operationTimeout); try { - return Await.result(future, AWAIT_DURATION); + return Await.result(future, operationDuration); } catch (Exception e) { throw new TimeoutException("Sending message " + message.getClass().toString() + " to actor " + actor.toString() + " failed" , e); } @@ -169,21 +172,19 @@ public class ActorContext { * * @param actor * @param message - * @param duration * @return */ - public Object executeRemoteOperation(ActorSelection actor, Object message, - FiniteDuration duration) { + public Object executeRemoteOperation(ActorSelection actor, Object message) { LOG.debug("Sending remote message {} to {}", message.getClass().toString(), actor.toString()); - Future future = - ask(actor, message, new Timeout(duration)); + Future future = ask(actor, message, operationTimeout); try { - return Await.result(future, AWAIT_DURATION); + return Await.result(future, operationDuration); } catch (Exception e) { - throw new TimeoutException("Sending message " + message.getClass().toString() + " to actor " + actor.toString() + " failed" , e); + throw new TimeoutException("Sending message " + message.getClass().toString() + + " to actor " + actor.toString() + " failed" , e); } } @@ -192,15 +193,13 @@ public class ActorContext { * * @param actor the ActorSelection * @param message the message to send - * @param duration the maximum amount of time to send he message * @return a Future containing the eventual result */ - public Future executeRemoteOperationAsync(ActorSelection actor, Object message, - FiniteDuration duration) { + public Future executeRemoteOperationAsync(ActorSelection actor, Object message) { LOG.debug("Sending remote message {} to {}", message.getClass().toString(), actor.toString()); - return ask(actor, message, new Timeout(duration)); + return ask(actor, message, operationTimeout); } /** @@ -223,16 +222,14 @@ public class ActorContext { * * @param shardName * @param message - * @param duration * @return * @throws org.opendaylight.controller.cluster.datastore.exceptions.TimeoutException if the message to the remote shard times out * @throws org.opendaylight.controller.cluster.datastore.exceptions.PrimaryNotFoundException if the primary shard is not found */ - public Object executeShardOperation(String shardName, Object message, - FiniteDuration duration) { + public Object executeShardOperation(String shardName, Object message) { ActorSelection primary = findPrimary(shardName); - return executeRemoteOperation(primary, message, duration); + return executeRemoteOperation(primary, message); } /** @@ -244,19 +241,17 @@ public class ActorContext { * * @param shardName the name of the shard on which the operation needs to be executed * @param message the message that needs to be sent to the shard - * @param duration the time duration in which this operation should complete * @return the message that was returned by the local actor on which the * the operation was executed. If a local shard was not found then * null is returned * @throws org.opendaylight.controller.cluster.datastore.exceptions.TimeoutException * if the operation does not complete in a specified time duration */ - public Object executeLocalShardOperation(String shardName, Object message, - FiniteDuration duration) { + public Object executeLocalShardOperation(String shardName, Object message) { ActorRef local = findLocalShard(shardName); if(local != null) { - return executeLocalOperation(local, message, duration); + return executeLocalOperation(local, message); } return null; diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/config/yang/config/distributed_datastore_provider/DistributedConfigDataStoreProviderModule.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/config/yang/config/distributed_datastore_provider/DistributedConfigDataStoreProviderModule.java index f5a0d3783a..e2fbacb461 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/config/yang/config/distributed_datastore_provider/DistributedConfigDataStoreProviderModule.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/config/yang/config/distributed_datastore_provider/DistributedConfigDataStoreProviderModule.java @@ -2,9 +2,12 @@ package org.opendaylight.controller.config.yang.config.distributed_datastore_pro import org.opendaylight.controller.cluster.datastore.DistributedDataStoreFactory; import org.opendaylight.controller.cluster.datastore.DistributedDataStoreProperties; +import org.osgi.framework.BundleContext; public class DistributedConfigDataStoreProviderModule extends org.opendaylight.controller.config.yang.config.distributed_datastore_provider.AbstractDistributedConfigDataStoreProviderModule { + private BundleContext bundleContext; + public DistributedConfigDataStoreProviderModule( org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) { @@ -33,9 +36,15 @@ public class DistributedConfigDataStoreProviderModule extends } return DistributedDataStoreFactory.createInstance("config", getConfigSchemaServiceDependency(), - new DistributedDataStoreProperties(props.getMaxShardDataChangeExecutorPoolSize(), - props.getMaxShardDataChangeExecutorQueueSize(), - props.getMaxShardDataChangeListenerQueueSize(), - props.getShardTransactionIdleTimeoutInMinutes())); + new DistributedDataStoreProperties( + props.getMaxShardDataChangeExecutorPoolSize().getValue(), + props.getMaxShardDataChangeExecutorQueueSize().getValue(), + props.getMaxShardDataChangeListenerQueueSize().getValue(), + props.getShardTransactionIdleTimeoutInMinutes().getValue(), + props.getOperationTimeoutInSeconds().getValue()), bundleContext); + } + + public void setBundleContext(BundleContext bundleContext) { + this.bundleContext = bundleContext; } } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/config/yang/config/distributed_datastore_provider/DistributedConfigDataStoreProviderModuleFactory.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/config/yang/config/distributed_datastore_provider/DistributedConfigDataStoreProviderModuleFactory.java index 67bf599454..0cdaca3a15 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/config/yang/config/distributed_datastore_provider/DistributedConfigDataStoreProviderModuleFactory.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/config/yang/config/distributed_datastore_provider/DistributedConfigDataStoreProviderModuleFactory.java @@ -8,6 +8,29 @@ * Do not modify this file unless it is present under src/main directory */ package org.opendaylight.controller.config.yang.config.distributed_datastore_provider; + +import org.opendaylight.controller.config.api.DependencyResolver; +import org.opendaylight.controller.config.api.DynamicMBeanWithInstance; +import org.opendaylight.controller.config.spi.Module; +import org.osgi.framework.BundleContext; + public class DistributedConfigDataStoreProviderModuleFactory extends org.opendaylight.controller.config.yang.config.distributed_datastore_provider.AbstractDistributedConfigDataStoreProviderModuleFactory { + @Override + public Module createModule(String instanceName, DependencyResolver dependencyResolver, BundleContext bundleContext) { + DistributedConfigDataStoreProviderModule module = (DistributedConfigDataStoreProviderModule)super.createModule(instanceName,dependencyResolver,bundleContext); + module.setBundleContext(bundleContext); + return module; + } + + @Override + public Module createModule(String instanceName, DependencyResolver dependencyResolver, + DynamicMBeanWithInstance old, BundleContext bundleContext) throws Exception { + DistributedConfigDataStoreProviderModule module = (DistributedConfigDataStoreProviderModule)super.createModule(instanceName, dependencyResolver, + old, bundleContext); + module.setBundleContext(bundleContext); + return module; + } + + } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/config/yang/config/distributed_datastore_provider/DistributedOperationalDataStoreProviderModule.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/config/yang/config/distributed_datastore_provider/DistributedOperationalDataStoreProviderModule.java index 443334d11f..c185e871ea 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/config/yang/config/distributed_datastore_provider/DistributedOperationalDataStoreProviderModule.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/config/yang/config/distributed_datastore_provider/DistributedOperationalDataStoreProviderModule.java @@ -2,9 +2,12 @@ package org.opendaylight.controller.config.yang.config.distributed_datastore_pro import org.opendaylight.controller.cluster.datastore.DistributedDataStoreFactory; import org.opendaylight.controller.cluster.datastore.DistributedDataStoreProperties; +import org.osgi.framework.BundleContext; public class DistributedOperationalDataStoreProviderModule extends org.opendaylight.controller.config.yang.config.distributed_datastore_provider.AbstractDistributedOperationalDataStoreProviderModule { + private BundleContext bundleContext; + public DistributedOperationalDataStoreProviderModule( org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) { @@ -34,10 +37,16 @@ public class DistributedOperationalDataStoreProviderModule extends return DistributedDataStoreFactory.createInstance("operational", getOperationalSchemaServiceDependency(), - new DistributedDataStoreProperties(props.getMaxShardDataChangeExecutorPoolSize(), - props.getMaxShardDataChangeExecutorQueueSize(), - props.getMaxShardDataChangeListenerQueueSize(), - props.getShardTransactionIdleTimeoutInMinutes())); + new DistributedDataStoreProperties( + props.getMaxShardDataChangeExecutorPoolSize().getValue(), + props.getMaxShardDataChangeExecutorQueueSize().getValue(), + props.getMaxShardDataChangeListenerQueueSize().getValue(), + props.getShardTransactionIdleTimeoutInMinutes().getValue(), + props.getOperationTimeoutInSeconds().getValue()), bundleContext); + } + + public void setBundleContext(BundleContext bundleContext) { + this.bundleContext = bundleContext; } } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/config/yang/config/distributed_datastore_provider/DistributedOperationalDataStoreProviderModuleFactory.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/config/yang/config/distributed_datastore_provider/DistributedOperationalDataStoreProviderModuleFactory.java index c9965fee09..364fe62923 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/config/yang/config/distributed_datastore_provider/DistributedOperationalDataStoreProviderModuleFactory.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/config/yang/config/distributed_datastore_provider/DistributedOperationalDataStoreProviderModuleFactory.java @@ -8,6 +8,27 @@ * Do not modify this file unless it is present under src/main directory */ package org.opendaylight.controller.config.yang.config.distributed_datastore_provider; + +import org.opendaylight.controller.config.api.DependencyResolver; +import org.opendaylight.controller.config.api.DynamicMBeanWithInstance; +import org.opendaylight.controller.config.spi.Module; +import org.osgi.framework.BundleContext; + public class DistributedOperationalDataStoreProviderModuleFactory extends org.opendaylight.controller.config.yang.config.distributed_datastore_provider.AbstractDistributedOperationalDataStoreProviderModuleFactory { + @Override + public Module createModule(String instanceName, DependencyResolver dependencyResolver, BundleContext bundleContext) { + DistributedOperationalDataStoreProviderModule module = (DistributedOperationalDataStoreProviderModule)super.createModule(instanceName,dependencyResolver,bundleContext); + module.setBundleContext(bundleContext); + return module; + } + + @Override + public Module createModule(String instanceName, DependencyResolver dependencyResolver, + DynamicMBeanWithInstance old, BundleContext bundleContext) throws Exception { + DistributedOperationalDataStoreProviderModule module = (DistributedOperationalDataStoreProviderModule)super.createModule(instanceName, dependencyResolver, + old, bundleContext); + module.setBundleContext(bundleContext); + return module; + } } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/resources/application.conf b/opendaylight/md-sal/sal-distributed-datastore/src/main/resources/application.conf index 8af9bd07d7..c29f93bb07 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/resources/application.conf +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/resources/application.conf @@ -1,5 +1,10 @@ odl-cluster-data { + bounded-mailbox { + mailbox-type = "org.opendaylight.controller.common.actor.MeteredBoundedMailbox" + mailbox-capacity = 1000 + mailbox-push-timeout-time = 100ms + } akka { loggers = ["akka.event.slf4j.Slf4jLogger"] cluster { diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/yang/distributed-datastore-provider.yang b/opendaylight/md-sal/sal-distributed-datastore/src/main/yang/distributed-datastore-provider.yang index a9a8a1ad98..d50be2ca0e 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/yang/distributed-datastore-provider.yang +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/yang/distributed-datastore-provider.yang @@ -36,30 +36,48 @@ module distributed-datastore-provider { config:java-name-prefix DistributedOperationalDataStoreProvider; } + typedef non-zero-uint16-type { + type uint16 { + range "1..max"; + } + } + + typedef operation-timeout-type { + type uint16 { + range "5..max"; + } + } + grouping data-store-properties { leaf max-shard-data-change-executor-queue-size { default 1000; - type uint16; + type non-zero-uint16-type; description "The maximum queue size for each shard's data store data change notification executor."; } leaf max-shard-data-change-executor-pool-size { default 20; - type uint16; + type non-zero-uint16-type; description "The maximum thread pool size for each shard's data store data change notification executor."; } leaf max-shard-data-change-listener-queue-size { default 1000; - type uint16; + type non-zero-uint16-type; description "The maximum queue size for each shard's data store data change listeners."; } leaf shard-transaction-idle-timeout-in-minutes { default 10; - type uint16; + type non-zero-uint16-type; description "The maximum amount of time a shard transaction can be idle without receiving any messages before it self-destructs."; } + + leaf operation-timeout-in-seconds { + default 5; + type operation-timeout-type; + description "The maximum amount of time for akka operations (remote or local) to complete before failing."; + } } // Augments the 'configuration' choice node under modules/module. diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/BasicIntegrationTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/BasicIntegrationTest.java index e70453f2d6..6f131f301f 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/BasicIntegrationTest.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/BasicIntegrationTest.java @@ -62,9 +62,9 @@ public class BasicIntegrationTest extends AbstractActorTest { .shardName("inventory").type("config").build(); final SchemaContext schemaContext = TestModel.createTestContext(); - ShardContext shardContext = new ShardContext(); + DatastoreContext datastoreContext = new DatastoreContext(); - final Props props = Shard.props(identifier, Collections.EMPTY_MAP, shardContext); + final Props props = Shard.props(identifier, Collections.EMPTY_MAP, datastoreContext); final ActorRef shard = getSystem().actorOf(props); new Within(duration("10 seconds")) { diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/DataChangeListenerProxyTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/DataChangeListenerProxyTest.java index e653c3d371..2ed11cfbda 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/DataChangeListenerProxyTest.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/DataChangeListenerProxyTest.java @@ -82,8 +82,7 @@ public class DataChangeListenerProxyTest extends AbstractActorTest { ActorContext testContext = new ActorContext(getSystem(), getSystem().actorOf(Props.create(DoNothingActor.class)), new MockClusterWrapper(), new MockConfiguration()); Object messages = testContext - .executeLocalOperation(actorRef, "messages", - ActorContext.ASK_DURATION); + .executeLocalOperation(actorRef, "messages"); Assert.assertNotNull(messages); 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 c99a7e8c8c..3d0aaa0082 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 @@ -62,8 +62,7 @@ public class DataChangeListenerRegistrationProxyTest extends AbstractActorTest{ ActorContext testContext = new ActorContext(getSystem(), getSystem().actorOf(Props.create(DoNothingActor.class)),new MockClusterWrapper(), new MockConfiguration()); Object messages = testContext - .executeLocalOperation(actorRef, "messages", - ActorContext.ASK_DURATION); + .executeLocalOperation(actorRef, "messages"); Assert.assertNotNull(messages); diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardManagerTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardManagerTest.java index e6f68c032a..1feefd1c1f 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardManagerTest.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardManagerTest.java @@ -42,11 +42,11 @@ public class ShardManagerTest { new JavaTestKit(system) {{ final Props props = ShardManager .props("config", new MockClusterWrapper(), - new MockConfiguration(), new ShardContext()); + new MockConfiguration(), new DatastoreContext()); final TestActorRef subject = TestActorRef.create(system, props); - new Within(duration("1 seconds")) { + new Within(duration("10 seconds")) { @Override protected void run() { @@ -67,11 +67,11 @@ public class ShardManagerTest { new JavaTestKit(system) {{ final Props props = ShardManager .props("config", new MockClusterWrapper(), - new MockConfiguration(), new ShardContext()); + new MockConfiguration(), new DatastoreContext()); final TestActorRef subject = TestActorRef.create(system, props); - new Within(duration("1 seconds")) { + new Within(duration("10 seconds")) { @Override protected void run() { @@ -91,17 +91,17 @@ public class ShardManagerTest { new JavaTestKit(system) {{ final Props props = ShardManager .props("config", new MockClusterWrapper(), - new MockConfiguration(), new ShardContext()); + new MockConfiguration(), new DatastoreContext()); final TestActorRef subject = TestActorRef.create(system, props); - new Within(duration("1 seconds")) { + new Within(duration("10 seconds")) { @Override protected void run() { subject.tell(new FindLocalShard("inventory"), getRef()); - final String out = new ExpectMsg(duration("1 seconds"), "find local") { + final String out = new ExpectMsg(duration("10 seconds"), "find local") { @Override protected String match(Object in) { if (in instanceof LocalShardNotFound) { @@ -128,17 +128,17 @@ public class ShardManagerTest { new JavaTestKit(system) {{ final Props props = ShardManager .props("config", mockClusterWrapper, - new MockConfiguration(), new ShardContext()); + new MockConfiguration(), new DatastoreContext()); final TestActorRef subject = TestActorRef.create(system, props); - new Within(duration("1 seconds")) { + new Within(duration("10 seconds")) { @Override protected void run() { subject.tell(new FindLocalShard(Shard.DEFAULT_NAME), getRef()); - final ActorRef out = new ExpectMsg(duration("1 seconds"), "find local") { + final ActorRef out = new ExpectMsg(duration("10 seconds"), "find local") { @Override protected ActorRef match(Object in) { if (in instanceof LocalShardFound) { @@ -164,12 +164,12 @@ public class ShardManagerTest { new JavaTestKit(system) {{ final Props props = ShardManager .props("config", new MockClusterWrapper(), - new MockConfiguration(), new ShardContext()); + new MockConfiguration(), new DatastoreContext()); final TestActorRef subject = TestActorRef.create(system, props); // the run() method needs to finish within 3 seconds - new Within(duration("1 seconds")) { + new Within(duration("10 seconds")) { @Override protected void run() { @@ -204,12 +204,12 @@ public class ShardManagerTest { new JavaTestKit(system) {{ final Props props = ShardManager .props("config", new MockClusterWrapper(), - new MockConfiguration(), new ShardContext()); + new MockConfiguration(), new DatastoreContext()); final TestActorRef subject = TestActorRef.create(system, props); // the run() method needs to finish within 3 seconds - new Within(duration("1 seconds")) { + new Within(duration("10 seconds")) { @Override protected void run() { diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardTest.java index fc45efcdea..4466e50f96 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardTest.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardTest.java @@ -35,7 +35,7 @@ import static org.junit.Assert.assertTrue; public class ShardTest extends AbstractActorTest { - private static final ShardContext shardContext = new ShardContext(); + private static final DatastoreContext DATA_STORE_CONTEXT = new DatastoreContext(); @Test public void testOnReceiveCreateTransactionChain() throws Exception { @@ -44,7 +44,7 @@ public class ShardTest extends AbstractActorTest { ShardIdentifier.builder().memberName("member-1") .shardName("inventory").type("config").build(); - final Props props = Shard.props(identifier, Collections.EMPTY_MAP, shardContext); + final Props props = Shard.props(identifier, Collections.EMPTY_MAP, DATA_STORE_CONTEXT); final ActorRef subject = getSystem().actorOf(props, "testCreateTransactionChain"); @@ -103,7 +103,7 @@ public class ShardTest extends AbstractActorTest { ShardIdentifier.builder().memberName("member-1") .shardName("inventory").type("config").build(); - final Props props = Shard.props(identifier, Collections.EMPTY_MAP, shardContext); + final Props props = Shard.props(identifier, Collections.EMPTY_MAP, DATA_STORE_CONTEXT); final ActorRef subject = getSystem().actorOf(props, "testRegisterChangeListener"); @@ -165,7 +165,7 @@ public class ShardTest extends AbstractActorTest { ShardIdentifier.builder().memberName("member-1") .shardName("inventory").type("config").build(); - final Props props = Shard.props(identifier, Collections.EMPTY_MAP, shardContext); + final Props props = Shard.props(identifier, Collections.EMPTY_MAP, DATA_STORE_CONTEXT); final ActorRef subject = getSystem().actorOf(props, "testCreateTransaction"); @@ -227,7 +227,7 @@ public class ShardTest extends AbstractActorTest { .shardName("inventory").type("config").build(); peerAddresses.put(identifier, null); - final Props props = Shard.props(identifier, peerAddresses, shardContext); + final Props props = Shard.props(identifier, peerAddresses, DATA_STORE_CONTEXT); final ActorRef subject = getSystem().actorOf(props, "testPeerAddressResolved"); diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardTransactionChainTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardTransactionChainTest.java index 0262b8c145..71eb1f1603 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardTransactionChainTest.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardTransactionChainTest.java @@ -28,7 +28,9 @@ public class ShardTransactionChainTest extends AbstractActorTest { private static final SchemaContext testSchemaContext = TestModel.createTestContext(); - private static final ShardContext shardContext = new ShardContext(); + private static final DatastoreContext DATA_STORE_CONTEXT = new DatastoreContext(); + + private static final String mockShardName = "mockShardName"; @BeforeClass public static void staticSetup() { @@ -39,7 +41,7 @@ public class ShardTransactionChainTest extends AbstractActorTest { public void testOnReceiveCreateTransaction() throws Exception { new JavaTestKit(getSystem()) {{ final Props props = ShardTransactionChain.props(store.createTransactionChain(), - testSchemaContext, shardContext); + testSchemaContext, DATA_STORE_CONTEXT, mockShardName); final ActorRef subject = getSystem().actorOf(props, "testCreateTransaction"); new Within(duration("1 seconds")) { @@ -77,7 +79,7 @@ public class ShardTransactionChainTest extends AbstractActorTest { public void testOnReceiveCloseTransactionChain() throws Exception { new JavaTestKit(getSystem()) {{ final Props props = ShardTransactionChain.props(store.createTransactionChain(), - testSchemaContext, shardContext); + testSchemaContext, DATA_STORE_CONTEXT,mockShardName ); final ActorRef subject = getSystem().actorOf(props, "testCloseTransactionChain"); new Within(duration("1 seconds")) { diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardTransactionFailureTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardTransactionFailureTest.java index 8a6534b87e..4fe60f6467 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardTransactionFailureTest.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardTransactionFailureTest.java @@ -57,7 +57,7 @@ public class ShardTransactionFailureTest extends AbstractActorTest { ShardIdentifier.builder().memberName("member-1") .shardName("inventory").type("operational").build(); - private final ShardContext shardContext = new ShardContext(); + private final DatastoreContext datastoreContext = new DatastoreContext(); @BeforeClass public static void staticSetup() { @@ -69,9 +69,9 @@ public class ShardTransactionFailureTest extends AbstractActorTest { throws Throwable { final ActorRef shard = - getSystem().actorOf(Shard.props(SHARD_IDENTIFIER, Collections.EMPTY_MAP, new ShardContext())); + getSystem().actorOf(Shard.props(SHARD_IDENTIFIER, Collections.EMPTY_MAP, new DatastoreContext())); final Props props = ShardTransaction.props(store.newReadOnlyTransaction(), shard, - testSchemaContext, shardContext); + testSchemaContext, datastoreContext,SHARD_IDENTIFIER.toString()); final TestActorRef subject = TestActorRef .create(getSystem(), props, @@ -99,9 +99,9 @@ public class ShardTransactionFailureTest extends AbstractActorTest { throws Throwable { final ActorRef shard = - getSystem().actorOf(Shard.props(SHARD_IDENTIFIER, Collections.EMPTY_MAP, new ShardContext())); + getSystem().actorOf(Shard.props(SHARD_IDENTIFIER, Collections.EMPTY_MAP, new DatastoreContext())); final Props props = ShardTransaction.props(store.newReadWriteTransaction(), shard, - testSchemaContext, shardContext); + testSchemaContext, datastoreContext,SHARD_IDENTIFIER.toString()); final TestActorRef subject = TestActorRef .create(getSystem(), props, @@ -129,9 +129,9 @@ public class ShardTransactionFailureTest extends AbstractActorTest { throws Throwable { final ActorRef shard = - getSystem().actorOf(Shard.props(SHARD_IDENTIFIER, Collections.EMPTY_MAP, new ShardContext())); + getSystem().actorOf(Shard.props(SHARD_IDENTIFIER, Collections.EMPTY_MAP, new DatastoreContext())); final Props props = ShardTransaction.props(store.newReadWriteTransaction(), shard, - testSchemaContext, shardContext); + testSchemaContext, datastoreContext,SHARD_IDENTIFIER.toString()); final TestActorRef subject = TestActorRef .create(getSystem(), props, @@ -159,9 +159,9 @@ public class ShardTransactionFailureTest extends AbstractActorTest { final ActorRef shard = - getSystem().actorOf(Shard.props(SHARD_IDENTIFIER, Collections.EMPTY_MAP, new ShardContext())); + getSystem().actorOf(Shard.props(SHARD_IDENTIFIER, Collections.EMPTY_MAP, new DatastoreContext())); final Props props = ShardTransaction.props(store.newWriteOnlyTransaction(), shard, - testSchemaContext, shardContext); + testSchemaContext, datastoreContext,SHARD_IDENTIFIER.toString()); final TestActorRef subject = TestActorRef .create(getSystem(), props, @@ -192,9 +192,9 @@ public class ShardTransactionFailureTest extends AbstractActorTest { final ActorRef shard = - getSystem().actorOf(Shard.props(SHARD_IDENTIFIER, Collections.EMPTY_MAP, new ShardContext())); + getSystem().actorOf(Shard.props(SHARD_IDENTIFIER, Collections.EMPTY_MAP, new DatastoreContext())); final Props props = ShardTransaction.props(store.newReadWriteTransaction(), shard, - testSchemaContext, shardContext); + testSchemaContext, datastoreContext,SHARD_IDENTIFIER.toString()); final TestActorRef subject = TestActorRef .create(getSystem(), props, @@ -230,9 +230,9 @@ public class ShardTransactionFailureTest extends AbstractActorTest { final ActorRef shard = - getSystem().actorOf(Shard.props(SHARD_IDENTIFIER, Collections.EMPTY_MAP, new ShardContext())); + getSystem().actorOf(Shard.props(SHARD_IDENTIFIER, Collections.EMPTY_MAP, new DatastoreContext())); final Props props = ShardTransaction.props(store.newReadWriteTransaction(), shard, - testSchemaContext, shardContext); + testSchemaContext, datastoreContext,SHARD_IDENTIFIER.toString()); final TestActorRef subject = TestActorRef .create(getSystem(), props, "testNegativeMergeTransactionReady"); @@ -263,9 +263,9 @@ public class ShardTransactionFailureTest extends AbstractActorTest { final ActorRef shard = - getSystem().actorOf(Shard.props(SHARD_IDENTIFIER, Collections.EMPTY_MAP, new ShardContext())); + getSystem().actorOf(Shard.props(SHARD_IDENTIFIER, Collections.EMPTY_MAP, new DatastoreContext())); final Props props = ShardTransaction.props(store.newReadWriteTransaction(), shard, - testSchemaContext, shardContext); + testSchemaContext, datastoreContext,SHARD_IDENTIFIER.toString()); final TestActorRef subject = TestActorRef .create(getSystem(), props, diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardTransactionTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardTransactionTest.java index 1dd824568a..ff2ee08f94 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardTransactionTest.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardTransactionTest.java @@ -60,7 +60,7 @@ public class ShardTransactionTest extends AbstractActorTest { ShardIdentifier.builder().memberName("member-1") .shardName("inventory").type("config").build(); - private ShardContext shardContext = new ShardContext(); + private DatastoreContext datastoreContext = new DatastoreContext(); @BeforeClass public static void staticSetup() { @@ -71,9 +71,9 @@ public class ShardTransactionTest extends AbstractActorTest { public void testOnReceiveReadData() throws Exception { new JavaTestKit(getSystem()) {{ final ActorRef shard = getSystem().actorOf(Shard.props(SHARD_IDENTIFIER, - Collections.EMPTY_MAP, new ShardContext())); + Collections.EMPTY_MAP, new DatastoreContext())); final Props props = ShardTransaction.props(store.newReadOnlyTransaction(), shard, - testSchemaContext, shardContext); + testSchemaContext, datastoreContext,SHARD_IDENTIFIER.toString()); final ActorRef subject = getSystem().actorOf(props, "testReadData"); new Within(duration("1 seconds")) { @@ -114,9 +114,9 @@ public class ShardTransactionTest extends AbstractActorTest { public void testOnReceiveReadDataWhenDataNotFound() throws Exception { new JavaTestKit(getSystem()) {{ final ActorRef shard = getSystem().actorOf(Shard.props(SHARD_IDENTIFIER, - Collections.EMPTY_MAP, new ShardContext())); + Collections.EMPTY_MAP, new DatastoreContext())); final Props props = ShardTransaction.props( store.newReadOnlyTransaction(), shard, - testSchemaContext, shardContext); + testSchemaContext, datastoreContext,SHARD_IDENTIFIER.toString()); final ActorRef subject = getSystem().actorOf(props, "testReadDataWhenDataNotFound"); new Within(duration("1 seconds")) { @@ -158,9 +158,9 @@ public class ShardTransactionTest extends AbstractActorTest { public void testOnReceiveDataExistsPositive() throws Exception { new JavaTestKit(getSystem()) {{ final ActorRef shard = getSystem().actorOf(Shard.props(SHARD_IDENTIFIER, - Collections.EMPTY_MAP, new ShardContext())); + Collections.EMPTY_MAP, new DatastoreContext())); final Props props = ShardTransaction.props(store.newReadOnlyTransaction(), shard, - testSchemaContext, shardContext); + testSchemaContext, datastoreContext,SHARD_IDENTIFIER.toString()); final ActorRef subject = getSystem().actorOf(props, "testDataExistsPositive"); new Within(duration("1 seconds")) { @@ -201,9 +201,9 @@ public class ShardTransactionTest extends AbstractActorTest { public void testOnReceiveDataExistsNegative() throws Exception { new JavaTestKit(getSystem()) {{ final ActorRef shard = getSystem().actorOf(Shard.props(SHARD_IDENTIFIER, - Collections.EMPTY_MAP, new ShardContext())); + Collections.EMPTY_MAP, new DatastoreContext())); final Props props = ShardTransaction.props(store.newReadOnlyTransaction(), shard, - testSchemaContext, shardContext); + testSchemaContext, datastoreContext,SHARD_IDENTIFIER.toString()); final ActorRef subject = getSystem().actorOf(props, "testDataExistsNegative"); new Within(duration("1 seconds")) { @@ -279,9 +279,9 @@ public class ShardTransactionTest extends AbstractActorTest { public void testOnReceiveWriteData() throws Exception { new JavaTestKit(getSystem()) {{ final ActorRef shard = getSystem().actorOf(Shard.props(SHARD_IDENTIFIER, - Collections.EMPTY_MAP, new ShardContext())); + Collections.EMPTY_MAP, new DatastoreContext())); final Props props = ShardTransaction.props(store.newWriteOnlyTransaction(), shard, - testSchemaContext, shardContext); + testSchemaContext, datastoreContext,SHARD_IDENTIFIER.toString()); final ActorRef subject = getSystem().actorOf(props, "testWriteData"); @@ -320,9 +320,9 @@ public class ShardTransactionTest extends AbstractActorTest { public void testOnReceiveMergeData() throws Exception { new JavaTestKit(getSystem()) {{ final ActorRef shard = getSystem().actorOf(Shard.props(SHARD_IDENTIFIER, - Collections.EMPTY_MAP, new ShardContext())); + Collections.EMPTY_MAP, new DatastoreContext())); final Props props = ShardTransaction.props(store.newReadWriteTransaction(), shard, - testSchemaContext, shardContext); + testSchemaContext, datastoreContext,SHARD_IDENTIFIER.toString()); final ActorRef subject = getSystem().actorOf(props, "testMergeData"); @@ -362,9 +362,9 @@ public class ShardTransactionTest extends AbstractActorTest { public void testOnReceiveDeleteData() throws Exception { new JavaTestKit(getSystem()) {{ final ActorRef shard = getSystem().actorOf(Shard.props(SHARD_IDENTIFIER, - Collections.EMPTY_MAP, new ShardContext())); + Collections.EMPTY_MAP, new DatastoreContext())); final Props props = ShardTransaction.props( store.newWriteOnlyTransaction(), shard, - testSchemaContext, shardContext); + testSchemaContext, datastoreContext,SHARD_IDENTIFIER.toString()); final ActorRef subject = getSystem().actorOf(props, "testDeleteData"); @@ -402,9 +402,9 @@ public class ShardTransactionTest extends AbstractActorTest { public void testOnReceiveReadyTransaction() throws Exception { new JavaTestKit(getSystem()) {{ final ActorRef shard = getSystem().actorOf(Shard.props(SHARD_IDENTIFIER, - Collections.EMPTY_MAP, new ShardContext())); + Collections.EMPTY_MAP, new DatastoreContext())); final Props props = ShardTransaction.props( store.newReadWriteTransaction(), shard, - testSchemaContext, shardContext); + testSchemaContext, datastoreContext,SHARD_IDENTIFIER.toString()); final ActorRef subject = getSystem().actorOf(props, "testReadyTransaction"); @@ -441,9 +441,9 @@ public class ShardTransactionTest extends AbstractActorTest { public void testOnReceiveCloseTransaction() throws Exception { new JavaTestKit(getSystem()) {{ final ActorRef shard = getSystem().actorOf(Shard.props(SHARD_IDENTIFIER, - Collections.EMPTY_MAP, new ShardContext())); + Collections.EMPTY_MAP, new DatastoreContext())); final Props props = ShardTransaction.props(store.newReadWriteTransaction(), shard, - testSchemaContext, shardContext); + testSchemaContext, datastoreContext,SHARD_IDENTIFIER.toString()); final ActorRef subject = getSystem().actorOf(props, "testCloseTransaction"); @@ -492,9 +492,9 @@ public class ShardTransactionTest extends AbstractActorTest { @Test(expected=UnknownMessageException.class) public void testNegativePerformingWriteOperationOnReadTransaction() throws Exception { final ActorRef shard = getSystem().actorOf(Shard.props(SHARD_IDENTIFIER, - Collections.EMPTY_MAP, new ShardContext())); + Collections.EMPTY_MAP, new DatastoreContext())); final Props props = ShardTransaction.props(store.newReadOnlyTransaction(), shard, - testSchemaContext, shardContext); + testSchemaContext, datastoreContext,SHARD_IDENTIFIER.toString()); final TestActorRef subject = TestActorRef.apply(props,getSystem()); subject.receive(new DeleteData(TestModel.TEST_PATH).toSerializable(), ActorRef.noSender()); @@ -503,14 +503,14 @@ public class ShardTransactionTest extends AbstractActorTest { @Test public void testShardTransactionInactivity() { - shardContext = new ShardContext(InMemoryDOMDataStoreConfigProperties.getDefault(), + datastoreContext = new DatastoreContext(InMemoryDOMDataStoreConfigProperties.getDefault(), Duration.create(500, TimeUnit.MILLISECONDS)); new JavaTestKit(getSystem()) {{ final ActorRef shard = getSystem().actorOf(Shard.props(SHARD_IDENTIFIER, - Collections.EMPTY_MAP, new ShardContext())); + Collections.EMPTY_MAP, new DatastoreContext())); final Props props = ShardTransaction.props(store.newReadWriteTransaction(), shard, - testSchemaContext, shardContext); + testSchemaContext, datastoreContext,SHARD_IDENTIFIER.toString()); final ActorRef subject = getSystem().actorOf(props, "testShardTransactionInactivity"); diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ThreePhaseCommitCohortFailureTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ThreePhaseCommitCohortFailureTest.java index 672166c442..e39b9abd65 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ThreePhaseCommitCohortFailureTest.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ThreePhaseCommitCohortFailureTest.java @@ -13,6 +13,7 @@ package org.opendaylight.controller.cluster.datastore; import akka.actor.ActorRef; import akka.actor.Props; import akka.testkit.TestActorRef; +import akka.util.Timeout; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListeningExecutorService; @@ -63,7 +64,8 @@ public class ThreePhaseCommitCohortFailureTest extends AbstractActorTest { ShardIdentifier.builder().memberName("member-1") .shardName("inventory").type("config").build(); - private final ShardContext shardContext = new ShardContext(); + private final DatastoreContext datastoreContext = new DatastoreContext(); + @BeforeClass public static void staticSetup() { @@ -77,13 +79,13 @@ public class ThreePhaseCommitCohortFailureTest extends AbstractActorTest { public void testNegativeAbortResultsInException() throws Exception { final ActorRef shard = getSystem().actorOf(Shard.props(SHARD_IDENTIFIER, - Collections.EMPTY_MAP, shardContext)); + Collections.EMPTY_MAP, datastoreContext)); final DOMStoreThreePhaseCommitCohort mockCohort = Mockito .mock(DOMStoreThreePhaseCommitCohort.class); final CompositeModification mockComposite = Mockito.mock(CompositeModification.class); final Props props = - ThreePhaseCommitCohort.props(mockCohort, shard, mockComposite); + ThreePhaseCommitCohort.props(mockCohort, shard, mockComposite,SHARD_IDENTIFIER.toString()); final TestActorRef subject = TestActorRef .create(getSystem(), props, @@ -106,13 +108,13 @@ public class ThreePhaseCommitCohortFailureTest extends AbstractActorTest { public void testNegativeCanCommitResultsInException() throws Exception { final ActorRef shard = getSystem().actorOf(Shard.props(SHARD_IDENTIFIER, - Collections.EMPTY_MAP, shardContext)); + Collections.EMPTY_MAP, datastoreContext)); final DOMStoreThreePhaseCommitCohort mockCohort = Mockito .mock(DOMStoreThreePhaseCommitCohort.class); final CompositeModification mockComposite = Mockito.mock(CompositeModification.class); final Props props = - ThreePhaseCommitCohort.props(mockCohort, shard, mockComposite); + ThreePhaseCommitCohort.props(mockCohort, shard, mockComposite,SHARD_IDENTIFIER.toString()); final TestActorRef subject = TestActorRef .create(getSystem(), props, @@ -138,13 +140,13 @@ public class ThreePhaseCommitCohortFailureTest extends AbstractActorTest { public void testNegativePreCommitResultsInException() throws Exception { final ActorRef shard = getSystem().actorOf(Shard.props(SHARD_IDENTIFIER, - Collections.EMPTY_MAP, shardContext)); + Collections.EMPTY_MAP, datastoreContext)); final DOMStoreThreePhaseCommitCohort mockCohort = Mockito .mock(DOMStoreThreePhaseCommitCohort.class); final CompositeModification mockComposite = Mockito.mock(CompositeModification.class); final Props props = - ThreePhaseCommitCohort.props(mockCohort, shard, mockComposite); + ThreePhaseCommitCohort.props(mockCohort, shard, mockComposite,SHARD_IDENTIFIER.toString()); final TestActorRef subject = TestActorRef .create(getSystem(), props, @@ -168,12 +170,12 @@ public class ThreePhaseCommitCohortFailureTest extends AbstractActorTest { public void testNegativeCommitResultsInException() throws Exception { final TestActorRef subject = TestActorRef.create(getSystem(), - Shard.props(SHARD_IDENTIFIER, Collections.EMPTY_MAP, shardContext), + Shard.props(SHARD_IDENTIFIER, Collections.EMPTY_MAP, datastoreContext), "testNegativeCommitResultsInException"); final ActorRef shardTransaction = getSystem().actorOf(ShardTransaction.props(store.newReadWriteTransaction(), subject, - testSchemaContext, shardContext)); + testSchemaContext, datastoreContext,SHARD_IDENTIFIER.toString())); ShardTransactionMessages.WriteData writeData = ShardTransactionMessages.WriteData.newBuilder() @@ -184,17 +186,17 @@ public class ThreePhaseCommitCohortFailureTest extends AbstractActorTest { ).build(); + Timeout askTimeout = new Timeout(ASK_RESULT_DURATION); + //This is done so that Modification list is updated which is used during commit - Future future = - akka.pattern.Patterns.ask(shardTransaction, writeData, 3000); + Future future = akka.pattern.Patterns.ask(shardTransaction, writeData, askTimeout); //ready transaction creates the cohort so that we get into the //block where in commmit is done ShardTransactionMessages.ReadyTransaction readyTransaction = ShardTransactionMessages.ReadyTransaction.newBuilder().build(); - future = - akka.pattern.Patterns.ask(shardTransaction, readyTransaction, 3000); + future = akka.pattern.Patterns.ask(shardTransaction, readyTransaction, askTimeout); //but when the message is sent it will have the MockCommit object //so that we can simulate throwing of exception @@ -215,10 +217,7 @@ public class ThreePhaseCommitCohortFailureTest extends AbstractActorTest { when(mockModification.toSerializable()).thenReturn( PersistentMessages.CompositeModification.newBuilder().build()); - future = - akka.pattern.Patterns.ask(subject, - mockForwardCommitTransaction - , 3000); + future = akka.pattern.Patterns.ask(subject, mockForwardCommitTransaction, askTimeout); Await.result(future, ASK_RESULT_DURATION); } 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 adb12b298e..1cd0f85fa1 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 @@ -35,7 +35,6 @@ import org.opendaylight.controller.cluster.datastore.utils.ActorContext; import org.opendaylight.controller.cluster.datastore.utils.DoNothingActor; import scala.concurrent.Future; -import scala.concurrent.duration.FiniteDuration; import java.util.List; import java.util.concurrent.ExecutionException; @@ -93,12 +92,12 @@ public class ThreePhaseCommitCohortProxyTest extends AbstractActorTest { } stubber.when(actorContext).executeRemoteOperationAsync(any(ActorSelection.class), - isA(requestType), any(FiniteDuration.class)); + isA(requestType)); } private void verifyCohortInvocations(int nCohorts, Class requestType) { verify(actorContext, times(nCohorts)).executeRemoteOperationAsync( - any(ActorSelection.class), isA(requestType), any(FiniteDuration.class)); + any(ActorSelection.class), isA(requestType)); } private void propagateExecutionExceptionCause(ListenableFuture future) throws Throwable { 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 f69ae88ec8..e5392e0251 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 @@ -56,8 +56,6 @@ import org.opendaylight.yangtools.yang.model.api.SchemaContext; import scala.concurrent.Await; import scala.concurrent.Future; import scala.concurrent.duration.Duration; -import scala.concurrent.duration.FiniteDuration; - import java.util.List; import java.util.concurrent.TimeUnit; @@ -216,10 +214,6 @@ public class TransactionProxyTest extends AbstractActorTest { return getSystem().actorSelection(actorRef.path()); } - private FiniteDuration anyDuration() { - return any(FiniteDuration.class); - } - private CreateTransactionReply createTransactionReply(ActorRef actorRef){ return CreateTransactionReply.newBuilder() .setTransactionActorPath(actorRef.path().toString()) @@ -232,7 +226,7 @@ public class TransactionProxyTest extends AbstractActorTest { when(mockActorContext).actorSelection(actorRef.path().toString()); doReturn(createTransactionReply(actorRef)).when(mockActorContext). executeShardOperation(eq(DefaultShardStrategy.DEFAULT_SHARD), - eqCreateTransaction(memberName, type), anyDuration()); + eqCreateTransaction(memberName, type)); doReturn(actorRef.path().toString()).when(mockActorContext).resolvePath( anyString(), eq(actorRef.path().toString())); doReturn(actorRef.path()).when(mockActorContext).actorFor(actorRef.path().toString()); @@ -259,7 +253,7 @@ public class TransactionProxyTest extends AbstractActorTest { READ_ONLY); doReturn(readDataReply(null)).when(mockActorContext).executeRemoteOperationAsync( - eq(actorSelection(actorRef)), eqReadData(), anyDuration()); + eq(actorSelection(actorRef)), eqReadData()); Optional> readOptional = transactionProxy.read( TestModel.TEST_PATH).get(5, TimeUnit.SECONDS); @@ -269,7 +263,7 @@ public class TransactionProxyTest extends AbstractActorTest { NormalizedNode expectedNode = ImmutableNodes.containerNode(TestModel.TEST_QNAME); doReturn(readDataReply(expectedNode)).when(mockActorContext).executeRemoteOperationAsync( - eq(actorSelection(actorRef)), eqReadData(), anyDuration()); + eq(actorSelection(actorRef)), eqReadData()); readOptional = transactionProxy.read(TestModel.TEST_PATH).get(5, TimeUnit.SECONDS); @@ -283,7 +277,7 @@ public class TransactionProxyTest extends AbstractActorTest { setupActorContextWithInitialCreateTransaction(READ_ONLY); doReturn(Futures.successful(new Object())).when(mockActorContext). - executeRemoteOperationAsync(any(ActorSelection.class), any(), anyDuration()); + executeRemoteOperationAsync(any(ActorSelection.class), any()); TransactionProxy transactionProxy = new TransactionProxy(mockActorContext, READ_ONLY); @@ -296,7 +290,7 @@ public class TransactionProxyTest extends AbstractActorTest { setupActorContextWithInitialCreateTransaction(READ_ONLY); doReturn(Futures.failed(new TestException())).when(mockActorContext). - executeRemoteOperationAsync(any(ActorSelection.class), any(), anyDuration()); + executeRemoteOperationAsync(any(ActorSelection.class), any()); TransactionProxy transactionProxy = new TransactionProxy(mockActorContext, READ_ONLY); @@ -308,7 +302,7 @@ public class TransactionProxyTest extends AbstractActorTest { throws Throwable { doThrow(exToThrow).when(mockActorContext).executeShardOperation( - anyString(), any(), anyDuration()); + anyString(), any()); TransactionProxy transactionProxy = new TransactionProxy(mockActorContext, READ_ONLY); @@ -348,14 +342,13 @@ public class TransactionProxyTest extends AbstractActorTest { NormalizedNode nodeToWrite = ImmutableNodes.containerNode(TestModel.TEST_QNAME); doReturn(writeDataReply()).when(mockActorContext).executeRemoteOperationAsync( - eq(actorSelection(actorRef)), eqWriteData(nodeToWrite), anyDuration()); + eq(actorSelection(actorRef)), eqWriteData(nodeToWrite)); doReturn(Futures.failed(new TestException())).when(mockActorContext). - executeRemoteOperationAsync(eq(actorSelection(actorRef)), eqDeleteData(), - anyDuration()); + executeRemoteOperationAsync(eq(actorSelection(actorRef)), eqDeleteData()); doReturn(readDataReply(null)).when(mockActorContext).executeRemoteOperationAsync( - eq(actorSelection(actorRef)), eqReadData(), anyDuration()); + eq(actorSelection(actorRef)), eqReadData()); TransactionProxy transactionProxy = new TransactionProxy(mockActorContext, READ_WRITE); @@ -368,7 +361,7 @@ public class TransactionProxyTest extends AbstractActorTest { propagateReadFailedExceptionCause(transactionProxy.read(TestModel.TEST_PATH)); } finally { verify(mockActorContext, times(0)).executeRemoteOperationAsync( - eq(actorSelection(actorRef)), eqReadData(), anyDuration()); + eq(actorSelection(actorRef)), eqReadData()); } } @@ -379,10 +372,10 @@ public class TransactionProxyTest extends AbstractActorTest { NormalizedNode expectedNode = ImmutableNodes.containerNode(TestModel.TEST_QNAME); doReturn(writeDataReply()).when(mockActorContext).executeRemoteOperationAsync( - eq(actorSelection(actorRef)), eqWriteData(expectedNode), anyDuration()); + eq(actorSelection(actorRef)), eqWriteData(expectedNode)); doReturn(readDataReply(expectedNode)).when(mockActorContext).executeRemoteOperationAsync( - eq(actorSelection(actorRef)), eqReadData(), anyDuration()); + eq(actorSelection(actorRef)), eqReadData()); TransactionProxy transactionProxy = new TransactionProxy(mockActorContext, READ_WRITE); @@ -414,14 +407,14 @@ public class TransactionProxyTest extends AbstractActorTest { READ_ONLY); doReturn(dataExistsReply(false)).when(mockActorContext).executeRemoteOperationAsync( - eq(actorSelection(actorRef)), eqDataExists(), anyDuration()); + eq(actorSelection(actorRef)), eqDataExists()); Boolean exists = transactionProxy.exists(TestModel.TEST_PATH).checkedGet(); assertEquals("Exists response", false, exists); doReturn(dataExistsReply(true)).when(mockActorContext).executeRemoteOperationAsync( - eq(actorSelection(actorRef)), eqDataExists(), anyDuration()); + eq(actorSelection(actorRef)), eqDataExists()); exists = transactionProxy.exists(TestModel.TEST_PATH).checkedGet(); @@ -443,7 +436,7 @@ public class TransactionProxyTest extends AbstractActorTest { setupActorContextWithInitialCreateTransaction(READ_ONLY); doReturn(Futures.successful(new Object())).when(mockActorContext). - executeRemoteOperationAsync(any(ActorSelection.class), any(), anyDuration()); + executeRemoteOperationAsync(any(ActorSelection.class), any()); TransactionProxy transactionProxy = new TransactionProxy(mockActorContext, READ_ONLY); @@ -456,7 +449,7 @@ public class TransactionProxyTest extends AbstractActorTest { setupActorContextWithInitialCreateTransaction(READ_ONLY); doReturn(Futures.failed(new TestException())).when(mockActorContext). - executeRemoteOperationAsync(any(ActorSelection.class), any(), anyDuration()); + executeRemoteOperationAsync(any(ActorSelection.class), any()); TransactionProxy transactionProxy = new TransactionProxy(mockActorContext, READ_ONLY); @@ -471,14 +464,13 @@ public class TransactionProxyTest extends AbstractActorTest { NormalizedNode nodeToWrite = ImmutableNodes.containerNode(TestModel.TEST_QNAME); doReturn(writeDataReply()).when(mockActorContext).executeRemoteOperationAsync( - eq(actorSelection(actorRef)), eqWriteData(nodeToWrite), anyDuration()); + eq(actorSelection(actorRef)), eqWriteData(nodeToWrite)); doReturn(Futures.failed(new TestException())).when(mockActorContext). - executeRemoteOperationAsync(eq(actorSelection(actorRef)), eqDeleteData(), - anyDuration()); + executeRemoteOperationAsync(eq(actorSelection(actorRef)), eqDeleteData()); doReturn(dataExistsReply(false)).when(mockActorContext).executeRemoteOperationAsync( - eq(actorSelection(actorRef)), eqDataExists(), anyDuration()); + eq(actorSelection(actorRef)), eqDataExists()); TransactionProxy transactionProxy = new TransactionProxy(mockActorContext, READ_WRITE); @@ -491,7 +483,7 @@ public class TransactionProxyTest extends AbstractActorTest { propagateReadFailedExceptionCause(transactionProxy.exists(TestModel.TEST_PATH)); } finally { verify(mockActorContext, times(0)).executeRemoteOperationAsync( - eq(actorSelection(actorRef)), eqDataExists(), anyDuration()); + eq(actorSelection(actorRef)), eqDataExists()); } } @@ -502,10 +494,10 @@ public class TransactionProxyTest extends AbstractActorTest { NormalizedNode nodeToWrite = ImmutableNodes.containerNode(TestModel.TEST_QNAME); doReturn(writeDataReply()).when(mockActorContext).executeRemoteOperationAsync( - eq(actorSelection(actorRef)), eqWriteData(nodeToWrite), anyDuration()); + eq(actorSelection(actorRef)), eqWriteData(nodeToWrite)); doReturn(dataExistsReply(true)).when(mockActorContext).executeRemoteOperationAsync( - eq(actorSelection(actorRef)), eqDataExists(), anyDuration()); + eq(actorSelection(actorRef)), eqDataExists()); TransactionProxy transactionProxy = new TransactionProxy(mockActorContext, READ_WRITE); @@ -556,7 +548,7 @@ public class TransactionProxyTest extends AbstractActorTest { NormalizedNode nodeToWrite = ImmutableNodes.containerNode(TestModel.TEST_QNAME); doReturn(writeDataReply()).when(mockActorContext).executeRemoteOperationAsync( - eq(actorSelection(actorRef)), eqWriteData(nodeToWrite), anyDuration()); + eq(actorSelection(actorRef)), eqWriteData(nodeToWrite)); TransactionProxy transactionProxy = new TransactionProxy(mockActorContext, WRITE_ONLY); @@ -564,7 +556,7 @@ public class TransactionProxyTest extends AbstractActorTest { transactionProxy.write(TestModel.TEST_PATH, nodeToWrite); verify(mockActorContext).executeRemoteOperationAsync( - eq(actorSelection(actorRef)), eqWriteData(nodeToWrite), anyDuration()); + eq(actorSelection(actorRef)), eqWriteData(nodeToWrite)); verifyRecordingOperationFutures(transactionProxy.getRecordedOperationFutures(), WriteDataReply.SERIALIZABLE_CLASS); @@ -599,7 +591,7 @@ public class TransactionProxyTest extends AbstractActorTest { NormalizedNode nodeToWrite = ImmutableNodes.containerNode(TestModel.TEST_QNAME); doReturn(mergeDataReply()).when(mockActorContext).executeRemoteOperationAsync( - eq(actorSelection(actorRef)), eqMergeData(nodeToWrite), anyDuration()); + eq(actorSelection(actorRef)), eqMergeData(nodeToWrite)); TransactionProxy transactionProxy = new TransactionProxy(mockActorContext, WRITE_ONLY); @@ -607,7 +599,7 @@ public class TransactionProxyTest extends AbstractActorTest { transactionProxy.merge(TestModel.TEST_PATH, nodeToWrite); verify(mockActorContext).executeRemoteOperationAsync( - eq(actorSelection(actorRef)), eqMergeData(nodeToWrite), anyDuration()); + eq(actorSelection(actorRef)), eqMergeData(nodeToWrite)); verifyRecordingOperationFutures(transactionProxy.getRecordedOperationFutures(), MergeDataReply.SERIALIZABLE_CLASS); @@ -618,7 +610,7 @@ public class TransactionProxyTest extends AbstractActorTest { ActorRef actorRef = setupActorContextWithInitialCreateTransaction(WRITE_ONLY); doReturn(deleteDataReply()).when(mockActorContext).executeRemoteOperationAsync( - eq(actorSelection(actorRef)), eqDeleteData(), anyDuration()); + eq(actorSelection(actorRef)), eqDeleteData()); TransactionProxy transactionProxy = new TransactionProxy(mockActorContext, WRITE_ONLY); @@ -626,7 +618,7 @@ public class TransactionProxyTest extends AbstractActorTest { transactionProxy.delete(TestModel.TEST_PATH); verify(mockActorContext).executeRemoteOperationAsync( - eq(actorSelection(actorRef)), eqDeleteData(), anyDuration()); + eq(actorSelection(actorRef)), eqDeleteData()); verifyRecordingOperationFutures(transactionProxy.getRecordedOperationFutures(), DeleteDataReply.SERIALIZABLE_CLASS); @@ -665,13 +657,13 @@ public class TransactionProxyTest extends AbstractActorTest { NormalizedNode nodeToWrite = ImmutableNodes.containerNode(TestModel.TEST_QNAME); doReturn(readDataReply(null)).when(mockActorContext).executeRemoteOperationAsync( - eq(actorSelection(actorRef)), eqReadData(), anyDuration()); + eq(actorSelection(actorRef)), eqReadData()); doReturn(writeDataReply()).when(mockActorContext).executeRemoteOperationAsync( - eq(actorSelection(actorRef)), eqWriteData(nodeToWrite), anyDuration()); + eq(actorSelection(actorRef)), eqWriteData(nodeToWrite)); doReturn(readyTxReply(actorRef.path())).when(mockActorContext).executeRemoteOperationAsync( - eq(actorSelection(actorRef)), isA(ReadyTransaction.SERIALIZABLE_CLASS), anyDuration()); + eq(actorSelection(actorRef)), isA(ReadyTransaction.SERIALIZABLE_CLASS)); TransactionProxy transactionProxy = new TransactionProxy(mockActorContext, READ_WRITE); @@ -700,14 +692,13 @@ public class TransactionProxyTest extends AbstractActorTest { NormalizedNode nodeToWrite = ImmutableNodes.containerNode(TestModel.TEST_QNAME); doReturn(mergeDataReply()).when(mockActorContext).executeRemoteOperationAsync( - eq(actorSelection(actorRef)), eqMergeData(nodeToWrite), anyDuration()); + eq(actorSelection(actorRef)), eqMergeData(nodeToWrite)); doReturn(Futures.failed(new TestException())).when(mockActorContext). - executeRemoteOperationAsync(eq(actorSelection(actorRef)), eqWriteData(nodeToWrite), - anyDuration()); + executeRemoteOperationAsync(eq(actorSelection(actorRef)), eqWriteData(nodeToWrite)); doReturn(readyTxReply(actorRef.path())).when(mockActorContext).executeRemoteOperationAsync( - eq(actorSelection(actorRef)), isA(ReadyTransaction.SERIALIZABLE_CLASS), anyDuration()); + eq(actorSelection(actorRef)), isA(ReadyTransaction.SERIALIZABLE_CLASS)); TransactionProxy transactionProxy = new TransactionProxy(mockActorContext, WRITE_ONLY); @@ -736,11 +727,11 @@ public class TransactionProxyTest extends AbstractActorTest { NormalizedNode nodeToWrite = ImmutableNodes.containerNode(TestModel.TEST_QNAME); doReturn(mergeDataReply()).when(mockActorContext).executeRemoteOperationAsync( - eq(actorSelection(actorRef)), eqMergeData(nodeToWrite), anyDuration()); + eq(actorSelection(actorRef)), eqMergeData(nodeToWrite)); doReturn(Futures.failed(new TestException())).when(mockActorContext). executeRemoteOperationAsync(eq(actorSelection(actorRef)), - isA(ReadyTransaction.SERIALIZABLE_CLASS), anyDuration()); + isA(ReadyTransaction.SERIALIZABLE_CLASS)); TransactionProxy transactionProxy = new TransactionProxy(mockActorContext, WRITE_ONLY); @@ -763,7 +754,7 @@ public class TransactionProxyTest extends AbstractActorTest { public void testReadyWithInitialCreateTransactionFailure() throws Exception { doThrow(new PrimaryNotFoundException("mock")).when(mockActorContext).executeShardOperation( - anyString(), any(), anyDuration()); + anyString(), any()); TransactionProxy transactionProxy = new TransactionProxy(mockActorContext, WRITE_ONLY); @@ -793,11 +784,11 @@ public class TransactionProxyTest extends AbstractActorTest { NormalizedNode nodeToWrite = ImmutableNodes.containerNode(TestModel.TEST_QNAME); doReturn(writeDataReply()).when(mockActorContext).executeRemoteOperationAsync( - eq(actorSelection(actorRef)), eqWriteData(nodeToWrite), anyDuration()); + eq(actorSelection(actorRef)), eqWriteData(nodeToWrite)); doReturn(Futures.successful(new Object())).when(mockActorContext). executeRemoteOperationAsync(eq(actorSelection(actorRef)), - isA(ReadyTransaction.SERIALIZABLE_CLASS), anyDuration()); + isA(ReadyTransaction.SERIALIZABLE_CLASS)); TransactionProxy transactionProxy = new TransactionProxy(mockActorContext, WRITE_ONLY); @@ -830,7 +821,7 @@ public class TransactionProxyTest extends AbstractActorTest { ActorRef actorRef = setupActorContextWithInitialCreateTransaction(READ_WRITE); doReturn(readDataReply(null)).when(mockActorContext).executeRemoteOperationAsync( - eq(actorSelection(actorRef)), eqReadData(), anyDuration()); + eq(actorSelection(actorRef)), eqReadData()); TransactionProxy transactionProxy = new TransactionProxy(mockActorContext, READ_WRITE); diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/jmx/mbeans/shard/ShardStatsTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/jmx/mbeans/shard/ShardStatsTest.java index 41adcc55b1..c4d0b85fb5 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/jmx/mbeans/shard/ShardStatsTest.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/jmx/mbeans/shard/ShardStatsTest.java @@ -1,3 +1,10 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ package org.opendaylight.controller.cluster.datastore.jmx.mbeans.shard; import org.junit.After; @@ -86,7 +93,69 @@ public class ShardStatsTest { Object attribute = mbeanServer.getAttribute(testMBeanName, "FailedTransactionsCount"); Assert.assertEquals((Long) attribute, (Long) 2L); + } + + @Test + public void testGetAbortTransactionsCount() throws Exception { + //let us increment AbortTransactions count and then check + shardStats.incrementAbortTransactionsCount(); + shardStats.incrementAbortTransactionsCount(); + + + //now let us get from MBeanServer what is the transaction count. + Object attribute = + mbeanServer.getAttribute(testMBeanName, "AbortTransactionsCount"); + Assert.assertEquals((Long) attribute, (Long) 2L); + } + + @Test + public void testGetFailedReadTransactionsCount() throws Exception { + //let us increment FailedReadTransactions count and then check + shardStats.incrementFailedReadTransactionsCount(); + shardStats.incrementFailedReadTransactionsCount(); + + + //now let us get from MBeanServer what is the transaction count. + Object attribute = + mbeanServer.getAttribute(testMBeanName, "FailedReadTransactionsCount"); + Assert.assertEquals((Long) attribute, (Long) 2L); + } + + @Test + public void testResetTransactionCounters() throws Exception { + + //let us increment committed transactions count and then check + shardStats.incrementCommittedTransactionCount(); + shardStats.incrementCommittedTransactionCount(); + shardStats.incrementCommittedTransactionCount(); + + //now let us get from MBeanServer what is the transaction count. + Object attribute = mbeanServer.getAttribute(testMBeanName, + "CommittedTransactionsCount"); + Assert.assertEquals((Long) attribute, (Long) 3L); + + //let us increment FailedReadTransactions count and then check + shardStats.incrementFailedReadTransactionsCount(); + shardStats.incrementFailedReadTransactionsCount(); + + + //now let us get from MBeanServer what is the transaction count. + attribute = + mbeanServer.getAttribute(testMBeanName, "FailedReadTransactionsCount"); + Assert.assertEquals((Long) attribute, (Long) 2L); + + + //here we will reset the counters and check the above ones are 0 after reset + mbeanServer.invoke(testMBeanName, "resetTransactionCounters", null, null); + + //now let us get from MBeanServer what is the transaction count. + attribute = mbeanServer.getAttribute(testMBeanName, + "CommittedTransactionsCount"); + Assert.assertEquals((Long) attribute, (Long) 0L); + attribute = + mbeanServer.getAttribute(testMBeanName, "FailedReadTransactionsCount"); + Assert.assertEquals((Long) attribute, (Long) 0L); } 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 fda9ccdfdb..5d8fb8393d 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 @@ -117,7 +117,7 @@ public class ActorContextTest extends AbstractActorTest{ new ActorContext(getSystem(), shardManagerActorRef , mock(ClusterWrapper.class), mock(Configuration.class)); - Object out = actorContext.executeLocalShardOperation("default", "hello", duration("1 seconds")); + Object out = actorContext.executeLocalShardOperation("default", "hello"); assertEquals("hello", out); @@ -144,7 +144,7 @@ public class ActorContextTest extends AbstractActorTest{ new ActorContext(getSystem(), shardManagerActorRef , mock(ClusterWrapper.class), mock(Configuration.class)); - Object out = actorContext.executeLocalShardOperation("default", "hello", duration("1 seconds")); + Object out = actorContext.executeLocalShardOperation("default", "hello"); assertNull(out); @@ -232,7 +232,7 @@ public class ActorContextTest extends AbstractActorTest{ ActorSelection actor = actorContext.actorSelection(shardActorRef.path()); - Object out = actorContext.executeRemoteOperation(actor, "hello", duration("3 seconds")); + Object out = actorContext.executeRemoteOperation(actor, "hello"); assertEquals("hello", out); @@ -261,8 +261,7 @@ public class ActorContextTest extends AbstractActorTest{ ActorSelection actor = actorContext.actorSelection(shardActorRef.path()); - Future future = actorContext.executeRemoteOperationAsync(actor, "hello", - Duration.create(3, TimeUnit.SECONDS)); + Future future = actorContext.executeRemoteOperationAsync(actor, "hello"); try { Object result = Await.result(future, Duration.create(3, TimeUnit.SECONDS)); diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/utils/MockActorContext.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/utils/MockActorContext.java index b19fd3a529..8fa3a17f90 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/utils/MockActorContext.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/utils/MockActorContext.java @@ -12,7 +12,6 @@ import static org.junit.Assert.assertNotNull; import akka.actor.ActorRef; import akka.actor.ActorSelection; import akka.actor.ActorSystem; -import scala.concurrent.duration.FiniteDuration; public class MockActorContext extends ActorContext { @@ -33,12 +32,12 @@ public class MockActorContext extends ActorContext { @Override public Object executeShardOperation(String shardName, - Object message, FiniteDuration duration) { + Object message) { return executeShardOperationResponse; } @Override public Object executeRemoteOperation(ActorSelection actor, - Object message, FiniteDuration duration) { + Object message) { return executeRemoteOperationResponse; } @@ -76,13 +75,13 @@ public class MockActorContext extends ActorContext { @Override public Object executeLocalOperation(ActorRef actor, - Object message, FiniteDuration duration) { + Object message) { return this.executeLocalOperationResponse; } @Override public Object executeLocalShardOperation(String shardName, - Object message, FiniteDuration duration) { + Object message) { return this.executeLocalShardOperationResponse; } } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/utils/TestUtils.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/utils/TestUtils.java index 939096e7f3..4ddba2f1b9 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/utils/TestUtils.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/utils/TestUtils.java @@ -21,8 +21,7 @@ public class TestUtils { ActorContext testContext = new ActorContext(actorSystem, actorSystem.actorOf( Props.create(DoNothingActor.class)), new MockClusterWrapper(), new MockConfiguration()); Object messages = testContext - .executeLocalOperation(actorRef, "messages", - ActorContext.ASK_DURATION); + .executeLocalOperation(actorRef, "messages"); Assert.assertNotNull(messages); diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/resources/application.conf b/opendaylight/md-sal/sal-distributed-datastore/src/test/resources/application.conf index 27b0374bac..6851b1b72c 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/test/resources/application.conf +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/resources/application.conf @@ -14,3 +14,8 @@ akka { } } } +bounded-mailbox { + mailbox-type = "org.opendaylight.controller.common.actor.MeteredBoundedMailbox" + mailbox-capacity = 1000 + mailbox-push-timeout-time = 100ms +} diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/dom/impl/DomInmemoryDataBrokerModule.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/dom/impl/DomInmemoryDataBrokerModule.java index 8664e8910b..b423bbd0e5 100644 --- a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/dom/impl/DomInmemoryDataBrokerModule.java +++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/dom/impl/DomInmemoryDataBrokerModule.java @@ -7,11 +7,13 @@ */ package org.opendaylight.controller.config.yang.md.sal.dom.impl; -import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; + import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitDeadlockException; +import org.opendaylight.controller.md.sal.common.util.jmx.ThreadExecutorStatsMXBeanImpl; import org.opendaylight.controller.md.sal.dom.broker.impl.DOMDataBrokerImpl; +import org.opendaylight.controller.md.sal.dom.broker.impl.jmx.CommitStatsMXBeanImpl; import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStoreFactory; import org.opendaylight.controller.sal.core.spi.data.DOMStore; import org.opendaylight.yangtools.util.concurrent.DeadlockDetectingListeningExecutorService; @@ -24,6 +26,8 @@ import com.google.common.collect.ImmutableMap; public final class DomInmemoryDataBrokerModule extends org.opendaylight.controller.config.yang.md.sal.dom.impl.AbstractDomInmemoryDataBrokerModule { + private static final String JMX_BEAN_TYPE = "DOMDataBroker"; + public DomInmemoryDataBrokerModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier, final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) { super(identifier, dependencyResolver); @@ -78,7 +82,7 @@ public final class DomInmemoryDataBrokerModule extends * nothing on success. The executor queue capacity is bounded and, if the capacity is * reached, subsequent submitted tasks will block the caller. */ - Executor listenableFutureExecutor = SpecialExecutors.newBlockingBoundedCachedThreadPool( + ExecutorService listenableFutureExecutor = SpecialExecutors.newBlockingBoundedCachedThreadPool( getMaxDataBrokerFutureCallbackPoolSize(), getMaxDataBrokerFutureCallbackQueueSize(), "CommitFutures"); @@ -87,6 +91,29 @@ public final class DomInmemoryDataBrokerModule extends TransactionCommitDeadlockException.DEADLOCK_EXECUTOR_FUNCTION, listenableFutureExecutor)); + final CommitStatsMXBeanImpl commitStatsMXBean = new CommitStatsMXBeanImpl( + newDataBroker.getCommitStatsTracker(), JMX_BEAN_TYPE); + commitStatsMXBean.registerMBean(); + + final ThreadExecutorStatsMXBeanImpl commitExecutorStatsMXBean = + new ThreadExecutorStatsMXBeanImpl(commitExecutor, "CommitExecutorStats", + JMX_BEAN_TYPE, null); + commitExecutorStatsMXBean.registerMBean(); + + final ThreadExecutorStatsMXBeanImpl commitFutureStatsMXBean = + new ThreadExecutorStatsMXBeanImpl(listenableFutureExecutor, + "CommitFutureExecutorStats", JMX_BEAN_TYPE, null); + commitFutureStatsMXBean.registerMBean(); + + newDataBroker.setCloseable(new AutoCloseable() { + @Override + public void close() { + commitStatsMXBean.unregisterMBean(); + commitExecutorStatsMXBean.unregisterMBean(); + commitFutureStatsMXBean.unregisterMBean(); + } + }); + return newDataBroker; } } diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMDataBrokerImpl.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMDataBrokerImpl.java index d1b9a8f600..d63d6cbe36 100644 --- a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMDataBrokerImpl.java +++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMDataBrokerImpl.java @@ -23,6 +23,7 @@ import org.opendaylight.controller.sal.core.spi.data.DOMStore; import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort; import org.opendaylight.controller.sal.core.spi.data.DOMStoreTransactionChain; import org.opendaylight.yangtools.concepts.ListenerRegistration; +import org.opendaylight.yangtools.util.DurationStatsTracker; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -40,6 +41,7 @@ public class DOMDataBrokerImpl extends AbstractDOMForwardedTransactionFactory datastores, final ListeningExecutorService executor) { @@ -47,6 +49,27 @@ public class DOMDataBrokerImpl extends AbstractDOMForwardedTransactionFactory absent()); } - } diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMDataCommitCoordinatorImpl.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMDataCommitCoordinatorImpl.java index 521e2d0e73..3fde8d360f 100644 --- a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMDataCommitCoordinatorImpl.java +++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMDataCommitCoordinatorImpl.java @@ -16,6 +16,7 @@ import javax.annotation.concurrent.GuardedBy; import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException; import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction; import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort; +import org.opendaylight.yangtools.util.DurationStatsTracker; import org.opendaylight.yangtools.util.concurrent.MappingCheckedFuture; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -69,6 +70,8 @@ public class DOMDataCommitCoordinatorImpl implements DOMDataCommitExecutor { private final ListeningExecutorService executor; + private final DurationStatsTracker commitStatsTracker = new DurationStatsTracker(); + /** * * Construct DOMDataCommitCoordinator which uses supplied executor to @@ -80,6 +83,10 @@ public class DOMDataCommitCoordinatorImpl implements DOMDataCommitExecutor { this.executor = Preconditions.checkNotNull(executor, "executor must not be null."); } + public DurationStatsTracker getCommitStatsTracker() { + return commitStatsTracker; + } + @Override public CheckedFuture submit(final DOMDataWriteTransaction transaction, final Iterable cohorts, final Optional listener) { @@ -90,7 +97,8 @@ public class DOMDataCommitCoordinatorImpl implements DOMDataCommitExecutor { ListenableFuture commitFuture = null; try { - commitFuture = executor.submit(new CommitCoordinationTask(transaction, cohorts, listener)); + commitFuture = executor.submit(new CommitCoordinationTask(transaction, cohorts, + listener, commitStatsTracker)); } catch(RejectedExecutionException e) { LOG.error("The commit executor's queue is full - submit task was rejected. \n" + executor, e); @@ -154,21 +162,25 @@ public class DOMDataCommitCoordinatorImpl implements DOMDataCommitExecutor { private final DOMDataWriteTransaction tx; private final Iterable cohorts; + private final DurationStatsTracker commitStatTracker; @GuardedBy("this") private CommitPhase currentPhase; public CommitCoordinationTask(final DOMDataWriteTransaction transaction, final Iterable cohorts, - final Optional listener) { + final Optional listener, + final DurationStatsTracker commitStatTracker) { this.tx = Preconditions.checkNotNull(transaction, "transaction must not be null"); this.cohorts = Preconditions.checkNotNull(cohorts, "cohorts must not be null"); this.currentPhase = CommitPhase.SUBMITTED; + this.commitStatTracker = commitStatTracker; } @Override public Void call() throws TransactionCommitFailedException { + long startTime = System.nanoTime(); try { canCommitBlocking(); preCommitBlocking(); @@ -178,6 +190,10 @@ public class DOMDataCommitCoordinatorImpl implements DOMDataCommitExecutor { LOG.warn("Tx: {} Error during phase {}, starting Abort", tx.getIdentifier(), currentPhase, e); abortBlocking(e); throw e; + } finally { + if(commitStatTracker != null) { + commitStatTracker.addDuration(System.nanoTime() - startTime); + } } } diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/compat/TranslatingListenerInvoker.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/compat/TranslatingListenerInvoker.java index b9504e7268..1c12d20504 100644 --- a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/compat/TranslatingListenerInvoker.java +++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/compat/TranslatingListenerInvoker.java @@ -62,12 +62,18 @@ abstract class TranslatingListenerInvoker implements AutoCloseable, DOMDataChang } } + @Override + public String toString() { + return getDelegate().getClass().getName(); + } + static final class TranslatingConfigListenerInvoker extends TranslatingListenerInvoker { public TranslatingConfigListenerInvoker(final DataChangeListener listener, final DataNormalizer normalizer) { super(listener, normalizer); } + @Override DataChangeEvent getLegacyEvent(final DataNormalizer normalizer, final AsyncDataChangeEvent> normalizedChange) { return TranslatingDataChangeEvent.createConfiguration(normalizedChange, normalizer); } @@ -85,6 +91,7 @@ abstract class TranslatingListenerInvoker implements AutoCloseable, DOMDataChang super(listener, normalizer); } + @Override DataChangeEvent getLegacyEvent(final DataNormalizer normalizer, final AsyncDataChangeEvent> normalizedChange) { return TranslatingDataChangeEvent.createOperational(normalizedChange, normalizer); } diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/jmx/CommitStatsMXBean.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/jmx/CommitStatsMXBean.java new file mode 100644 index 0000000000..40dc30eb0e --- /dev/null +++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/jmx/CommitStatsMXBean.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2014 Brocade Communications 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.jmx; + +/** + * MXBean interface for retrieving write Tx commit statistics. + * + * @author Thomas Pantelis + */ +public interface CommitStatsMXBean { + + /** + * Returns the total number of commits that have occurred. + */ + long getTotalCommits(); + + /** + * Returns a string representing the time duration of the longest commit, in the appropriate + * scaled units, along with the date/time that it occurred. + */ + String getLongestCommitTime(); + + /** + * Returns a string representing the time duration of the shortest commit, in the appropriate + * scaled units, along with the date/time that it occurred. + */ + String getShortestCommitTime(); + + /** + * Returns a string representing average commit time duration, in the appropriate + * scaled units. + */ + String getAverageCommitTime(); + + /** + * Clears the current stats to their defaults. + */ + void clearStats(); +} diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/jmx/CommitStatsMXBeanImpl.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/jmx/CommitStatsMXBeanImpl.java new file mode 100644 index 0000000000..f67f6b0148 --- /dev/null +++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/jmx/CommitStatsMXBeanImpl.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2014 Brocade Communications 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.jmx; + +import javax.annotation.Nonnull; + +import org.opendaylight.controller.md.sal.common.util.jmx.AbstractMXBean; +import org.opendaylight.yangtools.util.DurationStatsTracker; + +/** + * Implementation of the CommitStatsMXBean interface. + * + * @author Thomas Pantelis + */ +public class CommitStatsMXBeanImpl extends AbstractMXBean implements CommitStatsMXBean { + + private final DurationStatsTracker commitStatsTracker; + + /** + * Constructor. + * + * @param commitStatsTracker the DurationStatsTracker used to obtain the stats. + * @param mBeanType mBeanType Used as the type property in the bean's ObjectName. + */ + public CommitStatsMXBeanImpl(@Nonnull DurationStatsTracker commitStatsTracker, + @Nonnull String mBeanType) { + super("CommitStats", mBeanType, null); + this.commitStatsTracker = commitStatsTracker; + } + + @Override + public long getTotalCommits() { + return commitStatsTracker.getTotalDurations(); + } + + @Override + public String getLongestCommitTime() { + return commitStatsTracker.getDisplayableLongestDuration(); + } + + @Override + public String getShortestCommitTime() { + return commitStatsTracker.getDisplayableShortestDuration(); + } + + @Override + public String getAverageCommitTime() { + return commitStatsTracker.getDisplayableAverageDuration(); + } + + @Override + public void clearStats() { + commitStatsTracker.reset(); + } +} diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/GlobalBundleScanningSchemaServiceImpl.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/GlobalBundleScanningSchemaServiceImpl.java index 82637327f6..c4418c7911 100644 --- a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/GlobalBundleScanningSchemaServiceImpl.java +++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/GlobalBundleScanningSchemaServiceImpl.java @@ -10,6 +10,10 @@ package org.opendaylight.controller.sal.dom.broker; import static com.google.common.base.Preconditions.checkState; import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Optional; +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; + import java.net.URL; import java.util.ArrayList; import java.util.Collections; @@ -20,7 +24,7 @@ import org.opendaylight.controller.sal.core.api.model.SchemaService; import org.opendaylight.controller.sal.dom.broker.impl.SchemaContextProvider; import org.opendaylight.yangtools.concepts.ListenerRegistration; import org.opendaylight.yangtools.concepts.Registration; -import org.opendaylight.yangtools.concepts.util.ListenerRegistry; +import org.opendaylight.yangtools.util.ListenerRegistry; import org.opendaylight.yangtools.yang.model.api.Module; import org.opendaylight.yangtools.yang.model.api.SchemaContext; import org.opendaylight.yangtools.yang.model.api.SchemaContextListener; @@ -36,10 +40,6 @@ import org.osgi.util.tracker.ServiceTrackerCustomizer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.common.base.Optional; -import com.google.common.base.Preconditions; -import com.google.common.collect.ImmutableList; - public class GlobalBundleScanningSchemaServiceImpl implements SchemaContextProvider, SchemaService, ServiceTrackerCustomizer, AutoCloseable { private static final Logger LOG = LoggerFactory.getLogger(GlobalBundleScanningSchemaServiceImpl.class); @@ -71,7 +71,11 @@ public class GlobalBundleScanningSchemaServiceImpl implements SchemaContextProvi @VisibleForTesting public static synchronized void destroyInstance() { - instance = null; + try { + instance.close(); + } finally { + instance = null; + } } public BundleContext getContext() { @@ -124,16 +128,18 @@ public class GlobalBundleScanningSchemaServiceImpl implements SchemaContextProvi } @Override - public void close() throws Exception { + public void close() { if (bundleTracker != null) { bundleTracker.close(); } if (listenerTracker != null) { listenerTracker.close(); } - // FIXME: Add listeners.close(); - } + for (ListenerRegistration l : listeners.getListeners()) { + l.close(); + } + } private synchronized void updateContext(final SchemaContext snapshot) { Object[] services = listenerTracker.getServices(); diff --git a/opendaylight/md-sal/sal-dom-broker/src/test/java/org/opendaylight/controller/md/sal/dom/broker/impl/jmx/CommitStatsMXBeanImplTest.java b/opendaylight/md-sal/sal-dom-broker/src/test/java/org/opendaylight/controller/md/sal/dom/broker/impl/jmx/CommitStatsMXBeanImplTest.java new file mode 100644 index 0000000000..d796930302 --- /dev/null +++ b/opendaylight/md-sal/sal-dom-broker/src/test/java/org/opendaylight/controller/md/sal/dom/broker/impl/jmx/CommitStatsMXBeanImplTest.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2014 Brocade Communications 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.jmx; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; +import org.opendaylight.yangtools.util.DurationStatsTracker; + +/** + * Unit tests for CommitStatsMXBeanImpl. + * + * @author Thomas Pantelis + */ +public class CommitStatsMXBeanImplTest { + + @Test + public void test() { + + DurationStatsTracker commitStatsTracker = new DurationStatsTracker(); + CommitStatsMXBeanImpl bean = + new CommitStatsMXBeanImpl(commitStatsTracker, "Test"); + + commitStatsTracker.addDuration(100); + + String prefix = "100.0 ns"; + assertEquals("getTotalCommits", 1L, bean.getTotalCommits()); + assertEquals("getLongestCommitTime starts with \"" + prefix + "\"", true, + bean.getLongestCommitTime().startsWith("100.0 ns")); + assertEquals("getShortestCommitTime starts with \"" + prefix + "\"", true, + bean.getShortestCommitTime().startsWith(prefix)); + assertEquals("getAverageCommitTime starts with \"" + prefix + "\"", true, + bean.getAverageCommitTime().startsWith(prefix)); + } +} diff --git a/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/statistics/DOMStoreStatsTracker.java b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/statistics/DOMStoreStatsTracker.java new file mode 100644 index 0000000000..12713b2f45 --- /dev/null +++ b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/statistics/DOMStoreStatsTracker.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2014 Brocade Communications Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.sal.core.spi.data.statistics; + +import java.util.concurrent.ExecutorService; + +import javax.annotation.Nonnull; + +import org.opendaylight.yangtools.util.concurrent.QueuedNotificationManager; + +/** + * Interface for a class that tracks statistics for a data store. + * + * @author Thomas Pantelis + */ +public interface DOMStoreStatsTracker { + + /** + * Sets the executor used for DataChangeListener notifications. + * + * @param dclExecutor the executor + */ + void setDataChangeListenerExecutor( @Nonnull ExecutorService dclExecutor ); + + /** + * Sets the executor used internally by the data store. + * + * @param dsExecutor the executor + */ + void setDataStoreExecutor( @Nonnull ExecutorService dsExecutor ); + + /** + * Sets the QueuedNotificationManager use for DataChangeListener notifications, + * + * @param manager the manager + */ + void setNotificationManager( @Nonnull QueuedNotificationManager manager ); +} diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/config/yang/inmemory_datastore_provider/InMemoryConfigDataStoreProviderModule.java b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/config/yang/inmemory_datastore_provider/InMemoryConfigDataStoreProviderModule.java index fd1627c6f9..b98844ba64 100644 --- a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/config/yang/inmemory_datastore_provider/InMemoryConfigDataStoreProviderModule.java +++ b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/config/yang/inmemory_datastore_provider/InMemoryConfigDataStoreProviderModule.java @@ -1,7 +1,9 @@ package org.opendaylight.controller.config.yang.inmemory_datastore_provider; +import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStore; import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStoreConfigProperties; import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStoreFactory; +import org.opendaylight.controller.md.sal.dom.store.impl.jmx.InMemoryDataStoreStats; public class InMemoryConfigDataStoreProviderModule extends org.opendaylight.controller.config.yang.inmemory_datastore_provider.AbstractInMemoryConfigDataStoreProviderModule { @@ -20,9 +22,19 @@ public class InMemoryConfigDataStoreProviderModule extends org.opendaylight.cont @Override public java.lang.AutoCloseable createInstance() { - return InMemoryDOMDataStoreFactory.create("DOM-CFG", getSchemaServiceDependency(), + + InMemoryDOMDataStore dataStore = InMemoryDOMDataStoreFactory.create( + "DOM-CFG", getSchemaServiceDependency(), InMemoryDOMDataStoreConfigProperties.create(getMaxDataChangeExecutorPoolSize(), - getMaxDataChangeExecutorQueueSize(), getMaxDataChangeListenerQueueSize())); + getMaxDataChangeExecutorQueueSize(), getMaxDataChangeListenerQueueSize(), + getMaxDataStoreExecutorQueueSize())); + + InMemoryDataStoreStats statsBean = new InMemoryDataStoreStats("InMemoryConfigDataStore", + dataStore.getDataChangeListenerNotificationManager(), dataStore.getDomStoreExecutor()); + + dataStore.setCloseable(statsBean); + + return dataStore; } } diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/config/yang/inmemory_datastore_provider/InMemoryOperationalDataStoreProviderModule.java b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/config/yang/inmemory_datastore_provider/InMemoryOperationalDataStoreProviderModule.java index 6596acb4e1..4532452c65 100644 --- a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/config/yang/inmemory_datastore_provider/InMemoryOperationalDataStoreProviderModule.java +++ b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/config/yang/inmemory_datastore_provider/InMemoryOperationalDataStoreProviderModule.java @@ -1,7 +1,9 @@ package org.opendaylight.controller.config.yang.inmemory_datastore_provider; +import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStore; import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStoreConfigProperties; import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStoreFactory; +import org.opendaylight.controller.md.sal.dom.store.impl.jmx.InMemoryDataStoreStats; public class InMemoryOperationalDataStoreProviderModule extends org.opendaylight.controller.config.yang.inmemory_datastore_provider.AbstractInMemoryOperationalDataStoreProviderModule { @@ -20,9 +22,17 @@ public class InMemoryOperationalDataStoreProviderModule extends org.opendaylight @Override public java.lang.AutoCloseable createInstance() { - return InMemoryDOMDataStoreFactory.create("DOM-OPER", getSchemaServiceDependency(), + InMemoryDOMDataStore dataStore = InMemoryDOMDataStoreFactory.create("DOM-OPER", getSchemaServiceDependency(), InMemoryDOMDataStoreConfigProperties.create(getMaxDataChangeExecutorPoolSize(), - getMaxDataChangeExecutorQueueSize(), getMaxDataChangeListenerQueueSize())); - } + getMaxDataChangeExecutorQueueSize(), getMaxDataChangeListenerQueueSize(), + getMaxDataStoreExecutorQueueSize())); + + + InMemoryDataStoreStats statsBean = new InMemoryDataStoreStats("InMemoryOperationalDataStore", + dataStore.getDataChangeListenerNotificationManager(), dataStore.getDomStoreExecutor()); + dataStore.setCloseable(statsBean); + + return dataStore; + } } diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/ChangeListenerNotifyTask.java b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/ChangeListenerNotifyTask.java deleted file mode 100644 index 536cfa0081..0000000000 --- a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/ChangeListenerNotifyTask.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v1.0 which accompanies this distribution, - * and is available at http://www.eclipse.org/legal/epl-v10.html - */ -package org.opendaylight.controller.md.sal.dom.store.impl; - -import com.google.common.base.Preconditions; - -import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent; -import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener; -import org.opendaylight.yangtools.util.concurrent.NotificationManager; -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; - -class ChangeListenerNotifyTask implements Runnable { - private static final Logger LOG = LoggerFactory.getLogger(ChangeListenerNotifyTask.class); - - @SuppressWarnings("rawtypes") - private final NotificationManager notificationMgr; - private final AsyncDataChangeEvent> event; - private final DataChangeListenerRegistration listener; - - @SuppressWarnings("rawtypes") - public ChangeListenerNotifyTask(final DataChangeListenerRegistration listener, - final AsyncDataChangeEvent> event, - final NotificationManager notificationMgr) { - this.notificationMgr = Preconditions.checkNotNull(notificationMgr); - this.listener = Preconditions.checkNotNull(listener); - this.event = Preconditions.checkNotNull(event); - } - - @Override - public void run() { - final AsyncDataChangeListener> l = listener.getInstance(); - if (l == null) { - LOG.trace("Skipping event delivery to unregistered listener {}", l); - return; - } - LOG.trace("Listener {} event {}", l, event); - - // FIXME: Yo dawg I heard you like queues, so this was queued to be queued - notificationMgr.submitNotification(l, event); - } - - @Override - public String toString() { - return "ChangeListenerNotifyTask [listener=" + listener + ", event=" + event + "]"; - } -} diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDOMDataStore.java b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDOMDataStore.java index 7ad3b8201e..476356a19e 100644 --- a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDOMDataStore.java +++ b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDOMDataStore.java @@ -7,30 +7,29 @@ */ package org.opendaylight.controller.md.sal.dom.store.impl; +import static com.google.common.base.Preconditions.checkState; + import com.google.common.base.Optional; import com.google.common.base.Preconditions; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListeningExecutorService; +import com.google.common.util.concurrent.MoreExecutors; + +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; + +import javax.annotation.concurrent.GuardedBy; import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope; -import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent; import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener; import org.opendaylight.controller.md.sal.common.api.data.OptimisticLockFailedException; import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException; import org.opendaylight.controller.md.sal.dom.store.impl.SnapshotBackedWriteTransaction.TransactionReadyPrototype; -import org.opendaylight.yangtools.util.ExecutorServiceUtil; -import org.opendaylight.yangtools.util.concurrent.NotificationManager; -import org.opendaylight.yangtools.util.concurrent.QueuedNotificationManager; -import org.opendaylight.yangtools.yang.data.api.schema.tree.ConflictingModificationAppliedException; -import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree; -import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate; -import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification; -import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot; -import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException; import org.opendaylight.controller.md.sal.dom.store.impl.tree.ListenerTree; -import org.opendaylight.yangtools.yang.data.impl.schema.tree.InMemoryDataTreeFactory; import org.opendaylight.controller.sal.core.spi.data.DOMStore; import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadTransaction; import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadWriteTransaction; @@ -40,22 +39,23 @@ import org.opendaylight.controller.sal.core.spi.data.DOMStoreWriteTransaction; import org.opendaylight.yangtools.concepts.AbstractListenerRegistration; import org.opendaylight.yangtools.concepts.Identifiable; import org.opendaylight.yangtools.concepts.ListenerRegistration; +import org.opendaylight.yangtools.util.ExecutorServiceUtil; +import org.opendaylight.yangtools.util.concurrent.QueuedNotificationManager; +import org.opendaylight.yangtools.util.concurrent.QueuedNotificationManager.Invoker; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.api.schema.tree.ConflictingModificationAppliedException; +import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree; +import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate; +import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification; +import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot; +import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException; +import org.opendaylight.yangtools.yang.data.impl.schema.tree.InMemoryDataTreeFactory; import org.opendaylight.yangtools.yang.model.api.SchemaContext; import org.opendaylight.yangtools.yang.model.api.SchemaContextListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import javax.annotation.concurrent.GuardedBy; - -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicLong; - -import static com.google.common.base.Preconditions.checkState; - /** * In-memory DOM Data Store * @@ -68,18 +68,17 @@ import static com.google.common.base.Preconditions.checkState; public class InMemoryDOMDataStore implements DOMStore, Identifiable, SchemaContextListener, TransactionReadyPrototype,AutoCloseable { private static final Logger LOG = LoggerFactory.getLogger(InMemoryDOMDataStore.class); + private static final ListenableFuture SUCCESSFUL_FUTURE = Futures.immediateFuture(null); - @SuppressWarnings("rawtypes") - private static final QueuedNotificationManager.Invoker DCL_NOTIFICATION_MGR_INVOKER = - new QueuedNotificationManager.Invoker() { - - @SuppressWarnings("unchecked") + private static final Invoker, DOMImmutableDataChangeEvent> DCL_NOTIFICATION_MGR_INVOKER = + new Invoker, DOMImmutableDataChangeEvent>() { @Override - public void invokeListener( AsyncDataChangeListener listener, - AsyncDataChangeEvent notification ) { - listener.onDataChanged(notification); + public void invokeListener(final DataChangeListenerRegistration listener, + final DOMImmutableDataChangeEvent notification ) { + final AsyncDataChangeListener> inst = listener.getInstance(); + if (inst != null) { + inst.onDataChanged(notification); + } } }; @@ -88,24 +87,26 @@ public class InMemoryDOMDataStore implements DOMStore, Identifiable, Sch private final AtomicLong txCounter = new AtomicLong(0); private final ListeningExecutorService listeningExecutor; - @SuppressWarnings("rawtypes") - private final NotificationManager - dataChangeListenerNotificationManager; + private final QueuedNotificationManager, DOMImmutableDataChangeEvent> dataChangeListenerNotificationManager; private final ExecutorService dataChangeListenerExecutor; + private final ExecutorService domStoreExecutor; + private final String name; - public InMemoryDOMDataStore(final String name, final ListeningExecutorService listeningExecutor, + private volatile AutoCloseable closeable; + + public InMemoryDOMDataStore(final String name, final ExecutorService domStoreExecutor, final ExecutorService dataChangeListenerExecutor) { - this(name, listeningExecutor, dataChangeListenerExecutor, - InMemoryDOMDataStoreConfigProperties.DEFAULT_MAX_DATA_CHANGE_LISTENER_QUEUE_SIZE); + this(name, domStoreExecutor, dataChangeListenerExecutor, + InMemoryDOMDataStoreConfigProperties.DEFAULT_MAX_DATA_CHANGE_LISTENER_QUEUE_SIZE); } - public InMemoryDOMDataStore(final String name, final ListeningExecutorService listeningExecutor, - final ExecutorService dataChangeListenerExecutor, int maxDataChangeListenerQueueSize) { + public InMemoryDOMDataStore(final String name, final ExecutorService domStoreExecutor, + final ExecutorService dataChangeListenerExecutor, final int maxDataChangeListenerQueueSize) { this.name = Preconditions.checkNotNull(name); - this.listeningExecutor = Preconditions.checkNotNull(listeningExecutor); - + this.domStoreExecutor = Preconditions.checkNotNull(domStoreExecutor); + this.listeningExecutor = MoreExecutors.listeningDecorator(this.domStoreExecutor); this.dataChangeListenerExecutor = Preconditions.checkNotNull(dataChangeListenerExecutor); dataChangeListenerNotificationManager = @@ -114,6 +115,18 @@ public class InMemoryDOMDataStore implements DOMStore, Identifiable, Sch "DataChangeListenerQueueMgr"); } + public void setCloseable(AutoCloseable closeable) { + this.closeable = closeable; + } + + public QueuedNotificationManager getDataChangeListenerNotificationManager() { + return dataChangeListenerNotificationManager; + } + + public ExecutorService getDomStoreExecutor() { + return domStoreExecutor; + } + @Override public final String getIdentifier() { return name; @@ -148,7 +161,16 @@ public class InMemoryDOMDataStore implements DOMStore, Identifiable, Sch public void close() { ExecutorServiceUtil.tryGracefulShutdown(listeningExecutor, 30, TimeUnit.SECONDS); ExecutorServiceUtil.tryGracefulShutdown(dataChangeListenerExecutor, 30, TimeUnit.SECONDS); + + if(closeable != null) { + try { + closeable.close(); + } catch(Exception e) { + LOG.debug("Error closing instance", e); + } + } } + @Override public >> ListenerRegistration registerChangeListener( final YangInstanceIdentifier path, final L listener, final DataChangeScope scope) { @@ -175,8 +197,7 @@ public class InMemoryDOMDataStore implements DOMStore, Identifiable, Sch .addCreated(path, data) // .build(); - new ChangeListenerNotifyTask(reg, event, - dataChangeListenerNotificationManager).run(); + dataChangeListenerNotificationManager.submitNotification(reg, event); } } @@ -277,7 +298,7 @@ public class InMemoryDOMDataStore implements DOMStore, Identifiable, Sch } public synchronized void onTransactionCommited(final SnapshotBackedWriteTransaction transaction) { - // If commited transaction is latestOutstandingTx we clear + // If committed transaction is latestOutstandingTx we clear // latestOutstandingTx // field in order to base new transactions on Datastore Data Tree // directly. @@ -379,8 +400,7 @@ public class InMemoryDOMDataStore implements DOMStore, Identifiable, Sch @Override public Void call() { candidate = dataTree.prepare(modification); - listenerResolver = ResolveDataChangeEventsTask.create(candidate, listenerTree, - dataChangeListenerNotificationManager); + listenerResolver = ResolveDataChangeEventsTask.create(candidate, listenerTree); return null; } }); @@ -389,7 +409,7 @@ public class InMemoryDOMDataStore implements DOMStore, Identifiable, Sch @Override public ListenableFuture abort() { candidate = null; - return Futures.immediateFuture(null); + return SUCCESSFUL_FUTURE; } @Override @@ -402,14 +422,10 @@ public class InMemoryDOMDataStore implements DOMStore, Identifiable, Sch */ synchronized (this) { dataTree.commit(candidate); - - for (ChangeListenerNotifyTask task : listenerResolver.call()) { - LOG.trace("Scheduling invocation of listeners: {}", task); - task.run(); - } + listenerResolver.resolve(dataChangeListenerNotificationManager); } - return Futures.immediateFuture(null); + return SUCCESSFUL_FUTURE; } } } diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDOMDataStoreConfigProperties.java b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDOMDataStoreConfigProperties.java index 6e451ba12b..be89d53a69 100644 --- a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDOMDataStoreConfigProperties.java +++ b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDOMDataStoreConfigProperties.java @@ -20,15 +20,18 @@ public class InMemoryDOMDataStoreConfigProperties { public static final int DEFAULT_MAX_DATA_CHANGE_EXECUTOR_QUEUE_SIZE = 1000; public static final int DEFAULT_MAX_DATA_CHANGE_EXECUTOR_POOL_SIZE = 20; public static final int DEFAULT_MAX_DATA_CHANGE_LISTENER_QUEUE_SIZE = 1000; + public static final int DEFAULT_MAX_DATA_STORE_EXECUTOR_QUEUE_SIZE = 5000; private static final InMemoryDOMDataStoreConfigProperties DEFAULT = create(DEFAULT_MAX_DATA_CHANGE_EXECUTOR_POOL_SIZE, DEFAULT_MAX_DATA_CHANGE_EXECUTOR_QUEUE_SIZE, - DEFAULT_MAX_DATA_CHANGE_LISTENER_QUEUE_SIZE); + DEFAULT_MAX_DATA_CHANGE_LISTENER_QUEUE_SIZE, + DEFAULT_MAX_DATA_STORE_EXECUTOR_QUEUE_SIZE); private final int maxDataChangeExecutorQueueSize; private final int maxDataChangeExecutorPoolSize; private final int maxDataChangeListenerQueueSize; + private final int maxDataStoreExecutorQueueSize; /** * Constructs an instance with the given property values. @@ -39,11 +42,22 @@ public class InMemoryDOMDataStoreConfigProperties { * maximum queue size for the data change notification executor. * @param maxDataChangeListenerQueueSize * maximum queue size for the data change listeners. + * @param maxDataStoreExecutorQueueSize + * maximum queue size for the data store executor. */ + public static InMemoryDOMDataStoreConfigProperties create(int maxDataChangeExecutorPoolSize, + int maxDataChangeExecutorQueueSize, int maxDataChangeListenerQueueSize, + int maxDataStoreExecutorQueueSize) { + return new InMemoryDOMDataStoreConfigProperties(maxDataChangeExecutorPoolSize, + maxDataChangeExecutorQueueSize, maxDataChangeListenerQueueSize, + maxDataStoreExecutorQueueSize); + } + public static InMemoryDOMDataStoreConfigProperties create(int maxDataChangeExecutorPoolSize, int maxDataChangeExecutorQueueSize, int maxDataChangeListenerQueueSize) { return new InMemoryDOMDataStoreConfigProperties(maxDataChangeExecutorPoolSize, - maxDataChangeExecutorQueueSize, maxDataChangeListenerQueueSize); + maxDataChangeExecutorQueueSize, maxDataChangeListenerQueueSize, + DEFAULT_MAX_DATA_STORE_EXECUTOR_QUEUE_SIZE); } /** @@ -54,10 +68,12 @@ public class InMemoryDOMDataStoreConfigProperties { } private InMemoryDOMDataStoreConfigProperties(int maxDataChangeExecutorPoolSize, - int maxDataChangeExecutorQueueSize, int maxDataChangeListenerQueueSize) { + int maxDataChangeExecutorQueueSize, int maxDataChangeListenerQueueSize, + int maxDataStoreExecutorQueueSize) { this.maxDataChangeExecutorQueueSize = maxDataChangeExecutorQueueSize; this.maxDataChangeExecutorPoolSize = maxDataChangeExecutorPoolSize; this.maxDataChangeListenerQueueSize = maxDataChangeListenerQueueSize; + this.maxDataStoreExecutorQueueSize = maxDataStoreExecutorQueueSize; } /** @@ -80,4 +96,11 @@ public class InMemoryDOMDataStoreConfigProperties { public int getMaxDataChangeListenerQueueSize() { return maxDataChangeListenerQueueSize; } + + /** + * Returns the maximum queue size for the data store executor. + */ + public int getMaxDataStoreExecutorQueueSize() { + return maxDataStoreExecutorQueueSize; + } } diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDOMDataStoreFactory.java b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDOMDataStoreFactory.java index a3512743ed..052fb2b89b 100644 --- a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDOMDataStoreFactory.java +++ b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDOMDataStoreFactory.java @@ -9,13 +9,11 @@ package org.opendaylight.controller.md.sal.dom.store.impl; import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; import javax.annotation.Nullable; import org.opendaylight.controller.sal.core.api.model.SchemaService; import org.opendaylight.yangtools.util.concurrent.SpecialExecutors; -import com.google.common.util.concurrent.MoreExecutors; /** * A factory for creating InMemoryDOMDataStore instances. @@ -61,9 +59,12 @@ public final class InMemoryDOMDataStoreFactory { ExecutorService dataChangeListenerExecutor = SpecialExecutors.newBlockingBoundedFastThreadPool( dclExecutorMaxPoolSize, dclExecutorMaxQueueSize, name + "-DCL" ); + ExecutorService domStoreExecutor = SpecialExecutors.newBoundedSingleThreadExecutor( + actualProperties.getMaxDataStoreExecutorQueueSize(), "DOMStore-" + name ); + InMemoryDOMDataStore dataStore = new InMemoryDOMDataStore(name, - MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor()), - dataChangeListenerExecutor, actualProperties.getMaxDataChangeListenerQueueSize()); + domStoreExecutor, dataChangeListenerExecutor, + actualProperties.getMaxDataChangeListenerQueueSize()); if(schemaService != null) { schemaService.registerSchemaContextListener(dataStore); diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/ResolveDataChangeEventsTask.java b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/ResolveDataChangeEventsTask.java index a4e8c86aa8..5fe9866b12 100644 --- a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/ResolveDataChangeEventsTask.java +++ b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/ResolveDataChangeEventsTask.java @@ -12,14 +12,10 @@ import com.google.common.base.Preconditions; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Multimap; -import java.util.ArrayList; import java.util.Collection; import java.util.Map.Entry; -import java.util.concurrent.Callable; import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope; -import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent; -import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener; import org.opendaylight.controller.md.sal.dom.store.impl.DOMImmutableDataChangeEvent.Builder; import org.opendaylight.controller.md.sal.dom.store.impl.DOMImmutableDataChangeEvent.SimpleEventFactory; import org.opendaylight.controller.md.sal.dom.store.impl.tree.ListenerTree; @@ -40,35 +36,23 @@ import org.slf4j.LoggerFactory; * Computes data change events for all affected registered listeners in data * tree. */ -final class ResolveDataChangeEventsTask implements Callable> { +final class ResolveDataChangeEventsTask { private static final Logger LOG = LoggerFactory.getLogger(ResolveDataChangeEventsTask.class); - @SuppressWarnings("rawtypes") - private final NotificationManager notificationMgr; private final DataTreeCandidate candidate; private final ListenerTree listenerRoot; private Multimap, DOMImmutableDataChangeEvent> collectedEvents; - @SuppressWarnings("rawtypes") - public ResolveDataChangeEventsTask(final DataTreeCandidate candidate, final ListenerTree listenerTree, - final NotificationManager notificationMgr) { + public ResolveDataChangeEventsTask(final DataTreeCandidate candidate, final ListenerTree listenerTree) { this.candidate = Preconditions.checkNotNull(candidate); this.listenerRoot = Preconditions.checkNotNull(listenerTree); - this.notificationMgr = Preconditions.checkNotNull(notificationMgr); } /** - * Resolves and creates Notification Tasks - * - * Implementation of done as Map-Reduce with two steps: 1. resolving events - * and their mapping to listeners 2. merging events affecting same listener - * - * @return An {@link Iterable} of Notification Tasks which needs to be executed in - * order to delivery data change events. + * Resolves and submits notification tasks to the specified manager. */ - @Override - public synchronized Iterable call() { + public synchronized void resolve(final NotificationManager, DOMImmutableDataChangeEvent> manager) { try (final Walker w = listenerRoot.getWalker()) { // Defensive: reset internal state collectedEvents = ArrayListMultimap.create(); @@ -81,7 +65,6 @@ final class ResolveDataChangeEventsTask implements Callable ret = new ArrayList<>(); for (Entry, Collection> e : collectedEvents.asMap().entrySet()) { final Collection col = e.getValue(); final DOMImmutableDataChangeEvent event; @@ -98,12 +81,8 @@ final class ResolveDataChangeEventsTask implements Callable notificationMgr) { - return new ResolveDataChangeEventsTask(candidate, listenerTree, notificationMgr); + public static ResolveDataChangeEventsTask create(final DataTreeCandidate candidate, final ListenerTree listenerTree) { + return new ResolveDataChangeEventsTask(candidate, listenerTree); } } diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/jmx/InMemoryDataStoreStats.java b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/jmx/InMemoryDataStoreStats.java new file mode 100644 index 0000000000..b3608eceef --- /dev/null +++ b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/jmx/InMemoryDataStoreStats.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2014 Brocade Communications 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.store.impl.jmx; + +import java.util.concurrent.ExecutorService; + +import org.opendaylight.controller.md.sal.common.util.jmx.QueuedNotificationManagerMXBeanImpl; +import org.opendaylight.controller.md.sal.common.util.jmx.ThreadExecutorStatsMXBeanImpl; +import org.opendaylight.yangtools.util.concurrent.QueuedNotificationManager; + +/** + * Wrapper class for data store MXbeans. + * + * @author Thomas Pantelis + */ +public class InMemoryDataStoreStats implements AutoCloseable { + + private final ThreadExecutorStatsMXBeanImpl notificationExecutorStatsBean; + private final ThreadExecutorStatsMXBeanImpl dataStoreExecutorStatsBean; + private final QueuedNotificationManagerMXBeanImpl notificationManagerStatsBean; + + public InMemoryDataStoreStats(String mBeanType, QueuedNotificationManager manager, + ExecutorService dataStoreExecutor) { + + this.notificationManagerStatsBean = new QueuedNotificationManagerMXBeanImpl(manager, + "notification-manager", mBeanType, null); + notificationManagerStatsBean.registerMBean(); + + this.notificationExecutorStatsBean = new ThreadExecutorStatsMXBeanImpl(manager.getExecutor(), + "notification-executor", mBeanType, null); + this.notificationExecutorStatsBean.registerMBean(); + + this.dataStoreExecutorStatsBean = new ThreadExecutorStatsMXBeanImpl(dataStoreExecutor, + "data-store-executor", mBeanType, null); + this.dataStoreExecutorStatsBean.registerMBean(); + } + + @Override + public void close() throws Exception { + if(notificationExecutorStatsBean != null) { + notificationExecutorStatsBean.unregisterMBean(); + } + + if(dataStoreExecutorStatsBean != null) { + dataStoreExecutorStatsBean.unregisterMBean(); + } + + if(notificationManagerStatsBean != null) { + notificationManagerStatsBean.unregisterMBean(); + } + } +} diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/main/yang/opendaylight-inmemory-datastore-provider.yang b/opendaylight/md-sal/sal-inmemory-datastore/src/main/yang/opendaylight-inmemory-datastore-provider.yang index 17c86716d8..7d19a64446 100644 --- a/opendaylight/md-sal/sal-inmemory-datastore/src/main/yang/opendaylight-inmemory-datastore-provider.yang +++ b/opendaylight/md-sal/sal-inmemory-datastore/src/main/yang/opendaylight-inmemory-datastore-provider.yang @@ -52,6 +52,12 @@ module opendaylight-inmemory-datastore-provider { type uint16; description "The maximum queue size for the data change listeners."; } + + leaf max-data-store-executor-queue-size { + default 5000; + type uint16; + description "The maximum queue size for the data store executor."; + } } // Augments the 'configuration' choice node under modules/module. diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfStateSchemas.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfStateSchemas.java index 23b039c254..e0b774f53c 100644 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfStateSchemas.java +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfStateSchemas.java @@ -156,7 +156,13 @@ public final class NetconfStateSchemas { QName childNode = NetconfMessageTransformUtil.IETF_NETCONF_MONITORING_SCHEMA_FORMAT.withoutRevision(); - final String formatAsString = getSingleChildNodeValue(schemaNode, childNode).get(); + String formatAsString = getSingleChildNodeValue(schemaNode, childNode).get(); + //This is HotFix for situations where format statement in netconf-monitoring might be passed with prefix. + if (formatAsString.contains(":")) { + String[] prefixedString = formatAsString.split(":"); + //FIXME: might be good idea to check prefix against model namespace + formatAsString = prefixedString[1]; + } if(formatAsString.equals(Yang.QNAME.getLocalName()) == false) { logger.debug("{}: Ignoring schema due to unsupported format: {}", id, formatAsString); return Optional.absent(); diff --git a/opendaylight/md-sal/sal-remoterpc-connector/pom.xml b/opendaylight/md-sal/sal-remoterpc-connector/pom.xml index 6e92a22037..8d454c4bd6 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/pom.xml +++ b/opendaylight/md-sal/sal-remoterpc-connector/pom.xml @@ -81,37 +81,27 @@ org.opendaylight.yangtools yang-data-api - org.opendaylight.yangtools yang-model-api - - org.opendaylight.yangtools yang-data-impl - - org.opendaylight.yangtools yang-common - - - org.osgi org.osgi.core - org.slf4j slf4j-api - org.scala-lang scala-library @@ -120,8 +110,12 @@ com.codahale.metrics metrics-core - 3.0.1 + + + com.codahale.metrics + metrics-graphite + junit @@ -194,7 +188,8 @@ ${project.groupId}.${project.artifactId} - !org.iq80.*;!*snappy;!org.jboss.*;!com.jcraft.*;!org.fusesource.*;* + !org.iq80.*;!*snappy;!org.jboss.*;!com.jcraft.*;!org.fusesource.*;!*jetty*;!sun.security.*;* + diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/ActorSystemFactory.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/ActorSystemFactory.java index f1ca3ccd50..6a442c57cc 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/ActorSystemFactory.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/ActorSystemFactory.java @@ -10,12 +10,16 @@ package org.opendaylight.controller.remote.rpc; import akka.actor.ActorSystem; import akka.osgi.BundleDelegatingClassLoader; -import com.typesafe.config.ConfigFactory; +import org.opendaylight.controller.remote.rpc.utils.AkkaConfigurationReader; import org.osgi.framework.BundleContext; public class ActorSystemFactory { - private static volatile ActorSystem actorSystem = null; + + public static final String ACTOR_SYSTEM_NAME = "opendaylight-cluster-rpc"; + public static final String CONFIGURATION_NAME = "odl-cluster-rpc"; + + private static volatile ActorSystem actorSystem = null; public static final ActorSystem getInstance(){ return actorSystem; @@ -26,7 +30,7 @@ public class ActorSystemFactory { * * @param bundleContext */ - public static final void createInstance(final BundleContext bundleContext) { + public static final void createInstance(final BundleContext bundleContext, AkkaConfigurationReader akkaConfigurationReader) { if(actorSystem == null) { // Create an OSGi bundle classloader for actor system BundleDelegatingClassLoader classLoader = new BundleDelegatingClassLoader(bundleContext.getBundle(), @@ -34,8 +38,8 @@ public class ActorSystemFactory { synchronized (ActorSystemFactory.class) { // Double check if (actorSystem == null) { - ActorSystem system = ActorSystem.create("opendaylight-cluster-rpc", - ConfigFactory.load().getConfig("odl-cluster-rpc"), classLoader); + ActorSystem system = ActorSystem.create(ACTOR_SYSTEM_NAME, + akkaConfigurationReader.read().getConfig(CONFIGURATION_NAME), classLoader); actorSystem = system; } } @@ -43,4 +47,5 @@ public class ActorSystemFactory { throw new IllegalStateException("Actor system should be created only once. Use getInstance method to access existing actor system"); } } + } diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RemoteRpcImplementation.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RemoteRpcImplementation.java index 4496bd3263..7d7dbf0f3a 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RemoteRpcImplementation.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RemoteRpcImplementation.java @@ -1,9 +1,13 @@ package org.opendaylight.controller.remote.rpc; +import static akka.pattern.Patterns.ask; import akka.actor.ActorRef; -import com.google.common.util.concurrent.Futures; +import akka.dispatch.OnComplete; +import akka.util.Timeout; + import com.google.common.util.concurrent.ListenableFuture; -import org.opendaylight.controller.remote.rpc.messages.ErrorResponse; +import com.google.common.util.concurrent.SettableFuture; + import org.opendaylight.controller.remote.rpc.messages.InvokeRpc; import org.opendaylight.controller.remote.rpc.messages.RpcResponse; import org.opendaylight.controller.remote.rpc.utils.ActorUtil; @@ -13,73 +17,82 @@ import org.opendaylight.controller.sal.core.api.RpcImplementation; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.common.RpcResult; import org.opendaylight.yangtools.yang.common.RpcResultBuilder; +import org.opendaylight.yangtools.yang.common.RpcError.ErrorType; import org.opendaylight.yangtools.yang.data.api.CompositeNode; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.model.api.SchemaContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import scala.concurrent.ExecutionContext; + import java.util.Collections; import java.util.Set; -public class RemoteRpcImplementation implements RpcImplementation, - RoutedRpcDefaultImplementation { - private static final Logger LOG = LoggerFactory.getLogger(RemoteRpcImplementation.class); - private ActorRef rpcBroker; - private SchemaContext schemaContext; - - public RemoteRpcImplementation(ActorRef rpcBroker, SchemaContext schemaContext) { - this.rpcBroker = rpcBroker; - this.schemaContext = schemaContext; - } - - @Override - public ListenableFuture> invokeRpc(QName rpc, YangInstanceIdentifier identifier, CompositeNode input) { - InvokeRpc rpcMsg = new InvokeRpc(rpc, identifier, input); - - return executeMsg(rpcMsg); - } - - @Override - public Set getSupportedRpcs() { - // TODO : check if we need to get this from routing registry - return Collections.emptySet(); - } - - @Override - public ListenableFuture> invokeRpc(QName rpc, CompositeNode input) { - InvokeRpc rpcMsg = new InvokeRpc(rpc, null, input); - return executeMsg(rpcMsg); - } - - private ListenableFuture> executeMsg(Object rpcMsg) { - ListenableFuture> listenableFuture = null; - - try { - Object response = ActorUtil.executeOperation(rpcBroker, rpcMsg, ActorUtil.ASK_DURATION, ActorUtil.AWAIT_DURATION); - if(response instanceof RpcResponse) { - - RpcResponse rpcResponse = (RpcResponse) response; - CompositeNode result = XmlUtils.xmlToCompositeNode(rpcResponse.getResultCompositeNode()); - listenableFuture = Futures.immediateFuture(RpcResultBuilder.success(result).build()); - - } else if(response instanceof ErrorResponse) { - - ErrorResponse errorResponse = (ErrorResponse) response; - Exception e = errorResponse.getException(); - final RpcResultBuilder failed = RpcResultBuilder.failed(); - failed.withError(null, null, e.getMessage(), null, null, e.getCause()); - listenableFuture = Futures.immediateFuture(failed.build()); - - } - } catch (Exception e) { - LOG.error("Error occurred while invoking RPC actor {}", e); - - final RpcResultBuilder failed = RpcResultBuilder.failed(); - failed.withError(null, null, e.getMessage(), null, null, e.getCause()); - listenableFuture = Futures.immediateFuture(failed.build()); +public class RemoteRpcImplementation implements RpcImplementation, RoutedRpcDefaultImplementation { + private static final Logger LOG = LoggerFactory.getLogger(RemoteRpcImplementation.class); + private final ActorRef rpcBroker; + private final SchemaContext schemaContext; + + public RemoteRpcImplementation(ActorRef rpcBroker, SchemaContext schemaContext) { + this.rpcBroker = rpcBroker; + this.schemaContext = schemaContext; + } + + @Override + public ListenableFuture> invokeRpc(QName rpc, + YangInstanceIdentifier identifier, CompositeNode input) { + InvokeRpc rpcMsg = new InvokeRpc(rpc, identifier, input); + + return executeMsg(rpcMsg); + } + + @Override + public Set getSupportedRpcs() { + // TODO : check if we need to get this from routing registry + return Collections.emptySet(); + } + + @Override + public ListenableFuture> invokeRpc(QName rpc, CompositeNode input) { + InvokeRpc rpcMsg = new InvokeRpc(rpc, null, input); + return executeMsg(rpcMsg); } - return listenableFuture; - } + private ListenableFuture> executeMsg(InvokeRpc rpcMsg) { + + final SettableFuture> listenableFuture = SettableFuture.create(); + + scala.concurrent.Future future = ask(rpcBroker, rpcMsg, + new Timeout(ActorUtil.ASK_DURATION)); + + OnComplete onComplete = new OnComplete() { + @Override + public void onComplete(Throwable failure, Object reply) throws Throwable { + if(failure != null) { + LOG.error("InvokeRpc failed", failure); + + RpcResult rpcResult; + if(failure instanceof RpcErrorsException) { + rpcResult = RpcResultBuilder.failed().withRpcErrors( + ((RpcErrorsException)failure).getRpcErrors()).build(); + } else { + rpcResult = RpcResultBuilder.failed().withError( + ErrorType.RPC, failure.getMessage(), failure).build(); + } + + listenableFuture.set(rpcResult); + return; + } + + RpcResponse rpcReply = (RpcResponse)reply; + CompositeNode result = XmlUtils.xmlToCompositeNode(rpcReply.getResultCompositeNode()); + listenableFuture.set(RpcResultBuilder.success(result).build()); + } + }; + + future.onComplete(onComplete, ExecutionContext.Implicits$.MODULE$.global()); + + return listenableFuture; + } } diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RemoteRpcProviderFactory.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RemoteRpcProviderFactory.java index fc75f7747a..0e6b795c05 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RemoteRpcProviderFactory.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RemoteRpcProviderFactory.java @@ -9,6 +9,7 @@ package org.opendaylight.controller.remote.rpc; +import org.opendaylight.controller.remote.rpc.utils.DefaultAkkaConfigurationReader; import org.opendaylight.controller.sal.core.api.Broker; import org.opendaylight.controller.sal.core.api.RpcProvisionRegistry; import org.osgi.framework.BundleContext; @@ -16,7 +17,7 @@ import org.osgi.framework.BundleContext; public class RemoteRpcProviderFactory { public static RemoteRpcProvider createInstance(final Broker broker, final BundleContext bundleContext){ - ActorSystemFactory.createInstance(bundleContext); + ActorSystemFactory.createInstance(bundleContext, new DefaultAkkaConfigurationReader()); RemoteRpcProvider rpcProvider = new RemoteRpcProvider(ActorSystemFactory.getInstance(), (RpcProvisionRegistry) broker); broker.registerProvider(rpcProvider); diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RpcBroker.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RpcBroker.java index 4ec96c29cd..2aca655d26 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RpcBroker.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RpcBroker.java @@ -8,11 +8,14 @@ package org.opendaylight.controller.remote.rpc; +import static akka.pattern.Patterns.ask; import akka.actor.ActorRef; import akka.actor.Props; +import akka.dispatch.OnComplete; import akka.japi.Creator; import akka.japi.Pair; -import org.opendaylight.controller.remote.rpc.messages.ErrorResponse; +import akka.util.Timeout; + import org.opendaylight.controller.remote.rpc.messages.ExecuteRpc; import org.opendaylight.controller.remote.rpc.messages.InvokeRpc; import org.opendaylight.controller.remote.rpc.messages.RpcResponse; @@ -23,12 +26,23 @@ import org.opendaylight.controller.remote.rpc.utils.RoutingLogic; import org.opendaylight.controller.xml.codec.XmlUtils; import org.opendaylight.controller.sal.connector.api.RpcRouter; import org.opendaylight.controller.sal.core.api.Broker; +import org.opendaylight.controller.sal.core.api.Broker.ProviderSession; +import org.opendaylight.yangtools.yang.common.RpcError; +import org.opendaylight.yangtools.yang.common.RpcError.ErrorType; 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.model.api.SchemaContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.JdkFutureAdapters; +import com.google.common.util.concurrent.ListenableFuture; + +import java.util.Arrays; +import java.util.Collection; import java.util.List; import java.util.concurrent.Future; @@ -38,81 +52,159 @@ import java.util.concurrent.Future; public class RpcBroker extends AbstractUntypedActor { - private static final Logger LOG = LoggerFactory.getLogger(RpcBroker.class); - private final Broker.ProviderSession brokerSession; - private final ActorRef rpcRegistry; - private SchemaContext schemaContext; - - private RpcBroker(Broker.ProviderSession brokerSession, ActorRef rpcRegistry, SchemaContext schemaContext){ - this.brokerSession = brokerSession; - this.rpcRegistry = rpcRegistry; - this.schemaContext = schemaContext; - } - - public static Props props(final Broker.ProviderSession brokerSession, final ActorRef rpcRegistry, final SchemaContext schemaContext){ - return Props.create(new Creator(){ - - @Override - public RpcBroker create() throws Exception { - return new RpcBroker(brokerSession, rpcRegistry, schemaContext); - } - }); - } - @Override - protected void handleReceive(Object message) throws Exception { - if(message instanceof InvokeRpc) { - invokeRemoteRpc((InvokeRpc) message); - } else if(message instanceof ExecuteRpc) { - executeRpc((ExecuteRpc) message); + private static final Logger LOG = LoggerFactory.getLogger(RpcBroker.class); + private final Broker.ProviderSession brokerSession; + private final ActorRef rpcRegistry; + private final SchemaContext schemaContext; + + private RpcBroker(Broker.ProviderSession brokerSession, ActorRef rpcRegistry, + SchemaContext schemaContext) { + this.brokerSession = brokerSession; + this.rpcRegistry = rpcRegistry; + this.schemaContext = schemaContext; } - } - - private void invokeRemoteRpc(InvokeRpc msg) { - // Look up the remote actor to execute rpc - LOG.debug("Looking up the remote actor for route {}", msg); - try { - // Find router - RpcRouter.RouteIdentifier routeId = new RouteIdentifierImpl(null, msg.getRpc(), msg.getIdentifier()); - RpcRegistry.Messages.FindRouters rpcMsg = new RpcRegistry.Messages.FindRouters(routeId); - RpcRegistry.Messages.FindRoutersReply rpcReply = - (RpcRegistry.Messages.FindRoutersReply) ActorUtil.executeOperation(rpcRegistry, rpcMsg, ActorUtil.LOCAL_ASK_DURATION, ActorUtil.LOCAL_AWAIT_DURATION); - - List> actorRefList = rpcReply.getRouterWithUpdateTime(); - - if(actorRefList == null || actorRefList.isEmpty()) { - LOG.debug("No remote actor found for rpc {{}}.", msg.getRpc()); - - getSender().tell(new ErrorResponse( - new IllegalStateException("No remote actor found for rpc execution of : " + msg.getRpc())), self()); - } else { - RoutingLogic logic = new LatestEntryRoutingLogic(actorRefList); - ExecuteRpc executeMsg = new ExecuteRpc(XmlUtils.inputCompositeNodeToXml(msg.getInput(), schemaContext), msg.getRpc()); - Object operationRes = ActorUtil.executeOperation(logic.select(), - executeMsg, ActorUtil.REMOTE_ASK_DURATION, ActorUtil.REMOTE_AWAIT_DURATION); + public static Props props(Broker.ProviderSession brokerSession, ActorRef rpcRegistry, + SchemaContext schemaContext) { + return Props.create(new RpcBrokerCreator(brokerSession, rpcRegistry, schemaContext)); + } - getSender().tell(operationRes, self()); - } - } catch (Exception e) { - LOG.error("invokeRemoteRpc: {}", e); - getSender().tell(new ErrorResponse(e), self()); + @Override + protected void handleReceive(Object message) throws Exception { + if(message instanceof InvokeRpc) { + invokeRemoteRpc((InvokeRpc) message); + } else if(message instanceof ExecuteRpc) { + executeRpc((ExecuteRpc) message); + } } - } + private void invokeRemoteRpc(final InvokeRpc msg) { + LOG.debug("Looking up the remote actor for rpc {}", msg.getRpc()); + + RpcRouter.RouteIdentifier routeId = new RouteIdentifierImpl( + null, msg.getRpc(), msg.getIdentifier()); + RpcRegistry.Messages.FindRouters findMsg = new RpcRegistry.Messages.FindRouters(routeId); + + scala.concurrent.Future future = ask(rpcRegistry, findMsg, + new Timeout(ActorUtil.LOCAL_ASK_DURATION)); + final ActorRef sender = getSender(); + final ActorRef self = self(); - private void executeRpc(ExecuteRpc msg) { - LOG.debug("Executing rpc for rpc {}", msg.getRpc()); - try { - Future> rpc = brokerSession.rpc(msg.getRpc(), - XmlUtils.inputXmlToCompositeNode(msg.getRpc(), msg.getInputCompositeNode(), schemaContext)); - RpcResult rpcResult = rpc != null ? rpc.get():null; - CompositeNode result = rpcResult != null ? rpcResult.getResult() : null; - getSender().tell(new RpcResponse(XmlUtils.outputCompositeNodeToXml(result, schemaContext)), self()); - } catch (Exception e) { - LOG.error("executeRpc: {}", e); - getSender().tell(new ErrorResponse(e), self()); + OnComplete onComplete = new OnComplete() { + @Override + public void onComplete(Throwable failure, Object reply) throws Throwable { + if(failure != null) { + LOG.error("FindRouters failed", failure); + sender.tell(new akka.actor.Status.Failure(failure), self); + return; + } + + RpcRegistry.Messages.FindRoutersReply findReply = + (RpcRegistry.Messages.FindRoutersReply)reply; + + List> actorRefList = findReply.getRouterWithUpdateTime(); + + if(actorRefList == null || actorRefList.isEmpty()) { + String message = String.format( + "No remote implementation found for rpc %s", msg.getRpc()); + sender.tell(new akka.actor.Status.Failure(new RpcErrorsException( + message, Arrays.asList(RpcResultBuilder.newError(ErrorType.RPC, + "operation-not-supported", message)))), self); + return; + } + + finishInvokeRpc(actorRefList, msg, sender, self); + } + }; + + future.onComplete(onComplete, getContext().dispatcher()); } - } + protected void finishInvokeRpc(final List> actorRefList, + final InvokeRpc msg, final ActorRef sender, final ActorRef self) { + + RoutingLogic logic = new LatestEntryRoutingLogic(actorRefList); + + ExecuteRpc executeMsg = new ExecuteRpc(XmlUtils.inputCompositeNodeToXml(msg.getInput(), + schemaContext), msg.getRpc()); + + scala.concurrent.Future future = ask(logic.select(), executeMsg, + new Timeout(ActorUtil.REMOTE_ASK_DURATION)); + + OnComplete onComplete = new OnComplete() { + @Override + public void onComplete(Throwable failure, Object reply) throws Throwable { + if(failure != null) { + LOG.error("ExecuteRpc failed", failure); + sender.tell(new akka.actor.Status.Failure(failure), self); + return; + } + + sender.tell(reply, self); + } + }; + + future.onComplete(onComplete, getContext().dispatcher()); + } + + private void executeRpc(final ExecuteRpc msg) { + LOG.debug("Executing rpc {}", msg.getRpc()); + + Future> future = brokerSession.rpc(msg.getRpc(), + XmlUtils.inputXmlToCompositeNode(msg.getRpc(), msg.getInputCompositeNode(), + schemaContext)); + + ListenableFuture> listenableFuture = + JdkFutureAdapters.listenInPoolThread(future); + + final ActorRef sender = getSender(); + final ActorRef self = self(); + + Futures.addCallback(listenableFuture, new FutureCallback>() { + @Override + public void onSuccess(RpcResult result) { + if(result.isSuccessful()) { + sender.tell(new RpcResponse(XmlUtils.outputCompositeNodeToXml(result.getResult(), + schemaContext)), self); + } else { + String message = String.format("Execution of RPC %s failed", msg.getRpc()); + Collection errors = result.getErrors(); + if(errors == null || errors.size() == 0) { + errors = Arrays.asList(RpcResultBuilder.newError(ErrorType.RPC, + null, message)); + } + + sender.tell(new akka.actor.Status.Failure(new RpcErrorsException( + message, errors)), self); + } + } + + @Override + public void onFailure(Throwable t) { + LOG.error("executeRpc for {} failed: {}", msg.getRpc(), t); + sender.tell(new akka.actor.Status.Failure(t), self); + } + }); + } + + private static class RpcBrokerCreator implements Creator { + private static final long serialVersionUID = 1L; + + final Broker.ProviderSession brokerSession; + final ActorRef rpcRegistry; + final SchemaContext schemaContext; + + RpcBrokerCreator(ProviderSession brokerSession, ActorRef rpcRegistry, + SchemaContext schemaContext) { + this.brokerSession = brokerSession; + this.rpcRegistry = rpcRegistry; + this.schemaContext = schemaContext; + } + + @Override + public RpcBroker create() throws Exception { + return new RpcBroker(brokerSession, rpcRegistry, schemaContext); + } + } } diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RpcErrorsException.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RpcErrorsException.java new file mode 100644 index 0000000000..7e4d8a034e --- /dev/null +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RpcErrorsException.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2014 Brocade Communications 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.remote.rpc; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.opendaylight.yangtools.yang.common.RpcError; +import org.opendaylight.yangtools.yang.common.RpcError.ErrorSeverity; +import org.opendaylight.yangtools.yang.common.RpcError.ErrorType; +import org.opendaylight.yangtools.yang.common.RpcResultBuilder; + +/** + * An Exception for transferring RpcErrors. + * + * @author Thomas Pantelis + */ +public class RpcErrorsException extends Exception { + + private static final long serialVersionUID = 1L; + + private static class RpcErrorData implements Serializable { + private static final long serialVersionUID = 1L; + + final ErrorSeverity severity; + final ErrorType errorType; + final String tag; + final String applicationTag; + final String message; + final String info; + final Throwable cause; + + RpcErrorData(ErrorSeverity severity, ErrorType errorType, String tag, + String applicationTag, String message, String info, Throwable cause) { + this.severity = severity; + this.errorType = errorType; + this.tag = tag; + this.applicationTag = applicationTag; + this.message = message; + this.info = info; + this.cause = cause; + } + } + + private final List rpcErrorDataList = new ArrayList<>(); + + public RpcErrorsException(String message, Iterable rpcErrors) { + super(message); + + for(RpcError rpcError: rpcErrors) { + rpcErrorDataList.add(new RpcErrorData(rpcError.getSeverity(), rpcError.getErrorType(), + rpcError.getTag(), rpcError.getApplicationTag(), rpcError.getMessage(), + rpcError.getInfo(), rpcError.getCause())); + } + } + + public Collection getRpcErrors() { + Collection rpcErrors = new ArrayList<>(); + for(RpcErrorData ed: rpcErrorDataList) { + RpcError rpcError = ed.severity == ErrorSeverity.ERROR ? + RpcResultBuilder.newError(ed.errorType, ed.tag, ed.message, ed.applicationTag, + ed.info, ed.cause) : + RpcResultBuilder.newWarning(ed.errorType, ed.tag, ed.message, ed.applicationTag, + ed.info, ed.cause); + rpcErrors.add(rpcError); + } + + return rpcErrors; + } +} diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RpcManager.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RpcManager.java index 96f2472428..d4da226b9d 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RpcManager.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RpcManager.java @@ -15,8 +15,11 @@ import akka.actor.Props; import akka.actor.SupervisorStrategy; import akka.japi.Creator; import akka.japi.Function; +import com.typesafe.config.Config; +import com.typesafe.config.ConfigFactory; import org.opendaylight.controller.remote.rpc.messages.UpdateSchemaContext; import org.opendaylight.controller.remote.rpc.registry.RpcRegistry; +import org.opendaylight.controller.remote.rpc.utils.ActorUtil; import org.opendaylight.controller.sal.core.api.Broker; import org.opendaylight.controller.sal.core.api.RpcProvisionRegistry; import org.opendaylight.yangtools.yang.common.QName; @@ -69,10 +72,16 @@ public class RpcManager extends AbstractUntypedActor { private void createRpcActors() { LOG.debug("Create rpc registry and broker actors"); + Config conf = ConfigFactory.load(); - rpcRegistry = getContext().actorOf(Props.create(RpcRegistry.class), ActorConstants.RPC_REGISTRY); + rpcRegistry = + getContext().actorOf(Props.create(RpcRegistry.class). + withMailbox(ActorUtil.MAILBOX), ActorConstants.RPC_REGISTRY); + + rpcBroker = + getContext().actorOf(RpcBroker.props(brokerSession, rpcRegistry, schemaContext). + withMailbox(ActorUtil.MAILBOX),ActorConstants.RPC_BROKER); - rpcBroker = getContext().actorOf(RpcBroker.props(brokerSession, rpcRegistry, schemaContext), ActorConstants.RPC_BROKER); RpcRegistry.Messages.SetLocalRouter localRouter = new RpcRegistry.Messages.SetLocalRouter(rpcBroker); rpcRegistry.tell(localRouter, self()); } diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/ErrorResponse.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/ErrorResponse.java deleted file mode 100644 index 2c26243fe9..0000000000 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/ErrorResponse.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v1.0 which accompanies this distribution, - * and is available at http://www.eclipse.org/legal/epl-v10.html - */ -package org.opendaylight.controller.remote.rpc.messages; - -import com.google.common.base.Preconditions; - -import java.io.Serializable; - -public class ErrorResponse implements Serializable { - - private final Exception exception; - - public ErrorResponse(final Exception e) { - Preconditions.checkNotNull(e, "Exception should be present for error message"); - this.exception = e; - } - - public Exception getException() { - return exception; - } -} diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/registry/RpcRegistry.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/registry/RpcRegistry.java index 76f5930457..5109d31644 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/registry/RpcRegistry.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/registry/RpcRegistry.java @@ -20,6 +20,7 @@ import akka.pattern.Patterns; import com.google.common.base.Preconditions; import org.opendaylight.controller.remote.rpc.registry.gossip.Bucket; import org.opendaylight.controller.remote.rpc.registry.gossip.BucketStore; +import org.opendaylight.controller.remote.rpc.utils.ActorUtil; import org.opendaylight.controller.sal.connector.api.RpcRouter; import scala.concurrent.Future; @@ -107,7 +108,7 @@ public class RpcRegistry extends UntypedActor { Preconditions.checkState(localRouter != null, "Router must be set first"); - Future futureReply = Patterns.ask(bucketStore, new GetLocalBucket(), 1000); + Future futureReply = Patterns.ask(bucketStore, new GetLocalBucket(), ActorUtil.ASK_DURATION.toMillis()); futureReply.map(getMapperToAddRoutes(msg.getRouteIdentifiers()), getContext().dispatcher()); } @@ -116,7 +117,7 @@ public class RpcRegistry extends UntypedActor { */ private void receiveRemoveRoutes(RemoveRoutes msg) { - Future futureReply = Patterns.ask(bucketStore, new GetLocalBucket(), 1000); + Future futureReply = Patterns.ask(bucketStore, new GetLocalBucket(), ActorUtil.ASK_DURATION.toMillis()); futureReply.map(getMapperToRemoveRoutes(msg.getRouteIdentifiers()), getContext().dispatcher()); } @@ -129,7 +130,7 @@ public class RpcRegistry extends UntypedActor { private void receiveGetRouter(FindRouters msg) { final ActorRef sender = getSender(); - Future futureReply = Patterns.ask(bucketStore, new GetAllBuckets(), 1000); + Future futureReply = Patterns.ask(bucketStore, new GetAllBuckets(), ActorUtil.ASK_DURATION.toMillis()); futureReply.map(getMapperToGetRouter(msg.getRouteIdentifier(), sender), getContext().dispatcher()); } diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/registry/gossip/BucketStore.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/registry/gossip/BucketStore.java index 3b078aa062..ff51f4fcfa 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/registry/gossip/BucketStore.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/registry/gossip/BucketStore.java @@ -16,6 +16,7 @@ import akka.actor.UntypedActor; import akka.cluster.ClusterActorRefProvider; import akka.event.Logging; import akka.event.LoggingAdapter; +import org.opendaylight.controller.remote.rpc.utils.ActorUtil; import org.opendaylight.controller.utils.ConditionalProbe; import java.util.HashMap; @@ -76,7 +77,7 @@ public class BucketStore extends UntypedActor { selfAddress = provider.getDefaultAddress(); if ( provider instanceof ClusterActorRefProvider) - getContext().actorOf(Props.create(Gossiper.class), "gossiper"); + getContext().actorOf(Props.create(Gossiper.class).withMailbox(ActorUtil.MAILBOX), "gossiper"); } @Override diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/registry/gossip/Gossiper.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/registry/gossip/Gossiper.java index a8bc25c40b..f6ce5e55f3 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/registry/gossip/Gossiper.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/registry/gossip/Gossiper.java @@ -21,6 +21,7 @@ import akka.dispatch.Mapper; import akka.event.Logging; import akka.event.LoggingAdapter; import akka.pattern.Patterns; +import org.opendaylight.controller.remote.rpc.utils.ActorUtil; import scala.concurrent.Future; import scala.concurrent.duration.FiniteDuration; @@ -105,7 +106,7 @@ public class Gossiper extends UntypedActor { if (autoStartGossipTicks) { gossipTask = getContext().system().scheduler().schedule( new FiniteDuration(1, TimeUnit.SECONDS), //initial delay - new FiniteDuration(500, TimeUnit.MILLISECONDS), //interval + ActorUtil.GOSSIP_TICK_INTERVAL, //interval getSelf(), //target new Messages.GossiperMessages.GossipTick(), //message getContext().dispatcher(), //execution context @@ -227,7 +228,9 @@ public class Gossiper extends UntypedActor { return; final ActorRef sender = getSender(); - Future futureReply = Patterns.ask(getContext().parent(), new GetBucketVersions(), 1000); + Future futureReply = + Patterns.ask(getContext().parent(), new GetBucketVersions(), ActorUtil.ASK_DURATION.toMillis()); + futureReply.map(getMapperToProcessRemoteStatus(sender, status), getContext().dispatcher()); } @@ -267,7 +270,8 @@ public class Gossiper extends UntypedActor { */ void sendGossipTo(final ActorRef remote, final Set
addresses){ - Future futureReply = Patterns.ask(getContext().parent(), new GetBucketsByMembers(addresses), 1000); + Future futureReply = + Patterns.ask(getContext().parent(), new GetBucketsByMembers(addresses), ActorUtil.ASK_DURATION.toMillis()); futureReply.map(getMapperToSendGossip(remote), getContext().dispatcher()); } @@ -279,7 +283,10 @@ public class Gossiper extends UntypedActor { void getLocalStatusAndSendTo(Address remoteActorSystemAddress){ //Get local status from bucket store and send to remote - Future futureReply = Patterns.ask(getContext().parent(), new GetBucketVersions(), 1000); + Future futureReply = + Patterns.ask(getContext().parent(), new GetBucketVersions(), ActorUtil.ASK_DURATION.toMillis()); + + //Find gossiper on remote system ActorSelection remoteRef = getContext().system().actorSelection( remoteActorSystemAddress.toString() + getSelf().path().toStringWithoutAddress()); diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/ActorUtil.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/ActorUtil.java index b7b2216a08..e2baffa1b1 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/ActorUtil.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/ActorUtil.java @@ -8,40 +8,15 @@ */ package org.opendaylight.controller.remote.rpc.utils; -import akka.actor.ActorRef; -import akka.util.Timeout; -import scala.concurrent.Await; -import scala.concurrent.Future; import scala.concurrent.duration.Duration; import scala.concurrent.duration.FiniteDuration; import java.util.concurrent.TimeUnit; -import static akka.pattern.Patterns.ask; - public class ActorUtil { - public static final FiniteDuration LOCAL_ASK_DURATION = Duration.create(2, TimeUnit.SECONDS); - public static final FiniteDuration REMOTE_ASK_DURATION = Duration.create(15, TimeUnit.SECONDS); - public static final FiniteDuration ASK_DURATION = Duration.create(17, TimeUnit.SECONDS); - public static final FiniteDuration LOCAL_AWAIT_DURATION = Duration.create(2, TimeUnit.SECONDS); - public static final FiniteDuration REMOTE_AWAIT_DURATION = Duration.create(15, TimeUnit.SECONDS); - public static final FiniteDuration AWAIT_DURATION = Duration.create(17, TimeUnit.SECONDS); - - /** - * Executes an operation on a local actor and wait for it's response - * @param actor - * @param message - * @param askDuration - * @param awaitDuration - * @return The response of the operation - */ - public static Object executeOperation(ActorRef actor, Object message, - FiniteDuration askDuration, FiniteDuration awaitDuration) throws Exception{ - Future future = - ask(actor, message, new Timeout(askDuration)); - - return Await.result(future, awaitDuration); - } - - + public static final FiniteDuration LOCAL_ASK_DURATION = Duration.create(2, TimeUnit.SECONDS); + public static final FiniteDuration REMOTE_ASK_DURATION = Duration.create(15, TimeUnit.SECONDS); + public static final FiniteDuration ASK_DURATION = Duration.create(17, TimeUnit.SECONDS); + public static final FiniteDuration GOSSIP_TICK_INTERVAL = Duration.create(500, TimeUnit.MILLISECONDS); + public static final String MAILBOX = "bounded-mailbox"; } diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/AkkaConfigurationReader.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/AkkaConfigurationReader.java new file mode 100644 index 0000000000..035ce9a203 --- /dev/null +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/AkkaConfigurationReader.java @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.remote.rpc.utils; + +import com.typesafe.config.Config; + +public interface AkkaConfigurationReader { + Config read(); +} diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/DefaultAkkaConfigurationReader.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/DefaultAkkaConfigurationReader.java new file mode 100644 index 0000000000..a44d20ca39 --- /dev/null +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/DefaultAkkaConfigurationReader.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.remote.rpc.utils; + +import com.google.common.base.Preconditions; +import com.typesafe.config.Config; +import com.typesafe.config.ConfigFactory; + +import java.io.File; + +public class DefaultAkkaConfigurationReader implements AkkaConfigurationReader { + public static final String AKKA_CONF_PATH = "./configuration/initial/akka.conf"; + + @Override public Config read() { + File defaultConfigFile = new File(AKKA_CONF_PATH); + Preconditions.checkState(defaultConfigFile.exists(), "akka.conf is missing"); + return ConfigFactory.parseFile(defaultConfigFile); + + } +} diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/resources/application.conf b/opendaylight/md-sal/sal-remoterpc-connector/src/main/resources/application.conf index 711ae1c48b..266832a0ab 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/resources/application.conf +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/resources/application.conf @@ -38,10 +38,15 @@ odl-cluster-data { } odl-cluster-rpc { + bounded-mailbox { + mailbox-type = "org.opendaylight.controller.common.actor.MeteredBoundedMailbox" + mailbox-capacity = 1000 + mailbox-push-timeout-time = 100ms + } + akka { actor { provider = "akka.cluster.ClusterActorRefProvider" - } remote { log-remote-lifecycle-events = off diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/AbstractRpcTest.java b/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/AbstractRpcTest.java new file mode 100644 index 0000000000..8d886829aa --- /dev/null +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/AbstractRpcTest.java @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2014 Brocade Communications 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.remote.rpc; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import java.io.File; +import java.net.URI; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.mockito.Mockito; +import org.opendaylight.controller.sal.core.api.Broker; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.common.RpcError; +import org.opendaylight.yangtools.yang.common.RpcResult; +import org.opendaylight.yangtools.yang.common.RpcError.ErrorSeverity; +import org.opendaylight.yangtools.yang.common.RpcError.ErrorType; +import org.opendaylight.yangtools.yang.data.api.CompositeNode; +import org.opendaylight.yangtools.yang.data.api.Node; +import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode; +import org.opendaylight.yangtools.yang.data.impl.util.CompositeNodeBuilder; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl; + +import akka.actor.ActorRef; +import akka.actor.ActorSystem; +import akka.testkit.JavaTestKit; + +import com.google.common.collect.ImmutableList; +import com.typesafe.config.ConfigFactory; + +/** + * Base class for RPC tests. + * + * @author Thomas Pantelis + */ +public class AbstractRpcTest { + static final String TEST_REV = "2014-08-28"; + static final String TEST_NS = "urn:test"; + static final URI TEST_URI = URI.create(TEST_NS); + static final QName TEST_RPC = QName.create(TEST_NS, TEST_REV, "test-rpc"); + static final QName TEST_RPC_INPUT = QName.create(TEST_NS, TEST_REV, "input"); + static final QName TEST_RPC_INPUT_DATA = QName.create(TEST_NS, TEST_REV, "input-data"); + static final QName TEST_RPC_OUTPUT = QName.create(TEST_NS, TEST_REV, "output"); + static final QName TEST_RPC_OUTPUT_DATA = new QName(TEST_URI, "output-data"); + + static ActorSystem node1; + static ActorSystem node2; + + protected ActorRef rpcBroker1; + protected JavaTestKit probeReg1; + protected ActorRef rpcBroker2; + protected JavaTestKit probeReg2; + protected Broker.ProviderSession brokerSession; + protected SchemaContext schemaContext; + + @BeforeClass + public static void setup() throws InterruptedException { + node1 = ActorSystem.create("opendaylight-rpc", ConfigFactory.load().getConfig("memberA")); + node2 = ActorSystem.create("opendaylight-rpc", ConfigFactory.load().getConfig("memberB")); + } + + @AfterClass + public static void teardown() { + JavaTestKit.shutdownActorSystem(node1); + JavaTestKit.shutdownActorSystem(node2); + node1 = null; + node2 = null; + } + + @Before + public void setUp() { + schemaContext = new YangParserImpl().parseFiles(Arrays.asList( + new File(RpcBrokerTest.class.getResource("/test-rpc.yang").getPath()))); + + brokerSession = Mockito.mock(Broker.ProviderSession.class); + probeReg1 = new JavaTestKit(node1); + rpcBroker1 = node1.actorOf(RpcBroker.props(brokerSession, probeReg1.getRef(), schemaContext)); + probeReg2 = new JavaTestKit(node2); + rpcBroker2 = node2.actorOf(RpcBroker.props(brokerSession, probeReg2.getRef(), schemaContext)); + + } + + static void assertRpcErrorEquals(RpcError rpcError, ErrorSeverity severity, + ErrorType errorType, String tag, String message, String applicationTag, String info, + String causeMsg) { + assertEquals("getSeverity", severity, rpcError.getSeverity()); + assertEquals("getErrorType", errorType, rpcError.getErrorType()); + assertEquals("getTag", tag, rpcError.getTag()); + assertTrue("getMessage contains " + message, rpcError.getMessage().contains(message)); + assertEquals("getApplicationTag", applicationTag, rpcError.getApplicationTag()); + assertEquals("getInfo", info, rpcError.getInfo()); + + if(causeMsg == null) { + assertNull("Unexpected cause " + rpcError.getCause(), rpcError.getCause()); + } else { + assertEquals("Cause message", causeMsg, rpcError.getCause().getMessage()); + } + } + + static void assertCompositeNodeEquals(CompositeNode exp, CompositeNode actual) { + assertEquals("NodeType getNamespace", exp.getNodeType().getNamespace(), + actual.getNodeType().getNamespace()); + assertEquals("NodeType getLocalName", exp.getNodeType().getLocalName(), + actual.getNodeType().getLocalName()); + for(Node child: exp.getValue()) { + List> c = actual.get(child.getNodeType()); + assertNotNull("Missing expected child " + child.getNodeType(), c); + if(child instanceof CompositeNode) { + assertCompositeNodeEquals((CompositeNode) child, (CompositeNode)c.get(0)); + } else { + assertEquals("Value for Node " + child.getNodeType(), child.getValue(), + c.get(0).getValue()); + } + } + } + + static CompositeNode makeRPCInput(String data) { + CompositeNodeBuilder builder = ImmutableCompositeNode.builder() + .setQName(TEST_RPC_INPUT).addLeaf(TEST_RPC_INPUT_DATA, data); + return ImmutableCompositeNode.create( + TEST_RPC, ImmutableList.>of(builder.toInstance())); + } + + static CompositeNode makeRPCOutput(String data) { + CompositeNodeBuilder builder = ImmutableCompositeNode.builder() + .setQName(TEST_RPC_OUTPUT).addLeaf(TEST_RPC_OUTPUT_DATA, data); + return ImmutableCompositeNode.create( + TEST_RPC, ImmutableList.>of(builder.toInstance())); + } + + static void assertFailedRpcResult(RpcResult rpcResult, ErrorSeverity severity, + ErrorType errorType, String tag, String message, String applicationTag, String info, + String causeMsg) { + + assertNotNull("RpcResult was null", rpcResult); + assertEquals("isSuccessful", false, rpcResult.isSuccessful()); + Collection rpcErrors = rpcResult.getErrors(); + assertEquals("RpcErrors count", 1, rpcErrors.size()); + assertRpcErrorEquals(rpcErrors.iterator().next(), severity, errorType, tag, message, + applicationTag, info, causeMsg); + } + + static void assertSuccessfulRpcResult(RpcResult rpcResult, + CompositeNode expOutput) { + + assertNotNull("RpcResult was null", rpcResult); + assertEquals("isSuccessful", true, rpcResult.isSuccessful()); + assertCompositeNodeEquals(expOutput, rpcResult.getResult()); + } + + static class TestException extends Exception { + private static final long serialVersionUID = 1L; + + static final String MESSAGE = "mock error"; + + TestException() { + super(MESSAGE); + } + } +} diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/ActorSystemFactoryTest.java b/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/ActorSystemFactoryTest.java index ed5fa6d16e..cd1cd91869 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/ActorSystemFactoryTest.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/ActorSystemFactoryTest.java @@ -10,9 +10,11 @@ package org.opendaylight.controller.remote.rpc; import akka.actor.ActorSystem; +import com.typesafe.config.ConfigFactory; import junit.framework.Assert; import org.junit.After; import org.junit.Test; +import org.opendaylight.controller.remote.rpc.utils.AkkaConfigurationReader; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; @@ -27,13 +29,17 @@ public class ActorSystemFactoryTest { public void testActorSystemCreation(){ BundleContext context = mock(BundleContext.class); when(context.getBundle()).thenReturn(mock(Bundle.class)); - ActorSystemFactory.createInstance(context); + + AkkaConfigurationReader reader = mock(AkkaConfigurationReader.class); + when(reader.read()).thenReturn(ConfigFactory.load()); + + ActorSystemFactory.createInstance(context, reader); system = ActorSystemFactory.getInstance(); Assert.assertNotNull(system); // Check illegal state exception try { - ActorSystemFactory.createInstance(context); + ActorSystemFactory.createInstance(context, reader); fail("Illegal State exception should be thrown, while creating actor system second time"); } catch (IllegalStateException e) { } @@ -45,5 +51,4 @@ public class ActorSystemFactoryTest { system.shutdown(); } } - } diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/RemoteRpcImplementationTest.java b/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/RemoteRpcImplementationTest.java new file mode 100644 index 0000000000..6c3a57b344 --- /dev/null +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/RemoteRpcImplementationTest.java @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2014 Brocade Communications 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.remote.rpc; + +import static org.junit.Assert.assertEquals; +import java.net.URI; +import java.util.Arrays; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; + +import org.junit.Test; +import org.opendaylight.controller.remote.rpc.messages.InvokeRpc; +import org.opendaylight.controller.remote.rpc.messages.RpcResponse; +import org.opendaylight.controller.xml.codec.XmlUtils; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.common.RpcResult; +import org.opendaylight.yangtools.yang.common.RpcResultBuilder; +import org.opendaylight.yangtools.yang.common.RpcError.ErrorSeverity; +import org.opendaylight.yangtools.yang.common.RpcError.ErrorType; +import org.opendaylight.yangtools.yang.data.api.CompositeNode; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; + +import akka.testkit.JavaTestKit; + +import com.google.common.util.concurrent.ListenableFuture; + +/*** + * Unit tests for RemoteRpcImplementation. + * + * @author Thomas Pantelis + */ +public class RemoteRpcImplementationTest extends AbstractRpcTest { + + @Test + public void testInvokeRpc() throws Exception { + final AtomicReference assertError = new AtomicReference<>(); + try { + RemoteRpcImplementation rpcImpl = new RemoteRpcImplementation( + probeReg1.getRef(), schemaContext); + + final CompositeNode input = makeRPCInput("foo"); + final CompositeNode output = makeRPCOutput("bar"); + final AtomicReference invokeRpcMsg = setupInvokeRpcReply(assertError, output); + + ListenableFuture> future = rpcImpl.invokeRpc(TEST_RPC, input); + + RpcResult rpcResult = future.get(5, TimeUnit.SECONDS); + + assertSuccessfulRpcResult(rpcResult, (CompositeNode)output.getValue().get(0)); + + assertEquals("getRpc", TEST_RPC, invokeRpcMsg.get().getRpc()); + assertEquals("getInput", input, invokeRpcMsg.get().getInput()); + } finally { + if(assertError.get() != null) { + throw assertError.get(); + } + } + } + + @Test + public void testInvokeRpcWithIdentifier() throws Exception { + final AtomicReference assertError = new AtomicReference<>(); + try { + RemoteRpcImplementation rpcImpl = new RemoteRpcImplementation( + probeReg1.getRef(), schemaContext); + + QName instanceQName = new QName(new URI("ns"), "instance"); + YangInstanceIdentifier identifier = YangInstanceIdentifier.of(instanceQName); + + CompositeNode input = makeRPCInput("foo"); + CompositeNode output = makeRPCOutput("bar"); + final AtomicReference invokeRpcMsg = setupInvokeRpcReply(assertError, output); + + ListenableFuture> future = rpcImpl.invokeRpc( + TEST_RPC, identifier, input); + + RpcResult rpcResult = future.get(5, TimeUnit.SECONDS); + + assertSuccessfulRpcResult(rpcResult, (CompositeNode)output.getValue().get(0)); + + assertEquals("getRpc", TEST_RPC, invokeRpcMsg.get().getRpc()); + assertEquals("getInput", input, invokeRpcMsg.get().getInput()); + assertEquals("getRoute", identifier, invokeRpcMsg.get().getIdentifier()); + } finally { + if(assertError.get() != null) { + throw assertError.get(); + } + } + } + + @Test + public void testInvokeRpcWithRpcErrorsException() throws Exception { + final AtomicReference assertError = new AtomicReference<>(); + try { + RemoteRpcImplementation rpcImpl = new RemoteRpcImplementation( + probeReg1.getRef(), schemaContext); + + final CompositeNode input = makeRPCInput("foo"); + + setupInvokeRpcErrorReply(assertError, new RpcErrorsException( + "mock", Arrays.asList(RpcResultBuilder.newError(ErrorType.RPC, "tag", + "error", "appTag", "info", null)))); + + ListenableFuture> future = rpcImpl.invokeRpc(TEST_RPC, input); + + RpcResult rpcResult = future.get(5, TimeUnit.SECONDS); + + assertFailedRpcResult(rpcResult, ErrorSeverity.ERROR, ErrorType.RPC, "tag", + "error", "appTag", "info", null); + } finally { + if(assertError.get() != null) { + throw assertError.get(); + } + } + } + + @Test + public void testInvokeRpcWithOtherException() throws Exception { + final AtomicReference assertError = new AtomicReference<>(); + try { + RemoteRpcImplementation rpcImpl = new RemoteRpcImplementation( + probeReg1.getRef(), schemaContext); + + final CompositeNode input = makeRPCInput("foo"); + + setupInvokeRpcErrorReply(assertError, new TestException()); + + ListenableFuture> future = rpcImpl.invokeRpc(TEST_RPC, input); + + RpcResult rpcResult = future.get(5, TimeUnit.SECONDS); + + assertFailedRpcResult(rpcResult, ErrorSeverity.ERROR, ErrorType.RPC, "operation-failed", + TestException.MESSAGE, null, null, TestException.MESSAGE); + } finally { + if(assertError.get() != null) { + throw assertError.get(); + } + } + } + + private AtomicReference setupInvokeRpcReply( + final AtomicReference assertError, final CompositeNode output) { + return setupInvokeRpcReply(assertError, output, null); + } + + private AtomicReference setupInvokeRpcErrorReply( + final AtomicReference assertError, final Exception error) { + return setupInvokeRpcReply(assertError, null, error); + } + + private AtomicReference setupInvokeRpcReply( + final AtomicReference assertError, final CompositeNode output, + final Exception error) { + final AtomicReference invokeRpcMsg = new AtomicReference<>(); + + new Thread() { + @Override + public void run() { + try { + invokeRpcMsg.set(probeReg1.expectMsgClass( + JavaTestKit.duration("5 seconds"), InvokeRpc.class)); + + if(output != null) { + probeReg1.reply(new RpcResponse(XmlUtils.outputCompositeNodeToXml( + output, schemaContext))); + } else { + probeReg1.reply(new akka.actor.Status.Failure(error)); + } + + } catch(AssertionError e) { + assertError.set(e); + } + } + + }.start(); + + return invokeRpcMsg; + } +} diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/RemoteRpcProviderTest.java b/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/RemoteRpcProviderTest.java index 17ad237ad7..8a7e4a0398 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/RemoteRpcProviderTest.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/RemoteRpcProviderTest.java @@ -14,8 +14,8 @@ import akka.actor.ActorRef; import akka.actor.ActorSystem; import akka.testkit.JavaTestKit; import com.typesafe.config.ConfigFactory; -import junit.framework.Assert; import org.junit.AfterClass; +import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; import org.opendaylight.controller.sal.core.api.Broker; @@ -25,7 +25,6 @@ import org.opendaylight.yangtools.yang.model.api.SchemaContext; import scala.concurrent.Await; import scala.concurrent.duration.Duration; - import java.util.concurrent.TimeUnit; import static org.mockito.Mockito.mock; @@ -38,7 +37,7 @@ public class RemoteRpcProviderTest { @BeforeClass public static void setup() throws InterruptedException { - system = ActorSystem.create("opendaylight-rpc", ConfigFactory.load().getConfig("odl-cluster")); + system = ActorSystem.create("odl-cluster-rpc", ConfigFactory.load().getConfig("odl-cluster-rpc")); } @AfterClass @@ -59,7 +58,4 @@ public class RemoteRpcProviderTest { Duration.create(2, TimeUnit.SECONDS)); Assert.assertTrue(actorRef.path().toString().contains(ActorConstants.RPC_MANAGER_PATH)); } - - - } diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/RouteRpcListenerTest.java b/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/RouteRpcListenerTest.java index 9b6215addd..98a33bf4e6 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/RouteRpcListenerTest.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/RouteRpcListenerTest.java @@ -32,7 +32,7 @@ public class RouteRpcListenerTest { @BeforeClass public static void setup() throws InterruptedException { - system = ActorSystem.create("opendaylight-rpc", ConfigFactory.load().getConfig("odl-cluster")); + system = ActorSystem.create("opendaylight-rpc", ConfigFactory.load().getConfig("odl-cluster-rpc")); } @AfterClass diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/RpcBrokerTest.java b/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/RpcBrokerTest.java index d9a3b6a414..28b1b476cd 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/RpcBrokerTest.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/RpcBrokerTest.java @@ -10,144 +10,268 @@ package org.opendaylight.controller.remote.rpc; import akka.actor.ActorRef; -import akka.actor.ActorSystem; import akka.japi.Pair; import akka.testkit.JavaTestKit; + +import com.google.common.collect.Lists; import com.google.common.util.concurrent.Futures; -import com.typesafe.config.ConfigFactory; -import junit.framework.Assert; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; +import static org.junit.Assert.assertEquals; import org.junit.Test; -import org.mockito.Mockito; -import org.opendaylight.controller.remote.rpc.messages.ErrorResponse; +import org.mockito.ArgumentCaptor; +import org.opendaylight.controller.remote.rpc.messages.ExecuteRpc; import org.opendaylight.controller.remote.rpc.messages.InvokeRpc; import org.opendaylight.controller.remote.rpc.messages.RpcResponse; import org.opendaylight.controller.remote.rpc.registry.RpcRegistry; -import org.opendaylight.controller.sal.common.util.Rpcs; -import org.opendaylight.controller.sal.core.api.Broker; +import org.opendaylight.controller.remote.rpc.registry.RpcRegistry.Messages.FindRouters; +import org.opendaylight.controller.sal.connector.api.RpcRouter.RouteIdentifier; +import org.opendaylight.controller.xml.codec.XmlUtils; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.common.RpcError; +import org.opendaylight.yangtools.yang.common.RpcError.ErrorSeverity; +import org.opendaylight.yangtools.yang.common.RpcError.ErrorType; 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.api.ModifyAction; -import org.opendaylight.yangtools.yang.data.api.Node; -import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode; -import org.opendaylight.yangtools.yang.model.api.SchemaContext; - +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import java.net.URI; import java.net.URISyntaxException; -import java.util.ArrayList; -import java.util.Collection; +import java.util.Arrays; +import java.util.Collections; import java.util.List; -import java.util.concurrent.Future; -import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.any; + +public class RpcBrokerTest extends AbstractRpcTest { + + @Test + public void testInvokeRpcWithNoRemoteActor() throws Exception { + new JavaTestKit(node1) {{ + CompositeNode input = makeRPCInput("foo"); + + InvokeRpc invokeMsg = new InvokeRpc(TEST_RPC, null, input); + rpcBroker1.tell(invokeMsg, getRef()); + + probeReg1.expectMsgClass(duration("5 seconds"), RpcRegistry.Messages.FindRouters.class); + probeReg1.reply(new RpcRegistry.Messages.FindRoutersReply( + Collections.>emptyList())); + + akka.actor.Status.Failure failure = expectMsgClass(duration("5 seconds"), + akka.actor.Status.Failure.class); + + assertEquals("failure.cause()", RpcErrorsException.class, failure.cause().getClass()); + }}; + } + + + /** + * This test method invokes and executes the remote rpc + */ + //@Test + public void testInvokeRpc() throws URISyntaxException { + new JavaTestKit(node1) {{ + QName instanceQName = new QName(new URI("ns"), "instance"); + + CompositeNode invokeRpcResult = makeRPCOutput("bar"); + RpcResult rpcResult = + RpcResultBuilder.success(invokeRpcResult).build(); + ArgumentCaptor inputCaptor = new ArgumentCaptor<>(); + when(brokerSession.rpc(eq(TEST_RPC), inputCaptor.capture())) + .thenReturn(Futures.immediateFuture(rpcResult)); + + // invoke rpc + CompositeNode input = makeRPCInput("foo"); + YangInstanceIdentifier instanceID = YangInstanceIdentifier.of(instanceQName); + InvokeRpc invokeMsg = new InvokeRpc(TEST_RPC, instanceID, input); + rpcBroker1.tell(invokeMsg, getRef()); + + FindRouters findRouters = probeReg1.expectMsgClass(RpcRegistry.Messages.FindRouters.class); + RouteIdentifier routeIdentifier = findRouters.getRouteIdentifier(); + assertEquals("getType", TEST_RPC, routeIdentifier.getType()); + assertEquals("getRoute", instanceID, routeIdentifier.getRoute()); + + probeReg1.reply(new RpcRegistry.Messages.FindRoutersReply( + Arrays.asList(new Pair(rpcBroker2, 200L)))); + + RpcResponse rpcResponse = expectMsgClass(duration("5 seconds"), RpcResponse.class); + assertCompositeNodeEquals((CompositeNode)invokeRpcResult.getValue().get(0), + XmlUtils.xmlToCompositeNode(rpcResponse.getResultCompositeNode())); + assertCompositeNodeEquals(input, inputCaptor.getValue()); + }}; + } + + @Test + public void testInvokeRpcWithNoOutput() { + new JavaTestKit(node1) {{ + + RpcResult rpcResult = RpcResultBuilder.success().build(); + when(brokerSession.rpc(eq(TEST_RPC), any(CompositeNode.class))) + .thenReturn(Futures.immediateFuture(rpcResult)); + + InvokeRpc invokeMsg = new InvokeRpc(TEST_RPC, null, makeRPCInput("foo")); + rpcBroker1.tell(invokeMsg, getRef()); + + probeReg1.expectMsgClass(RpcRegistry.Messages.FindRouters.class); + probeReg1.reply(new RpcRegistry.Messages.FindRoutersReply( + Arrays.asList(new Pair(rpcBroker2, 200L)))); + + RpcResponse rpcResponse = expectMsgClass(duration("5 seconds"), RpcResponse.class); + + assertEquals("getResultCompositeNode", "", rpcResponse.getResultCompositeNode()); + }}; + } + + @Test + public void testInvokeRpcWithExecuteFailure() { + new JavaTestKit(node1) {{ + + RpcResult rpcResult = RpcResultBuilder.failed() + .withError(ErrorType.RPC, "tag", "error", "appTag", "info", + new Exception("mock")) + .build(); + when(brokerSession.rpc(eq(TEST_RPC), any(CompositeNode.class))) + .thenReturn(Futures.immediateFuture(rpcResult)); + + InvokeRpc invokeMsg = new InvokeRpc(TEST_RPC, null, makeRPCInput("foo")); + rpcBroker1.tell(invokeMsg, getRef()); + + probeReg1.expectMsgClass(RpcRegistry.Messages.FindRouters.class); + probeReg1.reply(new RpcRegistry.Messages.FindRoutersReply( + Arrays.asList(new Pair(rpcBroker2, 200L)))); + + akka.actor.Status.Failure failure = expectMsgClass(duration("5 seconds"), + akka.actor.Status.Failure.class); + + assertEquals("failure.cause()", RpcErrorsException.class, failure.cause().getClass()); + + RpcErrorsException errorsEx = (RpcErrorsException)failure.cause(); + List rpcErrors = Lists.newArrayList(errorsEx.getRpcErrors()); + assertEquals("RpcErrors count", 1, rpcErrors.size()); + assertRpcErrorEquals(rpcErrors.get(0), ErrorSeverity.ERROR, ErrorType.RPC, "tag", + "error", "appTag", "info", "mock"); + }}; + } + + @Test + public void testInvokeRpcWithFindRoutersFailure() { + new JavaTestKit(node1) {{ + + InvokeRpc invokeMsg = new InvokeRpc(TEST_RPC, null, makeRPCInput("foo")); + rpcBroker1.tell(invokeMsg, getRef()); + + probeReg1.expectMsgClass(RpcRegistry.Messages.FindRouters.class); + probeReg1.reply(new akka.actor.Status.Failure(new TestException())); + + akka.actor.Status.Failure failure = expectMsgClass(duration("5 seconds"), + akka.actor.Status.Failure.class); + + assertEquals("failure.cause()", TestException.class, failure.cause().getClass()); + }}; + } + + @Test + public void testExecuteRpc() { + new JavaTestKit(node1) {{ + + String xml = "foo"; + + CompositeNode invokeRpcResult = makeRPCOutput("bar"); + RpcResult rpcResult = + RpcResultBuilder.success(invokeRpcResult).build(); + ArgumentCaptor inputCaptor = new ArgumentCaptor<>(); + when(brokerSession.rpc(eq(TEST_RPC), inputCaptor.capture())) + .thenReturn(Futures.immediateFuture(rpcResult)); + + ExecuteRpc executeMsg = new ExecuteRpc(xml, TEST_RPC); + + rpcBroker1.tell(executeMsg, getRef()); + + RpcResponse rpcResponse = expectMsgClass(duration("5 seconds"), RpcResponse.class); + + assertCompositeNodeEquals((CompositeNode)invokeRpcResult.getValue().get(0), + XmlUtils.xmlToCompositeNode(rpcResponse.getResultCompositeNode())); + }}; + } + + @Test + public void testExecuteRpcFailureWithRpcErrors() { + new JavaTestKit(node1) {{ + + String xml = "foo"; + + RpcResult rpcResult = RpcResultBuilder.failed() + .withError(ErrorType.RPC, "tag1", "error", "appTag1", "info1", + new Exception("mock")) + .withWarning(ErrorType.PROTOCOL, "tag2", "warning", "appTag2", "info2", null) + .build(); + when(brokerSession.rpc(eq(TEST_RPC), any(CompositeNode.class))) + .thenReturn(Futures.immediateFuture(rpcResult)); + + ExecuteRpc executeMsg = new ExecuteRpc(xml, TEST_RPC); + + rpcBroker1.tell(executeMsg, getRef()); + + akka.actor.Status.Failure failure = expectMsgClass(duration("5 seconds"), + akka.actor.Status.Failure.class); + + assertEquals("failure.cause()", RpcErrorsException.class, failure.cause().getClass()); + + RpcErrorsException errorsEx = (RpcErrorsException)failure.cause(); + List rpcErrors = Lists.newArrayList(errorsEx.getRpcErrors()); + assertEquals("RpcErrors count", 2, rpcErrors.size()); + assertRpcErrorEquals(rpcErrors.get(0), ErrorSeverity.ERROR, ErrorType.RPC, "tag1", + "error", "appTag1", "info1", "mock"); + assertRpcErrorEquals(rpcErrors.get(1), ErrorSeverity.WARNING, ErrorType.PROTOCOL, "tag2", + "warning", "appTag2", "info2", null); + }}; + } + + @Test + public void testExecuteRpcFailureWithNoRpcErrors() { + new JavaTestKit(node1) {{ + + String xml = "foo"; + + RpcResult rpcResult = RpcResultBuilder.failed().build(); + when(brokerSession.rpc(eq(TEST_RPC), any(CompositeNode.class))) + .thenReturn(Futures.immediateFuture(rpcResult)); + + ExecuteRpc executeMsg = new ExecuteRpc(xml, TEST_RPC); + + rpcBroker1.tell(executeMsg, getRef()); + + akka.actor.Status.Failure failure = expectMsgClass(duration("5 seconds"), + akka.actor.Status.Failure.class); + + assertEquals("failure.cause()", RpcErrorsException.class, failure.cause().getClass()); + + RpcErrorsException errorsEx = (RpcErrorsException)failure.cause(); + List rpcErrors = Lists.newArrayList(errorsEx.getRpcErrors()); + assertEquals("RpcErrors count", 1, rpcErrors.size()); + assertRpcErrorEquals(rpcErrors.get(0), ErrorSeverity.ERROR, ErrorType.RPC, + "operation-failed", "failed", null, null, null); + }}; + } + + @Test + public void testExecuteRpcFailureWithException() { + new JavaTestKit(node1) {{ + + String xml = "foo"; + + when(brokerSession.rpc(eq(TEST_RPC), any(CompositeNode.class))) + .thenReturn(Futures.>immediateFailedFuture( + new TestException())); + + ExecuteRpc executeMsg = new ExecuteRpc(xml, TEST_RPC); + + rpcBroker1.tell(executeMsg, getRef()); + + akka.actor.Status.Failure failure = expectMsgClass(duration("5 seconds"), + akka.actor.Status.Failure.class); -public class RpcBrokerTest { - - static ActorSystem node1; - static ActorSystem node2; - private ActorRef rpcBroker1; - private JavaTestKit probeReg1; - private ActorRef rpcBroker2; - private JavaTestKit probeReg2; - private Broker.ProviderSession brokerSession; - - - @BeforeClass - public static void setup() throws InterruptedException { - node1 = ActorSystem.create("opendaylight-rpc", ConfigFactory.load().getConfig("memberA")); - node2 = ActorSystem.create("opendaylight-rpc", ConfigFactory.load().getConfig("memberB")); - } - - @AfterClass - public static void teardown() { - JavaTestKit.shutdownActorSystem(node1); - JavaTestKit.shutdownActorSystem(node2); - node1 = null; - node2 = null; - } - - @Before - public void createActor() { - brokerSession = Mockito.mock(Broker.ProviderSession.class); - SchemaContext schemaContext = mock(SchemaContext.class); - probeReg1 = new JavaTestKit(node1); - rpcBroker1 = node1.actorOf(RpcBroker.props(brokerSession, probeReg1.getRef(), schemaContext)); - probeReg2 = new JavaTestKit(node2); - rpcBroker2 = node2.actorOf(RpcBroker.props(brokerSession, probeReg2.getRef(), schemaContext)); - - } - @Test - public void testInvokeRpcError() throws Exception { - new JavaTestKit(node1) {{ - QName rpc = new QName(new URI("noactor1"), "noactor1"); - CompositeNode input = new ImmutableCompositeNode(QName.create("ns", "2013-12-09", "no child"), new ArrayList>(), ModifyAction.REPLACE); - - - InvokeRpc invokeMsg = new InvokeRpc(rpc, null, input); - rpcBroker1.tell(invokeMsg, getRef()); - probeReg1.expectMsgClass(RpcRegistry.Messages.FindRouters.class); - probeReg1.reply(new RpcRegistry.Messages.FindRoutersReply(new ArrayList>())); - - Boolean getMsg = new ExpectMsg("ErrorResponse") { - protected Boolean match(Object in) { - if (in instanceof ErrorResponse) { - ErrorResponse reply = (ErrorResponse)in; - return reply.getException().getMessage().contains("No remote actor found for rpc execution of :"); - } else { - throw noMatch(); - } - } - }.get(); // this extracts the received message - - Assert.assertTrue(getMsg); - - }}; - } - - - /** - * This test method invokes and executes the remote rpc - */ - - @Test - public void testInvokeRpc() throws URISyntaxException { - new JavaTestKit(node1) {{ - QName rpc = new QName(new URI("noactor1"), "noactor1"); - // invoke rpc - CompositeNode input = new ImmutableCompositeNode(QName.create("ns", "2013-12-09", "child1"), new ArrayList>(), ModifyAction.REPLACE); - InvokeRpc invokeMsg = new InvokeRpc(rpc, null, input); - rpcBroker1.tell(invokeMsg, getRef()); - - probeReg1.expectMsgClass(RpcRegistry.Messages.FindRouters.class); - List> routerList = new ArrayList>(); - - routerList.add(new Pair(rpcBroker2, 200L)); - - probeReg1.reply(new RpcRegistry.Messages.FindRoutersReply(routerList)); - - CompositeNode invokeRpcResult = mock(CompositeNode.class); - Collection errors = new ArrayList<>(); - RpcResult result = Rpcs.getRpcResult(true, invokeRpcResult, errors); - Future> rpcResult = Futures.immediateFuture(result); - when(brokerSession.rpc(rpc, input)).thenReturn(rpcResult); - - //verify response msg - Boolean getMsg = new ExpectMsg("RpcResponse") { - protected Boolean match(Object in) { - if (in instanceof RpcResponse) { - return true; - } else { - throw noMatch(); - } - } - }.get(); // this extracts the received message - - Assert.assertTrue(getMsg); - }}; - } + assertEquals("failure.cause()", TestException.class, failure.cause().getClass()); + }}; + } } diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/RpcListenerTest.java b/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/RpcListenerTest.java index 7b5a968866..956e159990 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/RpcListenerTest.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/RpcListenerTest.java @@ -28,7 +28,7 @@ public class RpcListenerTest { @BeforeClass public static void setup() throws InterruptedException { - system = ActorSystem.create("opendaylight-rpc", ConfigFactory.load().getConfig("odl-cluster")); + system = ActorSystem.create("opendaylight-rpc", ConfigFactory.load().getConfig("odl-cluster-rpc")); } @AfterClass diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/registry/RpcRegistryTest.java b/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/registry/RpcRegistryTest.java index e6793741a3..83f52930b2 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/registry/RpcRegistryTest.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/registry/RpcRegistryTest.java @@ -10,10 +10,8 @@ import akka.actor.Props; import akka.testkit.JavaTestKit; import com.google.common.base.Predicate; import com.typesafe.config.ConfigFactory; - import org.junit.After; import org.junit.AfterClass; -import org.junit.Assert; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; @@ -22,8 +20,6 @@ import org.opendaylight.controller.remote.rpc.registry.gossip.Messages; import org.opendaylight.controller.sal.connector.api.RpcRouter; import org.opendaylight.controller.utils.ConditionalProbe; import org.opendaylight.yangtools.yang.common.QName; -import scala.concurrent.Await; -import scala.concurrent.Future; import scala.concurrent.duration.FiniteDuration; import javax.annotation.Nullable; @@ -33,9 +29,9 @@ import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; -import static org.opendaylight.controller.remote.rpc.registry.RpcRegistry.Messages.SetLocalRouter; import static org.opendaylight.controller.remote.rpc.registry.RpcRegistry.Messages.AddOrUpdateRoutes; import static org.opendaylight.controller.remote.rpc.registry.RpcRegistry.Messages.RemoveRoutes; +import static org.opendaylight.controller.remote.rpc.registry.RpcRegistry.Messages.SetLocalRouter; public class RpcRegistryTest { @@ -95,7 +91,6 @@ public class RpcRegistryTest { */ @Test public void testAddRemoveRpcOnSameNode() throws URISyntaxException, InterruptedException { - validateSystemStartup(); final JavaTestKit mockBroker = new JavaTestKit(node1); @@ -137,8 +132,6 @@ public class RpcRegistryTest { @Test public void testRpcAddRemoveInCluster() throws URISyntaxException, InterruptedException { - validateSystemStartup(); - final JavaTestKit mockBroker1 = new JavaTestKit(node1); //install probe on node2's bucket store @@ -146,7 +139,6 @@ public class RpcRegistryTest { final JavaTestKit probe2 = createProbeForMessage( node2, bucketStorePath, Messages.BucketStoreMessages.UpdateRemoteBuckets.class); - //Add rpc on node 1 registry1.tell(new SetLocalRouter(mockBroker1.getRef()), mockBroker1.getRef()); registry1.tell(getAddRouteMessage(), mockBroker1.getRef()); @@ -175,8 +167,6 @@ public class RpcRegistryTest { @Test public void testRpcAddedOnMultiNodes() throws Exception { - validateSystemStartup(); - final JavaTestKit mockBroker1 = new JavaTestKit(node1); final JavaTestKit mockBroker2 = new JavaTestKit(node2); final JavaTestKit mockBroker3 = new JavaTestKit(node3); @@ -225,49 +215,6 @@ public class RpcRegistryTest { } - private void validateSystemStartup() throws InterruptedException { - - ActorPath gossiper1Path = new ChildActorPath(new ChildActorPath(registry1.path(), "store"), "gossiper"); - ActorPath gossiper2Path = new ChildActorPath(new ChildActorPath(registry2.path(), "store"), "gossiper"); - ActorPath gossiper3Path = new ChildActorPath(new ChildActorPath(registry3.path(), "store"), "gossiper"); - - ActorSelection gossiper1 = node1.actorSelection(gossiper1Path); - ActorSelection gossiper2 = node2.actorSelection(gossiper2Path); - ActorSelection gossiper3 = node3.actorSelection(gossiper3Path); - - - if (!resolveReference(gossiper1, gossiper2, gossiper3)) - Assert.fail("Could not find gossipers"); - } - - private Boolean resolveReference(ActorSelection... gossipers) { - - Boolean resolved = true; - for (int i = 0; i < 5; i++) { - - resolved = true; - System.out.println(System.currentTimeMillis() + " Resolving gossipers; trial #" + i); - - for (ActorSelection gossiper : gossipers) { - ActorRef ref = null; - - try { - Future future = gossiper.resolveOne(new FiniteDuration(15000, TimeUnit.MILLISECONDS)); - ref = Await.result(future, new FiniteDuration(10000, TimeUnit.MILLISECONDS)); - } catch (Exception e) { - System.out.println("Could not find gossiper in attempt#" + i + ". Got exception " + e.getMessage()); - } - - if (ref == null) - resolved = false; - } - - if (resolved) break; - - } - return resolved; - } - private AddOrUpdateRoutes getAddRouteMessage() throws URISyntaxException { return new AddOrUpdateRoutes(createRouteIds()); } diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/utils/LatestEntryRoutingLogicTest.java b/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/utils/LatestEntryRoutingLogicTest.java index b21f0f0069..cd031a1d0a 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/utils/LatestEntryRoutingLogicTest.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/utils/LatestEntryRoutingLogicTest.java @@ -30,7 +30,7 @@ public class LatestEntryRoutingLogicTest { @BeforeClass public static void setup() throws InterruptedException { - system = ActorSystem.create("opendaylight-rpc", ConfigFactory.load().getConfig("odl-cluster")); + system = ActorSystem.create("opendaylight-rpc", ConfigFactory.load().getConfig("odl-cluster-rpc")); } @AfterClass diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/test/resources/application.conf b/opendaylight/md-sal/sal-remoterpc-connector/src/test/resources/application.conf index 8100ed35ab..5c4af8d3da 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/test/resources/application.conf +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/test/resources/application.conf @@ -1,6 +1,12 @@ -odl-cluster{ +odl-cluster-rpc{ + bounded-mailbox { + mailbox-type = "org.opendaylight.controller.common.actor.MeteredBoundedMailbox" + mailbox-capacity = 1000 + mailbox-push-timeout-time = 10ms + } + akka { - loglevel = "DEBUG" + loglevel = "INFO" #log-config-on-start = on actor { @@ -34,12 +40,22 @@ unit-test{ loglevel = "INFO" #loggers = ["akka.event.slf4j.Slf4jLogger"] } + bounded-mailbox { + mailbox-type = "org.opendaylight.controller.common.actor.MeteredBoundedMailbox" + mailbox-capacity = 1000 + mailbox-push-timeout-time = 10ms + } } memberA{ + bounded-mailbox { + mailbox-type = "org.opendaylight.controller.common.actor.MeteredBoundedMailbox" + mailbox-capacity = 1000 + mailbox-push-timeout-time = 10ms + } akka { loglevel = "INFO" - loggers = ["akka.event.slf4j.Slf4jLogger"] + #loggers = ["akka.event.slf4j.Slf4jLogger"] actor { provider = "akka.cluster.ClusterActorRefProvider" debug { @@ -65,11 +81,20 @@ memberA{ } } memberB{ + bounded-mailbox { + mailbox-type = "org.opendaylight.controller.common.actor.MeteredBoundedMailbox" + mailbox-capacity = 1000 + mailbox-push-timeout-time = 10ms + } akka { loglevel = "INFO" - loggers = ["akka.event.slf4j.Slf4jLogger"] + #loggers = ["akka.event.slf4j.Slf4jLogger"] + actor { provider = "akka.cluster.ClusterActorRefProvider" + debug { + #lifecycle = on + } } remote { log-received-messages = off @@ -90,11 +115,19 @@ memberB{ } } memberC{ + bounded-mailbox { + mailbox-type = "org.opendaylight.controller.common.actor.MeteredBoundedMailbox" + mailbox-capacity = 1000 + mailbox-push-timeout-time = 10ms + } akka { loglevel = "INFO" - loggers = ["akka.event.slf4j.Slf4jLogger"] + #loggers = ["akka.event.slf4j.Slf4jLogger"] actor { provider = "akka.cluster.ClusterActorRefProvider" + debug { + #lifecycle = on + } } remote { log-received-messages = off @@ -113,4 +146,5 @@ memberC{ auto-down-unreachable-after = 10s } } -} \ No newline at end of file +} + diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/test/resources/test-rpc.yang b/opendaylight/md-sal/sal-remoterpc-connector/src/test/resources/test-rpc.yang new file mode 100644 index 0000000000..3474e91834 --- /dev/null +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/test/resources/test-rpc.yang @@ -0,0 +1,24 @@ +module test-rpc-service { + yang-version 1; + namespace "urn:test"; + prefix "rpc"; + + revision "2014-08-28" { + description + "Initial revision"; + } + + rpc test-rpc { + input { + leaf input-data { + type string; + } + } + + output { + leaf output-data { + type string; + } + } + } +} \ No newline at end of file diff --git a/opendaylight/md-sal/sal-rest-connector/pom.xml b/opendaylight/md-sal/sal-rest-connector/pom.xml index e61cafa70b..fe5c9f39d8 100644 --- a/opendaylight/md-sal/sal-rest-connector/pom.xml +++ b/opendaylight/md-sal/sal-rest-connector/pom.xml @@ -87,6 +87,19 @@ jaxrs-api provided + + org.opendaylight.controller + sal-core-spi + + + org.opendaylight.yangtools + yang-data-composite-node + + + org.opendaylight.yangtools + yang-data-codec-gson + + ch.qos.logback logback-classic @@ -109,15 +122,6 @@ mockito-all test - - org.opendaylight.controller - sal-core-spi - - - org.opendaylight.yangtools - yang-data-composite-node - 0.6.2-SNAPSHOT - diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/config/yang/md/sal/rest/connector/RestConnectorModule.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/config/yang/md/sal/rest/connector/RestConnectorModule.java index 52115a8f32..fe20e3a441 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/config/yang/md/sal/rest/connector/RestConnectorModule.java +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/config/yang/md/sal/rest/connector/RestConnectorModule.java @@ -1,10 +1,12 @@ package org.opendaylight.controller.config.yang.md.sal.rest.connector; -import org.opendaylight.controller.sal.rest.impl.RestconfProviderImpl; +import org.opendaylight.controller.sal.restconf.impl.RestconfProviderImpl; public class RestConnectorModule extends org.opendaylight.controller.config.yang.md.sal.rest.connector.AbstractRestConnectorModule { + private static RestConnectorRuntimeRegistration runtimeRegistration; + public RestConnectorModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) { super(identifier, dependencyResolver); } @@ -26,6 +28,14 @@ public class RestConnectorModule extends org.opendaylight.controller.config.yang instance.setWebsocketPort(getWebsocketPort()); // Register it with the Broker getDomBrokerDependency().registerProvider(instance); + + if(runtimeRegistration != null){ + runtimeRegistration.close(); + } + + runtimeRegistration = + getRootRuntimeBeanRegistratorWrapper().register(instance); + return instance; } } diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/api/RestconfConstants.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/api/RestconfConstants.java new file mode 100644 index 0000000000..1ec1c29eab --- /dev/null +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/api/RestconfConstants.java @@ -0,0 +1,7 @@ +package org.opendaylight.controller.sal.rest.api; + +public interface RestconfConstants { + + + public static String IDENTIFIER = "identifier"; +} diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/api/RestconfService.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/api/RestconfService.java index 9c149a21e6..4a46a3c267 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/api/RestconfService.java +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/api/RestconfService.java @@ -21,10 +21,12 @@ import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.ws.rs.core.UriInfo; +import org.opendaylight.controller.sal.restconf.impl.NormalizedNodeContext; import org.opendaylight.controller.sal.restconf.impl.StructuredData; import org.opendaylight.yangtools.yang.data.api.CompositeNode; import org.opendaylight.yangtools.yang.data.api.Node; + /** * The URI hierarchy for the RESTCONF resources consists of an entry point container, 4 top-level resources, and 1 * field. @@ -109,14 +111,14 @@ public interface RestconfService { @Path("/config/{identifier:.+}") @Produces({ Draft02.MediaTypes.DATA + JSON, Draft02.MediaTypes.DATA + XML, MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_XML }) - public StructuredData readConfigurationData(@Encoded @PathParam("identifier") String identifier, + public NormalizedNodeContext readConfigurationData(@Encoded @PathParam("identifier") String identifier, @Context UriInfo uriInfo); @GET @Path("/operational/{identifier:.+}") @Produces({ Draft02.MediaTypes.DATA + JSON, Draft02.MediaTypes.DATA + XML, MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_XML }) - public StructuredData readOperationalData(@Encoded @PathParam("identifier") String identifier, + public NormalizedNodeContext readOperationalData(@Encoded @PathParam("identifier") String identifier, @Context UriInfo uriInfo); @PUT diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/AbstractIdentifierAwareJaxRsProvider.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/AbstractIdentifierAwareJaxRsProvider.java new file mode 100644 index 0000000000..978ae0d9c5 --- /dev/null +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/AbstractIdentifierAwareJaxRsProvider.java @@ -0,0 +1,30 @@ +package org.opendaylight.controller.sal.rest.impl; + +import com.google.common.base.Optional; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.UriInfo; +import org.opendaylight.controller.sal.rest.api.RestconfConstants; +import org.opendaylight.controller.sal.restconf.impl.ControllerContext; +import org.opendaylight.controller.sal.restconf.impl.InstanceIdentifierContext; + +public class AbstractIdentifierAwareJaxRsProvider { + + @Context + private UriInfo uriInfo; + + protected final String getIdentifier() { + return uriInfo.getPathParameters().getFirst(RestconfConstants.IDENTIFIER); + } + + protected final Optional getIdentifierWithSchema() { + return Optional.of(getInstanceIdentifierContext()); + } + + protected InstanceIdentifierContext getInstanceIdentifierContext() { + return ControllerContext.getInstance().toInstanceIdentifier(getIdentifier()); + } + + protected UriInfo getUriInfo() { + return uriInfo; + } +} diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/JsonNormalizedNodeBodyReader.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/JsonNormalizedNodeBodyReader.java new file mode 100644 index 0000000000..dc989d2786 --- /dev/null +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/JsonNormalizedNodeBodyReader.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.sal.rest.impl; + +import com.google.common.base.Optional; +import com.google.gson.stream.JsonReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; +import javax.ws.rs.Consumes; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.ext.MessageBodyReader; +import javax.ws.rs.ext.Provider; +import org.opendaylight.controller.sal.rest.api.Draft02; +import org.opendaylight.controller.sal.rest.api.RestconfService; +import org.opendaylight.controller.sal.restconf.impl.InstanceIdentifierContext; +import org.opendaylight.controller.sal.restconf.impl.NormalizedNodeContext; +import org.opendaylight.controller.sal.restconf.impl.RestconfDocumentedException; +import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorTag; +import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorType; +import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter; +import org.opendaylight.yangtools.yang.data.codec.gson.JsonParserStream; +import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter; +import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@Provider +@Consumes({ Draft02.MediaTypes.DATA + RestconfService.JSON, Draft02.MediaTypes.OPERATION + RestconfService.JSON, + MediaType.APPLICATION_JSON }) +public class JsonNormalizedNodeBodyReader extends AbstractIdentifierAwareJaxRsProvider implements MessageBodyReader { + + private final static Logger LOG = LoggerFactory.getLogger(JsonNormalizedNodeBodyReader.class); + + @Override + public boolean isReadable(final Class type, final Type genericType, final Annotation[] annotations, + final MediaType mediaType) { + return true; + } + + @Override + public NormalizedNodeContext readFrom(final Class type, final Type genericType, + final Annotation[] annotations, final MediaType mediaType, + final MultivaluedMap httpHeaders, final InputStream entityStream) throws IOException, + WebApplicationException { + try { + Optional path = getIdentifierWithSchema(); + NormalizedNodeResult resultHolder = new NormalizedNodeResult(); + NormalizedNodeStreamWriter writer = ImmutableNormalizedNodeStreamWriter.from(resultHolder); + JsonParserStream jsonParser = JsonParserStream.create(writer, path.get().getSchemaContext()); + JsonReader reader = new JsonReader(new InputStreamReader(entityStream)); + jsonParser.parse(reader); + return new NormalizedNodeContext(path.get(),resultHolder.getResult()); + } catch (Exception e) { + LOG.debug("Error parsing json input", e); + + throw new RestconfDocumentedException("Error parsing input: " + e.getMessage(), ErrorType.PROTOCOL, + ErrorTag.MALFORMED_MESSAGE); + } + } +} + diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/NormalizedNodeJsonBodyWriter.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/NormalizedNodeJsonBodyWriter.java new file mode 100644 index 0000000000..cfb5e5d7c2 --- /dev/null +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/NormalizedNodeJsonBodyWriter.java @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.sal.rest.impl; + +import com.google.common.base.Charsets; +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; +import java.net.URI; +import java.util.Iterator; +import javax.ws.rs.Produces; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.core.Response; +import javax.ws.rs.ext.MessageBodyWriter; +import javax.ws.rs.ext.Provider; +import org.opendaylight.controller.sal.rest.api.Draft02; +import org.opendaylight.controller.sal.rest.api.RestconfService; +import org.opendaylight.controller.sal.restconf.impl.InstanceIdentifierContext; +import org.opendaylight.controller.sal.restconf.impl.NormalizedNodeContext; +import org.opendaylight.controller.sal.restconf.impl.RestconfDocumentedException; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; +import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; +import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild; +import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter; +import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter; +import org.opendaylight.yangtools.yang.data.codec.gson.JSONNormalizedNodeStreamWriter; +import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes; +import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.opendaylight.yangtools.yang.model.api.SchemaPath; + +@Provider +@Produces({ Draft02.MediaTypes.API + RestconfService.JSON, Draft02.MediaTypes.DATA + RestconfService.JSON, + Draft02.MediaTypes.OPERATION + RestconfService.JSON, MediaType.APPLICATION_JSON }) +public class NormalizedNodeJsonBodyWriter implements MessageBodyWriter { + + @Override + public boolean isWriteable(final Class type, final Type genericType, final Annotation[] annotations, final MediaType mediaType) { + return type.equals(NormalizedNodeContext.class); + } + + @Override + public long getSize(final NormalizedNodeContext t, final Class type, final Type genericType, final Annotation[] annotations, final MediaType mediaType) { + return -1; + } + + @Override + public void writeTo(final NormalizedNodeContext t, final Class type, final Type genericType, final Annotation[] annotations, + final MediaType mediaType, final MultivaluedMap httpHeaders, final OutputStream entityStream) + throws IOException, WebApplicationException { + NormalizedNode data = t.getData(); + InstanceIdentifierContext context = t.getInstanceIdentifierContext(); + DataSchemaNode schema = context.getSchemaNode(); + SchemaPath path = context.getSchemaNode().getPath(); + OutputStreamWriter outputWriter = new OutputStreamWriter(entityStream, Charsets.UTF_8); + if (data == null) { + throw new RestconfDocumentedException(Response.Status.NOT_FOUND); + } + + boolean isDataRoot = false; + URI initialNs = null; + outputWriter.write('{'); + if (SchemaPath.ROOT.equals(path)) { + isDataRoot = true; + } else { + path = path.getParent(); + // FIXME: Add proper handling of reading root. + } + if(!schema.isAugmenting() && !(schema instanceof SchemaContext)) { + initialNs = schema.getQName().getNamespace(); + } + NormalizedNodeStreamWriter jsonWriter = JSONNormalizedNodeStreamWriter.create(context.getSchemaContext(),path,initialNs,outputWriter); + NormalizedNodeWriter nnWriter = NormalizedNodeWriter.forStreamWriter(jsonWriter); + if(isDataRoot) { + writeDataRoot(outputWriter,nnWriter,(ContainerNode) data); + } else { + if(data instanceof MapEntryNode) { + data = ImmutableNodes.mapNodeBuilder(data.getNodeType()).withChild(((MapEntryNode) data)).build(); + } + nnWriter.write(data); + } + nnWriter.flush(); + outputWriter.write('}'); + outputWriter.flush(); + } + + private void writeDataRoot(OutputStreamWriter outputWriter, NormalizedNodeWriter nnWriter, ContainerNode data) throws IOException { + Iterator> iterator = data.getValue().iterator(); + while(iterator.hasNext()) { + DataContainerChild child = iterator.next(); + nnWriter.write(child); + nnWriter.flush(); + if(iterator.hasNext()) { + outputWriter.write(","); + } + } + } + +} diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/NormalizedNodeXmlBodyWriter.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/NormalizedNodeXmlBodyWriter.java new file mode 100644 index 0000000000..3a6de300a0 --- /dev/null +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/NormalizedNodeXmlBodyWriter.java @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.sal.rest.impl; + +import com.google.common.base.Throwables; +import java.io.IOException; +import java.io.OutputStream; +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; +import javax.ws.rs.Produces; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.core.Response; +import javax.ws.rs.ext.MessageBodyWriter; +import javax.ws.rs.ext.Provider; +import javax.xml.stream.FactoryConfigurationError; +import javax.xml.stream.XMLOutputFactory; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamWriter; +import org.opendaylight.controller.sal.rest.api.Draft02; +import org.opendaylight.controller.sal.rest.api.RestconfService; +import org.opendaylight.controller.sal.restconf.impl.InstanceIdentifierContext; +import org.opendaylight.controller.sal.restconf.impl.NormalizedNodeContext; +import org.opendaylight.controller.sal.restconf.impl.RestconfDocumentedException; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; +import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; +import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild; +import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter; +import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter; +import org.opendaylight.yangtools.yang.data.impl.codec.xml.XMLStreamNormalizedNodeStreamWriter; +import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.opendaylight.yangtools.yang.model.api.SchemaPath; + +@Provider +@Produces({ Draft02.MediaTypes.API + RestconfService.XML, Draft02.MediaTypes.DATA + RestconfService.XML, + Draft02.MediaTypes.OPERATION + RestconfService.XML, MediaType.APPLICATION_XML, MediaType.TEXT_XML }) +public class NormalizedNodeXmlBodyWriter implements MessageBodyWriter { + + private static final XMLOutputFactory XML_FACTORY; + + static { + XML_FACTORY = XMLOutputFactory.newFactory(); + XML_FACTORY.setProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES, true); + } + + @Override + public boolean isWriteable(final Class type, final Type genericType, final Annotation[] annotations, + final MediaType mediaType) { + return type.equals(NormalizedNodeContext.class); + } + + @Override + public long getSize(final NormalizedNodeContext t, final Class type, final Type genericType, + final Annotation[] annotations, final MediaType mediaType) { + return -1; + } + + @Override + public void writeTo(final NormalizedNodeContext t, final Class type, final Type genericType, + final Annotation[] annotations, final MediaType mediaType, + final MultivaluedMap httpHeaders, final OutputStream entityStream) throws IOException, + WebApplicationException { + InstanceIdentifierContext pathContext = t.getInstanceIdentifierContext(); + if (t.getData() == null) { + throw new RestconfDocumentedException(Response.Status.NOT_FOUND); + } + + XMLStreamWriter xmlWriter; + try { + xmlWriter = XML_FACTORY.createXMLStreamWriter(entityStream); + } catch (XMLStreamException e) { + throw new IllegalStateException(e); + } catch (FactoryConfigurationError e) { + throw new IllegalStateException(e); + } + NormalizedNode data = t.getData(); + SchemaPath schemaPath = pathContext.getSchemaNode().getPath(); + + boolean isDataRoot = false; + if (SchemaPath.ROOT.equals(schemaPath)) { + isDataRoot = true; + } else { + schemaPath = schemaPath.getParent(); + } + + NormalizedNodeStreamWriter jsonWriter = XMLStreamNormalizedNodeStreamWriter.create(xmlWriter, + pathContext.getSchemaContext(), schemaPath); + NormalizedNodeWriter nnWriter = NormalizedNodeWriter.forStreamWriter(jsonWriter); + if (isDataRoot) { + writeRootElement(xmlWriter, nnWriter, (ContainerNode) data); + } else { + if (data instanceof MapEntryNode) { + // Restconf allows returning one list item. We need to wrap it + // in map node in order to serialize it properly + data = ImmutableNodes.mapNodeBuilder(data.getNodeType()).addChild((MapEntryNode) data).build(); + } + nnWriter.write(data); + nnWriter.flush(); + } + } + + private void writeRootElement(XMLStreamWriter xmlWriter, NormalizedNodeWriter nnWriter, ContainerNode data) + throws IOException { + try { + QName name = SchemaContext.NAME; + xmlWriter.writeStartElement(name.getNamespace().toString(), name.getLocalName()); + for (DataContainerChild child : data.getValue()) { + nnWriter.write(child); + } + nnWriter.flush(); + xmlWriter.writeEndElement(); + xmlWriter.flush(); + } catch (XMLStreamException e) { + Throwables.propagate(e); + } + } +} diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/RestconfApplication.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/RestconfApplication.java index b4b2a1f9ef..c9496af4c8 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/RestconfApplication.java +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/RestconfApplication.java @@ -14,12 +14,19 @@ import javax.ws.rs.core.Application; import org.opendaylight.controller.sal.restconf.impl.BrokerFacade; import org.opendaylight.controller.sal.restconf.impl.ControllerContext; import org.opendaylight.controller.sal.restconf.impl.RestconfImpl; +import org.opendaylight.controller.sal.restconf.impl.StatisticsRestconfServiceWrapper; public class RestconfApplication extends Application { @Override public Set> getClasses() { - return ImmutableSet.> of(RestconfDocumentedExceptionMapper.class); + return ImmutableSet.> builder() + .add(RestconfDocumentedExceptionMapper.class) + .add(XmlNormalizedNodeBodyReader.class) + .add(JsonNormalizedNodeBodyReader.class) + .add(NormalizedNodeJsonBodyWriter.class) + .add(NormalizedNodeXmlBodyWriter.class) + .build(); } @Override @@ -32,11 +39,11 @@ public class RestconfApplication extends Application { restconfImpl.setControllerContext(controllerContext); singletons.add(controllerContext); singletons.add(brokerFacade); - singletons.add(restconfImpl); - singletons.add(XmlToCompositeNodeProvider.INSTANCE); + singletons.add(StatisticsRestconfServiceWrapper.getInstance()); singletons.add(StructuredDataToXmlProvider.INSTANCE); - singletons.add(JsonToCompositeNodeProvider.INSTANCE); singletons.add(StructuredDataToJsonProvider.INSTANCE); + singletons.add(JsonToCompositeNodeProvider.INSTANCE); + singletons.add(XmlToCompositeNodeProvider.INSTANCE); return singletons; } diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/StructuredDataToJsonProvider.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/StructuredDataToJsonProvider.java index 933ed0f849..063d2f51af 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/StructuredDataToJsonProvider.java +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/StructuredDataToJsonProvider.java @@ -30,27 +30,24 @@ import org.opendaylight.yangtools.yang.model.api.DataNodeContainer; @Provider @Produces({ Draft02.MediaTypes.API + RestconfService.JSON, Draft02.MediaTypes.DATA + RestconfService.JSON, - Draft02.MediaTypes.OPERATION + RestconfService.JSON, MediaType.APPLICATION_JSON }) + Draft02.MediaTypes.OPERATION + RestconfService.JSON, MediaType.APPLICATION_JSON }) public enum StructuredDataToJsonProvider implements MessageBodyWriter { INSTANCE; @Override - public boolean isWriteable(final Class type, final Type genericType, final Annotation[] annotations, - final MediaType mediaType) { + public boolean isWriteable(final Class type, final Type genericType, final Annotation[] annotations, final MediaType mediaType) { return type.equals(StructuredData.class); } @Override - public long getSize(final StructuredData t, final Class type, final Type genericType, - final Annotation[] annotations, final MediaType mediaType) { + public long getSize(final StructuredData t, final Class type, final Type genericType, final Annotation[] annotations, final MediaType mediaType) { return -1; } @Override - public void writeTo(final StructuredData t, final Class type, final Type genericType, - final Annotation[] annotations, final MediaType mediaType, - final MultivaluedMap httpHeaders, final OutputStream entityStream) throws IOException, - WebApplicationException { + public void writeTo(final StructuredData t, final Class type, final Type genericType, final Annotation[] annotations, + final MediaType mediaType, final MultivaluedMap httpHeaders, final OutputStream entityStream) + throws IOException, WebApplicationException { CompositeNode data = t.getData(); if (data == null) { throw new RestconfDocumentedException(Response.Status.NOT_FOUND); diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/XmlNormalizedNodeBodyReader.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/XmlNormalizedNodeBodyReader.java new file mode 100644 index 0000000000..062a4488f3 --- /dev/null +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/XmlNormalizedNodeBodyReader.java @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.sal.rest.impl; + +import com.google.common.base.Optional; +import java.io.IOException; +import java.io.InputStream; +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; +import java.util.Collections; +import java.util.List; +import javax.ws.rs.Consumes; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.ext.MessageBodyReader; +import javax.ws.rs.ext.Provider; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import org.opendaylight.controller.sal.rest.api.Draft02; +import org.opendaylight.controller.sal.rest.api.RestconfService; +import org.opendaylight.controller.sal.restconf.impl.InstanceIdentifierContext; +import org.opendaylight.controller.sal.restconf.impl.NormalizedNodeContext; +import org.opendaylight.controller.sal.restconf.impl.RestconfDocumentedException; +import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorTag; +import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorType; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.impl.codec.xml.XmlUtils; +import org.opendaylight.yangtools.yang.data.impl.schema.transform.dom.parser.DomToNormalizedNodeParserFactory; +import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode; +import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; +import org.opendaylight.yangtools.yang.model.api.ListSchemaNode; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +@Provider +@Consumes({ Draft02.MediaTypes.DATA + RestconfService.XML, Draft02.MediaTypes.OPERATION + RestconfService.XML, + MediaType.APPLICATION_XML, MediaType.TEXT_XML }) +public class XmlNormalizedNodeBodyReader extends AbstractIdentifierAwareJaxRsProvider implements MessageBodyReader { + + private final static Logger LOG = LoggerFactory.getLogger(XmlNormalizedNodeBodyReader.class); + private final static DomToNormalizedNodeParserFactory DOM_PARSER_FACTORY = DomToNormalizedNodeParserFactory.getInstance(XmlUtils.DEFAULT_XML_CODEC_PROVIDER); + private static final DocumentBuilderFactory BUILDERFACTORY; + + static { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + factory.setNamespaceAware(true); + factory.setCoalescing(true); + factory.setIgnoringElementContentWhitespace(true); + factory.setIgnoringComments(true); + BUILDERFACTORY = factory; + } + + @Override + public boolean isReadable(final Class type, final Type genericType, final Annotation[] annotations, + final MediaType mediaType) { + return true; + } + + @Override + public NormalizedNodeContext readFrom(final Class type, final Type genericType, + final Annotation[] annotations, final MediaType mediaType, + final MultivaluedMap httpHeaders, final InputStream entityStream) throws IOException, + WebApplicationException { + try { + Optional path = getIdentifierWithSchema(); + + final DocumentBuilder dBuilder; + try { + dBuilder = BUILDERFACTORY.newDocumentBuilder(); + } catch (ParserConfigurationException e) { + throw new RuntimeException("Failed to parse XML document", e); + } + Document doc = dBuilder.parse(entityStream); + + NormalizedNode result = parse(path.get(),doc); + return new NormalizedNodeContext(path.get(),result); + } catch (Exception e) { + LOG.debug("Error parsing json input", e); + + throw new RestconfDocumentedException("Error parsing input: " + e.getMessage(), ErrorType.PROTOCOL, + ErrorTag.MALFORMED_MESSAGE); + } + } + + private static NormalizedNode parse(InstanceIdentifierContext pathContext,Document doc) { + List elements = Collections.singletonList(doc.getDocumentElement()); + DataSchemaNode schemaNode = pathContext.getSchemaNode(); + if(schemaNode instanceof ContainerSchemaNode) { + return DOM_PARSER_FACTORY.getContainerNodeParser().parse(Collections.singletonList(doc.getDocumentElement()), (ContainerSchemaNode) schemaNode); + } else if(schemaNode instanceof ListSchemaNode) { + ListSchemaNode casted = (ListSchemaNode) schemaNode; + return DOM_PARSER_FACTORY.getMapEntryNodeParser().parse(elements, casted); + } + return null; + } +} + diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/XmlToCompositeNodeReader.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/XmlToCompositeNodeReader.java index 5944d6003e..6b9da80c68 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/XmlToCompositeNodeReader.java +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/XmlToCompositeNodeReader.java @@ -28,6 +28,7 @@ import org.opendaylight.controller.sal.restconf.impl.NodeWrapper; import org.opendaylight.controller.sal.restconf.impl.SimpleNodeWrapper; import org.opendaylight.yangtools.yang.data.api.Node; +@Deprecated public class XmlToCompositeNodeReader { private final static XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance(); @@ -45,7 +46,6 @@ public class XmlToCompositeNodeReader { } eventReader = xmlInputFactory.createXMLEventReader(entityStream); - if (eventReader.hasNext()) { XMLEvent element = eventReader.peek(); if (element.isStartDocument()) { @@ -110,7 +110,7 @@ public class XmlToCompositeNodeReader { return entityStream; } - private boolean isInputStreamEmpty(InputStream entityStream) throws IOException { + private boolean isInputStreamEmpty(final InputStream entityStream) throws IOException { boolean isEmpty = false; entityStream.mark(1); if (entityStream.read() == -1) { @@ -180,7 +180,7 @@ public class XmlToCompositeNodeReader { resolveValueOfElement(data, startElement)); } - private String getValueOf(StartElement startElement) throws XMLStreamException { + private String getValueOf(final StartElement startElement) throws XMLStreamException { String data = null; if (eventReader.hasNext()) { final XMLEvent innerEvent = eventReader.peek(); @@ -201,7 +201,7 @@ public class XmlToCompositeNodeReader { return data == null ? null : data.trim(); } - private String getAdditionalData(XMLEvent event) throws XMLStreamException { + private String getAdditionalData(final XMLEvent event) throws XMLStreamException { String data = ""; if (eventReader.hasNext()) { final XMLEvent innerEvent = eventReader.peek(); @@ -216,16 +216,16 @@ public class XmlToCompositeNodeReader { return data; } - private String getLocalNameFor(StartElement startElement) { + private String getLocalNameFor(final StartElement startElement) { return startElement.getName().getLocalPart(); } - private URI getNamespaceFor(StartElement startElement) { + private URI getNamespaceFor(final StartElement startElement) { String namespaceURI = startElement.getName().getNamespaceURI(); return namespaceURI.isEmpty() ? null : URI.create(namespaceURI); } - private Object resolveValueOfElement(String value, StartElement startElement) { + private Object resolveValueOfElement(final String value, final StartElement startElement) { // it could be instance-identifier Built-In Type if (value.startsWith("/")) { IdentityValuesDTO iiValue = RestUtil.asInstanceIdentifier(value, new RestUtil.PrefixMapingFromXml( diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/XmlToNormalizedNodeReaderWithSchema.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/XmlToNormalizedNodeReaderWithSchema.java deleted file mode 100644 index 935d96cb12..0000000000 --- a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/XmlToNormalizedNodeReaderWithSchema.java +++ /dev/null @@ -1,352 +0,0 @@ -/* - * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v1.0 which accompanies this distribution, - * and is available at http://www.eclipse.org/legal/epl-v10.html - */ -package org.opendaylight.controller.sal.rest.impl; - -import static com.google.common.base.Preconditions.checkArgument; - -import java.io.BufferedInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.net.URI; -import java.util.HashMap; -import java.util.Map; -import java.util.Stack; -import javax.xml.stream.XMLEventReader; -import javax.xml.stream.XMLInputFactory; -import javax.xml.stream.XMLStreamConstants; -import javax.xml.stream.XMLStreamException; -import javax.xml.stream.events.Characters; -import javax.xml.stream.events.StartElement; -import javax.xml.stream.events.XMLEvent; -import org.opendaylight.controller.sal.restconf.impl.CompositeNodeWrapper; -import org.opendaylight.controller.sal.restconf.impl.IdentityValuesDTO; -import org.opendaylight.controller.sal.restconf.impl.InstanceIdWithSchemaNode; -import org.opendaylight.controller.sal.restconf.impl.NodeWrapper; -import org.opendaylight.controller.sal.restconf.impl.RestCodec; -import org.opendaylight.controller.sal.restconf.impl.RestconfDocumentedException; -import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorTag; -import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorType; -import org.opendaylight.controller.sal.restconf.impl.SimpleNodeWrapper; -import org.opendaylight.yangtools.yang.common.QName; -import org.opendaylight.yangtools.yang.data.api.Node; -import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode; -import org.opendaylight.yangtools.yang.model.api.DataNodeContainer; -import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; -import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode; -import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode; -import org.opendaylight.yangtools.yang.model.api.ListSchemaNode; -import org.opendaylight.yangtools.yang.model.api.TypeDefinition; - -public class XmlToNormalizedNodeReaderWithSchema { - - private final static XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance(); - private XMLEventReader eventReader; - private InstanceIdWithSchemaNode iiWithSchema; - - public XmlToNormalizedNodeReaderWithSchema(final InstanceIdWithSchemaNode iiWithSchema) { - this.iiWithSchema = iiWithSchema; - } - - public Node read(InputStream entityStream) throws XMLStreamException, UnsupportedFormatException, IOException { - // Get an XML stream which can be marked, and reset, so we can check and see if there is - // any content being provided. - entityStream = getMarkableStream(entityStream); - - if (isInputStreamEmpty(entityStream)) { - return null; - } - - eventReader = xmlInputFactory.createXMLEventReader(entityStream); - if (eventReader.hasNext()) { - XMLEvent element = eventReader.peek(); - if (element.isStartDocument()) { - eventReader.nextEvent(); - } - } - - final Stack> processingQueue = new Stack<>(); - NodeWrapper root = null; - NodeWrapper element = null; - Stack processingQueueSchema = new Stack<>(); - - while (eventReader.hasNext()) { - final XMLEvent event = eventReader.nextEvent(); - - if (event.isStartElement()) { - final StartElement startElement = event.asStartElement(); - CompositeNodeWrapper compParentNode = null; - if (!processingQueue.isEmpty() && processingQueue.peek() instanceof CompositeNodeWrapper) { - compParentNode = (CompositeNodeWrapper) processingQueue.peek(); - findSchemaNodeForElement(startElement, processingQueueSchema); - } else { - processingQueueSchema = checkElementAndSchemaNodeNameAndNamespace(startElement, - iiWithSchema.getSchemaNode()); - DataSchemaNode currentSchemaNode = processingQueueSchema.peek(); - if (!(currentSchemaNode instanceof ListSchemaNode) - && !(currentSchemaNode instanceof ContainerSchemaNode)) { - throw new UnsupportedFormatException( - "Top level element has to be of type list or container schema node."); - } - } - - NodeWrapper newNode = null; - if (isCompositeNodeEvent(event)) { - newNode = resolveCompositeNodeFromStartElement(processingQueueSchema.peek().getQName()); - if (root == null) { - root = newNode; - } - } else if (isSimpleNodeEvent(event)) { - newNode = resolveSimpleNodeFromStartElement(processingQueueSchema.peek(), getValueOf(startElement)); - if (root == null) { - root = newNode; - } - } - - if (newNode != null) { - processingQueue.push(newNode); - if (compParentNode != null) { - compParentNode.addValue(newNode); - } - } - } else if (event.isEndElement()) { - element = processingQueue.pop(); -// if(((EndElement)event).getName().getLocalPart().equals - processingQueueSchema.pop(); - } - } - - if (!root.getLocalName().equals(element.getLocalName())) { - throw new UnsupportedFormatException("XML should contain only one root element"); - } - - return root.unwrap(); - } - - private void findSchemaNodeForElement(StartElement element, Stack processingQueueSchema) { - DataSchemaNode currentSchemaNode = processingQueueSchema.peek(); - if (currentSchemaNode instanceof DataNodeContainer) { - final URI realNamespace = getNamespaceFor(element); - final String realName = getLocalNameFor(element); - Map childNamesakes = resolveChildsWithNameAsElement( - ((DataNodeContainer) currentSchemaNode), realName); - DataSchemaNode childDataSchemaNode = childNamesakes.get(realNamespace); - if (childDataSchemaNode == null) { - throw new RestconfDocumentedException("Element " + realName + " has namespace " + realNamespace - + ". Available namespaces are: " + childNamesakes.keySet(), ErrorType.APPLICATION, - ErrorTag.INVALID_VALUE); - } - processingQueueSchema.push(childDataSchemaNode); - } else { - throw new RestconfDocumentedException("Element " + processingQueueSchema.peek().getQName().getLocalName() - + " should be data node container .", ErrorType.APPLICATION, ErrorTag.INVALID_VALUE); - } - - } - - /** - * Returns map of data schema node which are accesible by URI which have equal name - */ - private Map resolveChildsWithNameAsElement(final DataNodeContainer dataNodeContainer, - final String realName) { - final Map namespaceToDataSchemaNode = new HashMap(); - for (DataSchemaNode dataSchemaNode : dataNodeContainer.getChildNodes()) { - if (dataSchemaNode.equals(realName)) { - namespaceToDataSchemaNode.put(dataSchemaNode.getQName().getNamespace(), dataSchemaNode); - } - } - return namespaceToDataSchemaNode; - } - - private final Stack checkElementAndSchemaNodeNameAndNamespace(final StartElement startElement, - final DataSchemaNode node) { - checkArgument(startElement != null, "Start Element cannot be NULL!"); - final String expectedName = node.getQName().getLocalName(); - final String xmlName = getLocalNameFor(startElement); - final URI expectedNamespace = node.getQName().getNamespace(); - final URI xmlNamespace = getNamespaceFor(startElement); - if (!expectedName.equals(xmlName)) { - throw new RestconfDocumentedException("Xml element name: " + xmlName + "\nSchema node name: " - + expectedName, org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorType.APPLICATION, - ErrorTag.INVALID_VALUE); - } - - if (xmlNamespace != null && !expectedNamespace.equals(xmlNamespace)) { - throw new RestconfDocumentedException("Xml element ns: " + xmlNamespace + "\nSchema node ns: " - + expectedNamespace, - org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorType.APPLICATION, - ErrorTag.INVALID_VALUE); - } - Stack processingQueueSchema = new Stack<>(); - processingQueueSchema.push(node); - return processingQueueSchema; - } - - /** - * If the input stream is not markable, then it wraps the input stream with a buffered stream, which is mark able. - * That way we can check if the stream is empty safely. - * - * @param entityStream - * @return - */ - private InputStream getMarkableStream(InputStream entityStream) { - if (!entityStream.markSupported()) { - entityStream = new BufferedInputStream(entityStream); - } - return entityStream; - } - - private boolean isInputStreamEmpty(final InputStream entityStream) throws IOException { - boolean isEmpty = false; - entityStream.mark(1); - if (entityStream.read() == -1) { - isEmpty = true; - } - entityStream.reset(); - return isEmpty; - } - - private boolean isSimpleNodeEvent(final XMLEvent event) throws XMLStreamException { - checkArgument(event != null, "XML Event cannot be NULL!"); - if (event.isStartElement()) { - XMLEvent innerEvent = skipCommentsAndWhitespace(); - if (innerEvent != null && (innerEvent.isCharacters() || innerEvent.isEndElement())) { - return true; - } - } - return false; - } - - private boolean isCompositeNodeEvent(final XMLEvent event) throws XMLStreamException { - checkArgument(event != null, "XML Event cannot be NULL!"); - if (event.isStartElement()) { - XMLEvent innerEvent = skipCommentsAndWhitespace(); - if (innerEvent != null) { - if (innerEvent.isStartElement()) { - return true; - } - } - } - return false; - } - - private XMLEvent skipCommentsAndWhitespace() throws XMLStreamException { - while (eventReader.hasNext()) { - XMLEvent event = eventReader.peek(); - if (event.getEventType() == XMLStreamConstants.COMMENT) { - eventReader.nextEvent(); - continue; - } - - if (event.isCharacters()) { - Characters chars = event.asCharacters(); - if (chars.isWhiteSpace()) { - eventReader.nextEvent(); - continue; - } - } - return event; - } - return null; - } - - private CompositeNodeWrapper resolveCompositeNodeFromStartElement(final QName qName) { - // checkArgument(startElement != null, "Start Element cannot be NULL!"); - CompositeNodeWrapper compositeNodeWrapper = new CompositeNodeWrapper("dummy"); - compositeNodeWrapper.setQname(qName); - return compositeNodeWrapper; - - } - - private SimpleNodeWrapper resolveSimpleNodeFromStartElement(final DataSchemaNode node, final String value) - throws XMLStreamException { - // checkArgument(startElement != null, "Start Element cannot be NULL!"); - Object deserializedValue = null; - - if (node instanceof LeafSchemaNode) { - TypeDefinition baseType = RestUtil.resolveBaseTypeFrom(((LeafSchemaNode) node).getType()); - deserializedValue = RestCodec.from(baseType, iiWithSchema.getMountPoint()).deserialize(value); - } else if (node instanceof LeafListSchemaNode) { - TypeDefinition baseType = RestUtil.resolveBaseTypeFrom(((LeafListSchemaNode) node).getType()); - deserializedValue = RestCodec.from(baseType, iiWithSchema.getMountPoint()).deserialize(value); - } - // String data; - // if (data == null) { - // return new EmptyNodeWrapper(getNamespaceFor(startElement), getLocalNameFor(startElement)); - // } - SimpleNodeWrapper simpleNodeWrapper = new SimpleNodeWrapper("dummy", deserializedValue); - simpleNodeWrapper.setQname(node.getQName()); - return simpleNodeWrapper; - } - - private String getValueOf(final StartElement startElement) throws XMLStreamException { - String data = null; - if (eventReader.hasNext()) { - final XMLEvent innerEvent = eventReader.peek(); - if (innerEvent.isCharacters()) { - final Characters chars = innerEvent.asCharacters(); - if (!chars.isWhiteSpace()) { - data = innerEvent.asCharacters().getData(); - data = data + getAdditionalData(eventReader.nextEvent()); - } - } else if (innerEvent.isEndElement()) { - if (startElement.getLocation().getCharacterOffset() == innerEvent.getLocation().getCharacterOffset()) { - data = null; - } else { - data = ""; - } - } - } - return data == null ? null : data.trim(); - } - - private String getAdditionalData(final XMLEvent event) throws XMLStreamException { - String data = ""; - if (eventReader.hasNext()) { - final XMLEvent innerEvent = eventReader.peek(); - if (innerEvent.isCharacters() && !innerEvent.isEndElement()) { - final Characters chars = innerEvent.asCharacters(); - if (!chars.isWhiteSpace()) { - data = innerEvent.asCharacters().getData(); - data = data + getAdditionalData(eventReader.nextEvent()); - } - } - } - return data; - } - - private String getLocalNameFor(final StartElement startElement) { - return startElement.getName().getLocalPart(); - } - - private URI getNamespaceFor(final StartElement startElement) { - String namespaceURI = startElement.getName().getNamespaceURI(); - return namespaceURI.isEmpty() ? null : URI.create(namespaceURI); - } - - private Object resolveValueOfElement(final String value, final StartElement startElement) { - // it could be instance-identifier Built-In Type - if (value.startsWith("/")) { - IdentityValuesDTO iiValue = RestUtil.asInstanceIdentifier(value, new RestUtil.PrefixMapingFromXml( - startElement)); - if (iiValue != null) { - return iiValue; - } - } - // it could be identityref Built-In Type - String[] namespaceAndValue = value.split(":"); - if (namespaceAndValue.length == 2) { - String namespace = startElement.getNamespaceContext().getNamespaceURI(namespaceAndValue[0]); - if (namespace != null && !namespace.isEmpty()) { - return new IdentityValuesDTO(namespace, namespaceAndValue[1], namespaceAndValue[0], value); - } - } - // it is not "prefix:value" but just "value" - return value; - } - -} diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/BrokerFacade.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/BrokerFacade.java index f11e25c046..8dbc5b50ee 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/BrokerFacade.java +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/BrokerFacade.java @@ -9,6 +9,7 @@ package org.opendaylight.controller.sal.restconf.impl; import com.google.common.base.Optional; import com.google.common.util.concurrent.CheckedFuture; +import com.google.common.util.concurrent.ListenableFuture; import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException; @@ -40,6 +41,7 @@ import javax.ws.rs.core.Response.Status; import java.util.ArrayList; import java.util.Iterator; import java.util.List; +import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.CONFIGURATION; @@ -176,31 +178,39 @@ public class BrokerFacade { private NormalizedNode readDataViaTransaction(final DOMDataReadTransaction transaction, LogicalDatastoreType datastore, YangInstanceIdentifier path) { LOG.trace("Read " + datastore.name() + " via Restconf: {}", path); - final CheckedFuture>, ReadFailedException> listenableFuture = - transaction.read(datastore, path); + final ListenableFuture>> listenableFuture = transaction.read(datastore, path); + if (listenableFuture != null) { + Optional> optional; + try { + LOG.debug("Reading result data from transaction."); + optional = listenableFuture.get(); + } catch (InterruptedException | ExecutionException e) { + throw new RestconfDocumentedException("Problem to get data from transaction.", e.getCause()); - try { - Optional> optional = listenableFuture.checkedGet(); - return optional.isPresent() ? optional.get() : null; - } catch(ReadFailedException e) { - throw new RestconfDocumentedException(e.getMessage(), e, e.getErrorList()); + } + if (optional != null) { + if (optional.isPresent()) { + return optional.get(); + } + } } + return null; } private CheckedFuture postDataViaTransaction( final DOMDataReadWriteTransaction rWTransaction, final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode payload, DataNormalizationOperation root) { - CheckedFuture>, ReadFailedException> futureDatastoreData = - rWTransaction.read(datastore, path); + ListenableFuture>> futureDatastoreData = rWTransaction.read(datastore, path); try { - final Optional> optionalDatastoreData = futureDatastoreData.checkedGet(); + final Optional> optionalDatastoreData = futureDatastoreData.get(); if (optionalDatastoreData.isPresent() && payload.equals(optionalDatastoreData.get())) { - LOG.trace("Post Configuration via Restconf was not executed because data already exists :{}", path); + String errMsg = "Post Configuration via Restconf was not executed because data already exists"; + LOG.trace(errMsg + ":{}", path); throw new RestconfDocumentedException("Data already exists for path: " + path, ErrorType.PROTOCOL, ErrorTag.DATA_EXISTS); } - } catch(ReadFailedException e) { - LOG.warn("Error reading from datastore with path: " + path, e); + } catch (InterruptedException | ExecutionException e) { + LOG.trace("It wasn't possible to get data loaded from datastore at path " + path); } ensureParentsByMerge(datastore, path, rWTransaction, root); @@ -241,21 +251,27 @@ public class BrokerFacade { try { currentOp = currentOp.getChild(currentArg); } catch (DataNormalizationException e) { - throw new RestconfDocumentedException( - String.format("Error normalizing data for path %s", normalizedPath), e); + throw new IllegalArgumentException( + String.format("Invalid child encountered in path %s", normalizedPath), e); } currentArguments.add(currentArg); YangInstanceIdentifier currentPath = YangInstanceIdentifier.create(currentArguments); + final Boolean exists; + try { - boolean exists = rwTx.exists(store, currentPath).checkedGet(); - if (!exists && iterator.hasNext()) { - rwTx.merge(store, currentPath, currentOp.createDefault(currentArg)); - } + CheckedFuture future = + rwTx.exists(store, currentPath); + exists = future.checkedGet(); } catch (ReadFailedException e) { LOG.error("Failed to read pre-existing data from store {} path {}", store, currentPath, e); - throw new RestconfDocumentedException("Failed to read pre-existing data", e); + throw new IllegalStateException("Failed to read pre-existing data", e); + } + + + if (!exists && iterator.hasNext()) { + rwTx.merge(store, currentPath, currentOp.createDefault(currentArg)); } } } diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/ControllerContext.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/ControllerContext.java index 93e6a2c0e9..b0a6e0d6f3 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/ControllerContext.java +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/ControllerContext.java @@ -118,15 +118,19 @@ public class ControllerContext implements SchemaContextListener { this.onGlobalContextUpdated(schemas); } - public InstanceIdWithSchemaNode toInstanceIdentifier(final String restconfInstance) { + public InstanceIdentifierContext toInstanceIdentifier(final String restconfInstance) { return this.toIdentifier(restconfInstance, false); } - public InstanceIdWithSchemaNode toMountPointIdentifier(final String restconfInstance) { + public SchemaContext getGlobalSchema() { + return globalSchema; + } + + public InstanceIdentifierContext toMountPointIdentifier(final String restconfInstance) { return this.toIdentifier(restconfInstance, true); } - private InstanceIdWithSchemaNode toIdentifier(final String restconfInstance, final boolean toMountPointIdentifier) { + private InstanceIdentifierContext toIdentifier(final String restconfInstance, final boolean toMountPointIdentifier) { this.checkPreconditions(); final List pathArgs = urlPathArgsDecode(SLASH_SPLITTER.split(restconfInstance)); @@ -144,7 +148,7 @@ public class ControllerContext implements SchemaContextListener { InstanceIdentifierBuilder builder = YangInstanceIdentifier.builder(); Module latestModule = globalSchema.findModuleByName(startModule, null); - InstanceIdWithSchemaNode iiWithSchemaNode = this.collectPathArguments(builder, pathArgs, latestModule, null, + InstanceIdentifierContext iiWithSchemaNode = this.collectPathArguments(builder, pathArgs, latestModule, null, toMountPointIdentifier); if (iiWithSchemaNode == null) { @@ -462,7 +466,7 @@ public class ControllerContext implements SchemaContextListener { return object == null ? "" : URLEncoder.encode(object.toString(), ControllerContext.URI_ENCODING_CHAR_SET); } - private InstanceIdWithSchemaNode collectPathArguments(final InstanceIdentifierBuilder builder, + private InstanceIdentifierContext collectPathArguments(final InstanceIdentifierBuilder builder, final List strings, final DataNodeContainer parentNode, final DOMMountPoint mountPoint, final boolean returnJustMountPoint) { Preconditions.> checkNotNull(strings); @@ -472,7 +476,7 @@ public class ControllerContext implements SchemaContextListener { } if (strings.isEmpty()) { - return new InstanceIdWithSchemaNode(builder.toInstance(), ((DataSchemaNode) parentNode), mountPoint); + return new InstanceIdentifierContext(builder.toInstance(), ((DataSchemaNode) parentNode), mountPoint,mountPoint != null ? mountPoint.getSchemaContext() : globalSchema); } String head = strings.iterator().next(); @@ -511,12 +515,12 @@ public class ControllerContext implements SchemaContextListener { if (returnJustMountPoint) { YangInstanceIdentifier instance = YangInstanceIdentifier.builder().toInstance(); - return new InstanceIdWithSchemaNode(instance, mountPointSchema, mount); + return new InstanceIdentifierContext(instance, mountPointSchema, mount,mountPointSchema); } if (strings.size() == 1) { YangInstanceIdentifier instance = YangInstanceIdentifier.builder().toInstance(); - return new InstanceIdWithSchemaNode(instance, mountPointSchema, mount); + return new InstanceIdentifierContext(instance, mountPointSchema, mount,mountPointSchema); } final String moduleNameBehindMountPoint = toModuleName(strings.get(1)); @@ -632,7 +636,7 @@ public class ControllerContext implements SchemaContextListener { returnJustMountPoint); } - return new InstanceIdWithSchemaNode(builder.toInstance(), targetNode, mountPoint); + return new InstanceIdentifierContext(builder.toInstance(), targetNode, mountPoint,mountPoint != null ? mountPoint.getSchemaContext() : globalSchema); } public static DataSchemaNode findInstanceDataChildByNameAndNamespace(final DataNodeContainer container, final String name, diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/InstanceIdWithSchemaNode.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/InstanceIdentifierContext.java similarity index 71% rename from opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/InstanceIdWithSchemaNode.java rename to opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/InstanceIdentifierContext.java index b58a6eeaea..b9c311121e 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/InstanceIdWithSchemaNode.java +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/InstanceIdentifierContext.java @@ -10,18 +10,21 @@ package org.opendaylight.controller.sal.restconf.impl; import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; -public class InstanceIdWithSchemaNode { +public class InstanceIdentifierContext { private final YangInstanceIdentifier instanceIdentifier; private final DataSchemaNode schemaNode; private final DOMMountPoint mountPoint; + private final SchemaContext schemaContext; - public InstanceIdWithSchemaNode(YangInstanceIdentifier instanceIdentifier, DataSchemaNode schemaNode, - DOMMountPoint mountPoint) { + public InstanceIdentifierContext(YangInstanceIdentifier instanceIdentifier, DataSchemaNode schemaNode, + DOMMountPoint mountPoint,SchemaContext context) { this.instanceIdentifier = instanceIdentifier; this.schemaNode = schemaNode; this.mountPoint = mountPoint; + this.schemaContext = context; } public YangInstanceIdentifier getInstanceIdentifier() { @@ -36,4 +39,8 @@ public class InstanceIdWithSchemaNode { return mountPoint; } + public SchemaContext getSchemaContext() { + return schemaContext; + } + } diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/NormalizedDataPrunner.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/NormalizedDataPrunner.java new file mode 100644 index 0000000000..5d0185b4cc --- /dev/null +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/NormalizedDataPrunner.java @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.sal.restconf.impl; + +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; +import org.opendaylight.yangtools.yang.data.api.schema.AnyXmlNode; +import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode; +import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode; +import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; +import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild; +import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode; +import org.opendaylight.yangtools.yang.data.api.schema.LeafNode; +import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode; +import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode; +import org.opendaylight.yangtools.yang.data.api.schema.MapNode; +import org.opendaylight.yangtools.yang.data.api.schema.MixinNode; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.api.schema.OrderedLeafSetNode; +import org.opendaylight.yangtools.yang.data.api.schema.OrderedMapNode; +import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode; +import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode; +import org.opendaylight.yangtools.yang.data.impl.schema.Builders; +import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder; +import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder; +import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder; + +class NormalizedDataPrunner { + + public DataContainerChild pruneDataAtDepth(final DataContainerChild node, final Integer depth) { + if (depth == null) { + return node; + } + + if (node instanceof LeafNode || node instanceof LeafSetNode || node instanceof AnyXmlNode + || node instanceof OrderedLeafSetNode) { + return node; + } else if (node instanceof MixinNode) { + return processMixinNode(node, depth); + } else if (node instanceof DataContainerNode) { + return processContainerNode(node, depth); + } + throw new IllegalStateException("Unexpected Mixin node occured why pruning data to requested depth"); + } + + private DataContainerChild processMixinNode(final NormalizedNode node, final Integer depth) { + if (node instanceof AugmentationNode) { + return processAugmentationNode(node, depth); + } else if (node instanceof ChoiceNode) { + return processChoiceNode(node, depth); + } else if (node instanceof OrderedMapNode) { + return processOrderedMapNode(node, depth); + } else if (node instanceof MapNode) { + return processMapNode(node, depth); + } else if (node instanceof UnkeyedListNode) { + return processUnkeyedListNode(node, depth); + } + throw new IllegalStateException("Unexpected Mixin node occured why pruning data to requested depth"); + } + + private DataContainerChild processContainerNode(final NormalizedNode node, final Integer depth) { + final ContainerNode containerNode = (ContainerNode) node; + DataContainerNodeAttrBuilder newContainerBuilder = Builders.containerBuilder() + .withNodeIdentifier(containerNode.getIdentifier()); + if (depth > 1) { + processDataContainerChild((DataContainerNode) node, depth, newContainerBuilder); + } + return newContainerBuilder.build(); + } + + private DataContainerChild processChoiceNode(final NormalizedNode node, final Integer depth) { + final ChoiceNode choiceNode = (ChoiceNode) node; + DataContainerNodeBuilder newChoiceBuilder = Builders.choiceBuilder() + .withNodeIdentifier(choiceNode.getIdentifier()); + + processDataContainerChild((DataContainerNode) node, depth, newChoiceBuilder); + + return newChoiceBuilder.build(); + } + + private DataContainerChild processAugmentationNode(final NormalizedNode node, final Integer depth) { + final AugmentationNode augmentationNode = (AugmentationNode) node; + DataContainerNodeBuilder> newAugmentationBuilder = Builders + .augmentationBuilder().withNodeIdentifier(augmentationNode.getIdentifier()); + + processDataContainerChild((DataContainerNode) node, depth, newAugmentationBuilder); + + return newAugmentationBuilder.build(); + } + + private void processDataContainerChild( + final DataContainerNode node, + final Integer depth, + final DataContainerNodeBuilder> newBuilder) { + + for (DataContainerChild nodeValue : node.getValue()) { + newBuilder.withChild(pruneDataAtDepth(nodeValue, depth - 1)); + } + + } + + private DataContainerChild processUnkeyedListNode(final NormalizedNode node, final Integer depth) { + CollectionNodeBuilder newUnkeyedListBuilder = Builders + .unkeyedListBuilder(); + if (depth > 1) { + for (UnkeyedListEntryNode oldUnkeyedListEntry : ((UnkeyedListNode) node).getValue()) { + DataContainerNodeAttrBuilder newUnkeyedListEntry = Builders + .unkeyedListEntryBuilder().withNodeIdentifier(oldUnkeyedListEntry.getIdentifier()); + for (DataContainerChild oldUnkeyedListEntryValue : oldUnkeyedListEntry + .getValue()) { + newUnkeyedListEntry.withChild(pruneDataAtDepth(oldUnkeyedListEntryValue, depth - 1)); + } + newUnkeyedListBuilder.addChild(newUnkeyedListEntry.build()); + } + } + return newUnkeyedListBuilder.build(); + } + + private DataContainerChild processOrderedMapNode(final NormalizedNode node, final Integer depth) { + CollectionNodeBuilder newOrderedMapNodeBuilder = Builders.orderedMapBuilder(); + processMapEntries(node, depth, newOrderedMapNodeBuilder); + return newOrderedMapNodeBuilder.build(); + } + + private DataContainerChild processMapNode(final NormalizedNode node, final Integer depth) { + CollectionNodeBuilder newMapNodeBuilder = Builders.mapBuilder(); + processMapEntries(node, depth, newMapNodeBuilder); + return newMapNodeBuilder.build(); + } + + private void processMapEntries(final NormalizedNode node, final Integer depth, + CollectionNodeBuilder newOrderedMapNodeBuilder) { + if (depth > 1) { + for (MapEntryNode oldMapEntryNode : ((MapNode) node).getValue()) { + DataContainerNodeAttrBuilder newMapEntryNodeBuilder = Builders + .mapEntryBuilder().withNodeIdentifier(oldMapEntryNode.getIdentifier()); + for (DataContainerChild mapEntryNodeValue : oldMapEntryNode.getValue()) { + newMapEntryNodeBuilder.withChild(pruneDataAtDepth(mapEntryNodeValue, depth - 1)); + } + newOrderedMapNodeBuilder.withChild(newMapEntryNodeBuilder.build()); + } + } + } + + +} diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/NormalizedNodeContext.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/NormalizedNodeContext.java new file mode 100644 index 0000000000..e698693b95 --- /dev/null +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/NormalizedNodeContext.java @@ -0,0 +1,22 @@ +package org.opendaylight.controller.sal.restconf.impl; + +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; + +public class NormalizedNodeContext { + + private final InstanceIdentifierContext context; + private final NormalizedNode data; + + public NormalizedNodeContext(InstanceIdentifierContext context, NormalizedNode data) { + this.context = context; + this.data = data; + } + + public InstanceIdentifierContext getInstanceIdentifierContext() { + return context; + } + + public NormalizedNode getData() { + return data; + } +} diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfImpl.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfImpl.java index b94f6a6166..5d8c910afc 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfImpl.java +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfImpl.java @@ -15,7 +15,7 @@ import com.google.common.base.Strings; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; - +import java.math.BigInteger; import java.net.URI; import java.text.ParseException; import java.text.SimpleDateFormat; @@ -32,7 +32,6 @@ import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; import javax.ws.rs.core.UriBuilder; import javax.ws.rs.core.UriInfo; - import org.apache.commons.lang3.StringUtils; import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; @@ -87,6 +86,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class RestconfImpl implements RestconfService { + private enum UriParameters { PRETTY_PRINT("prettyPrint"), DEPTH("depth"); @@ -103,6 +103,8 @@ public class RestconfImpl implements RestconfService { } } + + private final static RestconfImpl INSTANCE = new RestconfImpl(); private static final int NOTIFICATION_PORT = 8181; @@ -205,7 +207,7 @@ public class RestconfImpl implements RestconfService { Set modules = null; DOMMountPoint mountPoint = null; if (identifier.contains(ControllerContext.MOUNT)) { - InstanceIdWithSchemaNode mountPointIdentifier = this.controllerContext.toMountPointIdentifier(identifier); + InstanceIdentifierContext mountPointIdentifier = this.controllerContext.toMountPointIdentifier(identifier); mountPoint = mountPointIdentifier.getMountPoint(); modules = this.controllerContext.getAllModules(mountPoint); } else { @@ -236,7 +238,7 @@ public class RestconfImpl implements RestconfService { Module module = null; DOMMountPoint mountPoint = null; if (identifier.contains(ControllerContext.MOUNT)) { - InstanceIdWithSchemaNode mountPointIdentifier = this.controllerContext.toMountPointIdentifier(identifier); + InstanceIdentifierContext mountPointIdentifier = this.controllerContext.toMountPointIdentifier(identifier); mountPoint = mountPointIdentifier.getMountPoint(); module = this.controllerContext.findModuleByNameAndRevision(mountPoint, moduleNameAndRevision); } else { @@ -267,7 +269,7 @@ public class RestconfImpl implements RestconfService { Set modules = null; DOMMountPoint mountPoint = null; if (identifier.contains(ControllerContext.MOUNT)) { - InstanceIdWithSchemaNode mountPointIdentifier = this.controllerContext.toMountPointIdentifier(identifier); + InstanceIdentifierContext mountPointIdentifier = this.controllerContext.toMountPointIdentifier(identifier); mountPoint = mountPointIdentifier.getMountPoint(); modules = this.controllerContext.getAllModules(mountPoint); } else { @@ -518,7 +520,7 @@ public class RestconfImpl implements RestconfService { DOMMountPoint mountPoint = null; if (identifier.contains(ControllerContext.MOUNT)) { // mounted RPC call - look up mount instance. - InstanceIdWithSchemaNode mountPointId = controllerContext.toMountPointIdentifier(identifier); + InstanceIdentifierContext mountPointId = controllerContext.toMountPointIdentifier(identifier); mountPoint = mountPointId.getMountPoint(); int startOfRemoteRpcName = identifier.lastIndexOf(ControllerContext.MOUNT) @@ -615,8 +617,8 @@ public class RestconfImpl implements RestconfService { } @Override - public StructuredData readConfigurationData(final String identifier, final UriInfo uriInfo) { - final InstanceIdWithSchemaNode iiWithData = controllerContext.toInstanceIdentifier(identifier); + public NormalizedNodeContext readConfigurationData(final String identifier, final UriInfo uriInfo) { + final InstanceIdentifierContext iiWithData = controllerContext.toInstanceIdentifier(identifier); DOMMountPoint mountPoint = iiWithData.getMountPoint(); NormalizedNode data = null; YangInstanceIdentifier normalizedII; @@ -628,12 +630,7 @@ public class RestconfImpl implements RestconfService { normalizedII = controllerContext.toNormalized(iiWithData.getInstanceIdentifier()); data = broker.readConfigurationData(normalizedII); } - - final CompositeNode compositeNode = datastoreNormalizedNodeToCompositeNode(data, iiWithData.getSchemaNode()); - final CompositeNode prunedCompositeNode = pruneDataAtDepth(compositeNode, parseDepthParameter(uriInfo)); - - final boolean prettyPrintMode = parsePrettyPrintParameter(uriInfo); - return new StructuredData(prunedCompositeNode, iiWithData.getSchemaNode(), mountPoint, prettyPrintMode); + return new NormalizedNodeContext(iiWithData, data); } @SuppressWarnings("unchecked") @@ -679,8 +676,8 @@ public class RestconfImpl implements RestconfService { } @Override - public StructuredData readOperationalData(final String identifier, final UriInfo info) { - final InstanceIdWithSchemaNode iiWithData = controllerContext.toInstanceIdentifier(identifier); + public NormalizedNodeContext readOperationalData(final String identifier, final UriInfo info) { + final InstanceIdentifierContext iiWithData = controllerContext.toInstanceIdentifier(identifier); DOMMountPoint mountPoint = iiWithData.getMountPoint(); NormalizedNode data = null; YangInstanceIdentifier normalizedII; @@ -693,11 +690,7 @@ public class RestconfImpl implements RestconfService { data = broker.readOperationalData(normalizedII); } - final CompositeNode compositeNode = datastoreNormalizedNodeToCompositeNode(data, iiWithData.getSchemaNode()); - final CompositeNode prunedCompositeNode = pruneDataAtDepth(compositeNode, parseDepthParameter(info)); - - final boolean prettyPrintMode = parsePrettyPrintParameter(info); - return new StructuredData(prunedCompositeNode, iiWithData.getSchemaNode(), mountPoint, prettyPrintMode); + return new NormalizedNodeContext(iiWithData, data); } private boolean parsePrettyPrintParameter(final UriInfo info) { @@ -707,7 +700,7 @@ public class RestconfImpl implements RestconfService { @Override public Response updateConfigurationData(final String identifier, final Node payload) { - final InstanceIdWithSchemaNode iiWithData = this.controllerContext.toInstanceIdentifier(identifier); + final InstanceIdentifierContext iiWithData = this.controllerContext.toInstanceIdentifier(identifier); validateInput(iiWithData.getSchemaNode(), payload); @@ -774,7 +767,7 @@ public class RestconfImpl implements RestconfService { * if key values or key count in payload and URI isn't equal * */ - private void validateListKeysEqualityInPayloadAndUri(final InstanceIdWithSchemaNode iiWithData, + private void validateListKeysEqualityInPayloadAndUri(final InstanceIdentifierContext iiWithData, final CompositeNode payload) { if (iiWithData.getSchemaNode() instanceof ListSchemaNode) { final List keyDefinitions = ((ListSchemaNode) iiWithData.getSchemaNode()).getKeyDefinition(); @@ -825,7 +818,7 @@ public class RestconfImpl implements RestconfService { ErrorType.PROTOCOL, ErrorTag.UNKNOWN_NAMESPACE); } - InstanceIdWithSchemaNode iiWithData = null; + InstanceIdentifierContext iiWithData = null; CompositeNode value = null; if (this.representsMountPointRootData(payload)) { // payload represents mount point data and URI represents path to the mount point @@ -841,7 +834,7 @@ public class RestconfImpl implements RestconfService { value = this.normalizeNode(payload, iiWithData.getSchemaNode(), iiWithData.getMountPoint()); } else { - final InstanceIdWithSchemaNode incompleteInstIdWithData = this.controllerContext + final InstanceIdentifierContext incompleteInstIdWithData = this.controllerContext .toInstanceIdentifier(identifier); final DataNodeContainer parentSchema = (DataNodeContainer) incompleteInstIdWithData.getSchemaNode(); DOMMountPoint mountPoint = incompleteInstIdWithData.getMountPoint(); @@ -856,7 +849,7 @@ public class RestconfImpl implements RestconfService { parentSchema, payloadName, module.getNamespace()); value = this.normalizeNode(payload, schemaNode, mountPoint); - iiWithData = addLastIdentifierFromData(incompleteInstIdWithData, value, schemaNode); + iiWithData = addLastIdentifierFromData(incompleteInstIdWithData, value, schemaNode,incompleteInstIdWithData.getSchemaContext()); } final NormalizedNode datastoreNormalizedData = compositeNodeToDatastoreNormalizedNode(value, @@ -906,7 +899,7 @@ public class RestconfImpl implements RestconfService { final DataSchemaNode schemaNode = ControllerContext.findInstanceDataChildByNameAndNamespace(module, payloadName, module.getNamespace()); final CompositeNode value = this.normalizeNode(payload, schemaNode, null); - final InstanceIdWithSchemaNode iiWithData = this.addLastIdentifierFromData(null, value, schemaNode); + final InstanceIdentifierContext iiWithData = this.addLastIdentifierFromData(null, value, schemaNode,ControllerContext.getInstance().getGlobalSchema()); final NormalizedNode datastoreNormalizedData = compositeNodeToDatastoreNormalizedNode(value, schemaNode); DOMMountPoint mountPoint = iiWithData.getMountPoint(); YangInstanceIdentifier normalizedII; @@ -932,7 +925,7 @@ public class RestconfImpl implements RestconfService { @Override public Response deleteConfigurationData(final String identifier) { - final InstanceIdWithSchemaNode iiWithData = controllerContext.toInstanceIdentifier(identifier); + final InstanceIdentifierContext iiWithData = controllerContext.toInstanceIdentifier(identifier); DOMMountPoint mountPoint = iiWithData.getMountPoint(); YangInstanceIdentifier normalizedII; @@ -1098,8 +1091,8 @@ public class RestconfImpl implements RestconfService { return module; } - private InstanceIdWithSchemaNode addLastIdentifierFromData(final InstanceIdWithSchemaNode identifierWithSchemaNode, - final CompositeNode data, final DataSchemaNode schemaOfData) { + private InstanceIdentifierContext addLastIdentifierFromData(final InstanceIdentifierContext identifierWithSchemaNode, + final CompositeNode data, final DataSchemaNode schemaOfData, SchemaContext schemaContext) { YangInstanceIdentifier instanceIdentifier = null; if (identifierWithSchemaNode != null) { instanceIdentifier = identifierWithSchemaNode.getInstanceIdentifier(); @@ -1122,11 +1115,12 @@ public class RestconfImpl implements RestconfService { YangInstanceIdentifier instance = iiBuilder.toInstance(); DOMMountPoint mountPoint = null; + SchemaContext schemaCtx = null; if (identifierWithSchemaNode != null) { mountPoint = identifierWithSchemaNode.getMountPoint(); } - return new InstanceIdWithSchemaNode(instance, schemaOfData, mountPoint); + return new InstanceIdentifierContext(instance, schemaOfData, mountPoint,schemaContext); } private HashMap resolveKeysFromData(final ListSchemaNode listNode, final CompositeNode dataNode) { @@ -1514,16 +1508,16 @@ public class RestconfImpl implements RestconfService { "It wasn't possible to translate specified data to datastore readable form.")); } - private InstanceIdWithSchemaNode normalizeInstanceIdentifierWithSchemaNode( - final InstanceIdWithSchemaNode iiWithSchemaNode) { + private InstanceIdentifierContext normalizeInstanceIdentifierWithSchemaNode( + final InstanceIdentifierContext iiWithSchemaNode) { return normalizeInstanceIdentifierWithSchemaNode(iiWithSchemaNode, false); } - private InstanceIdWithSchemaNode normalizeInstanceIdentifierWithSchemaNode( - final InstanceIdWithSchemaNode iiWithSchemaNode, final boolean unwrapLastListNode) { - return new InstanceIdWithSchemaNode(instanceIdentifierToReadableFormForNormalizeNode( + private InstanceIdentifierContext normalizeInstanceIdentifierWithSchemaNode( + final InstanceIdentifierContext iiWithSchemaNode, final boolean unwrapLastListNode) { + return new InstanceIdentifierContext(instanceIdentifierToReadableFormForNormalizeNode( iiWithSchemaNode.getInstanceIdentifier(), unwrapLastListNode), iiWithSchemaNode.getSchemaNode(), - iiWithSchemaNode.getMountPoint()); + iiWithSchemaNode.getMountPoint(),iiWithSchemaNode.getSchemaContext()); } private YangInstanceIdentifier instanceIdentifierToReadableFormForNormalizeNode( @@ -1560,4 +1554,9 @@ public class RestconfImpl implements RestconfService { } return false; } + + public BigInteger getOperationalReceived() { + // TODO Auto-generated method stub + return null; + } } diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/RestconfProviderImpl.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfProviderImpl.java similarity index 59% rename from opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/RestconfProviderImpl.java rename to opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfProviderImpl.java index 2fa99819d5..abadbf6cb8 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/RestconfProviderImpl.java +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfProviderImpl.java @@ -5,35 +5,41 @@ * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html */ -package org.opendaylight.controller.sal.rest.impl; +package org.opendaylight.controller.sal.restconf.impl; -import java.util.Collection; -import java.util.Collections; +import org.opendaylight.controller.config.yang.md.sal.rest.connector.Config; +import org.opendaylight.controller.config.yang.md.sal.rest.connector.Get; +import org.opendaylight.controller.config.yang.md.sal.rest.connector.Operational; +import org.opendaylight.controller.config.yang.md.sal.rest.connector.Post; +import org.opendaylight.controller.config.yang.md.sal.rest.connector.Put; +import org.opendaylight.controller.config.yang.md.sal.rest.connector.RestConnectorRuntimeMXBean; +import org.opendaylight.controller.config.yang.md.sal.rest.connector.Rpcs; import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker; import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService; import org.opendaylight.controller.sal.core.api.Broker.ProviderSession; import org.opendaylight.controller.sal.core.api.Provider; import org.opendaylight.controller.sal.core.api.model.SchemaService; import org.opendaylight.controller.sal.rest.api.RestConnector; -import org.opendaylight.controller.sal.restconf.impl.BrokerFacade; -import org.opendaylight.controller.sal.restconf.impl.ControllerContext; import org.opendaylight.controller.sal.streams.websockets.WebSocketServer; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.PortNumber; import org.opendaylight.yangtools.concepts.ListenerRegistration; import org.opendaylight.yangtools.yang.model.api.SchemaContextListener; -public class RestconfProviderImpl implements Provider, AutoCloseable, RestConnector { +import java.math.BigInteger; +import java.util.Collection; +import java.util.Collections; - public final static String NOT_INITALIZED_MSG = "Restconf is not initialized yet. Please try again later"; +public class RestconfProviderImpl implements Provider, AutoCloseable, RestConnector, RestConnectorRuntimeMXBean { + private final StatisticsRestconfServiceWrapper stats = StatisticsRestconfServiceWrapper.getInstance(); private ListenerRegistration listenerRegistration; private PortNumber port; + private Thread webSocketServerThread; + public void setWebsocketPort(PortNumber port) { this.port = port; } - private Thread webSocketServerThread; - @Override public void onSessionInitiated(ProviderSession session) { final DOMDataBroker domDataBroker = session.getService(DOMDataBroker.class); @@ -58,9 +64,45 @@ public class RestconfProviderImpl implements Provider, AutoCloseable, RestConnec @Override public void close() { + if (listenerRegistration != null) { listenerRegistration.close(); } + + WebSocketServer.destroyInstance(); webSocketServerThread.interrupt(); } + + @Override + public Config getConfig() { + Config config = new Config(); + Get get = new Get(); + get.setReceivedRequests(stats.getConfigGet()); + config.setGet(get); + Post post = new Post(); + post.setReceivedRequests(stats.getConfigPost()); + config.setPost(post); + Put put = new Put(); + put.setReceivedRequests(stats.getConfigPut()); + config.setPut(put); + return config; + } + + @Override + public Operational getOperational() { + BigInteger opGet = stats.getOperationalGet(); + Operational operational = new Operational(); + Get get = new Get(); + get.setReceivedRequests(opGet); + operational.setGet(get); + return operational; + } + + @Override + public Rpcs getRpcs() { + BigInteger rpcInvoke = stats.getRpc(); + Rpcs rpcs = new Rpcs(); + rpcs.setReceivedRequests(rpcInvoke); + return rpcs ; + } } diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/StatisticsRestconfServiceWrapper.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/StatisticsRestconfServiceWrapper.java new file mode 100644 index 0000000000..eafbb81c48 --- /dev/null +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/StatisticsRestconfServiceWrapper.java @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.sal.restconf.impl; + +import java.math.BigInteger; +import java.util.concurrent.atomic.AtomicLong; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; +import org.opendaylight.controller.sal.rest.api.RestconfService; +import org.opendaylight.yangtools.yang.data.api.CompositeNode; +import org.opendaylight.yangtools.yang.data.api.Node; + +public class StatisticsRestconfServiceWrapper implements RestconfService { + + AtomicLong operationalGet = new AtomicLong(); + AtomicLong configGet = new AtomicLong(); + AtomicLong rpc = new AtomicLong(); + AtomicLong configPost = new AtomicLong(); + AtomicLong configPut = new AtomicLong(); + AtomicLong configDelete = new AtomicLong(); + + private static final StatisticsRestconfServiceWrapper INSTANCE = new StatisticsRestconfServiceWrapper(RestconfImpl.getInstance()); + + final RestconfService delegate; + + private StatisticsRestconfServiceWrapper(RestconfService delegate) { + this.delegate = delegate; + } + + public static StatisticsRestconfServiceWrapper getInstance() { + return INSTANCE; + } + + @Override + public Object getRoot() { + return delegate.getRoot(); + } + + @Override + public StructuredData getModules(UriInfo uriInfo) { + return delegate.getModules(uriInfo); + } + + @Override + public StructuredData getModules(String identifier, UriInfo uriInfo) { + return delegate.getModules(identifier, uriInfo); + } + + @Override + public StructuredData getModule(String identifier, UriInfo uriInfo) { + return delegate.getModule(identifier, uriInfo); + } + + @Override + public StructuredData getOperations(UriInfo uriInfo) { + return delegate.getOperations(uriInfo); + } + + @Override + public StructuredData getOperations(String identifier, UriInfo uriInfo) { + return delegate.getOperations(identifier, uriInfo); + } + + @Override + public StructuredData invokeRpc(String identifier, CompositeNode payload, UriInfo uriInfo) { + rpc.incrementAndGet(); + return delegate.invokeRpc(identifier, payload, uriInfo); + } + + @Override + public StructuredData invokeRpc(String identifier, String noPayload, UriInfo uriInfo) { + rpc.incrementAndGet(); + return delegate.invokeRpc(identifier, noPayload, uriInfo); + } + + @Override + public NormalizedNodeContext readConfigurationData(String identifier, UriInfo uriInfo) { + configGet.incrementAndGet(); + return delegate.readConfigurationData(identifier, uriInfo); + } + + @Override + public NormalizedNodeContext readOperationalData(String identifier, UriInfo uriInfo) { + operationalGet.incrementAndGet(); + return delegate.readOperationalData(identifier, uriInfo); + } + + @Override + public Response updateConfigurationData(String identifier, Node payload) { + configPut.incrementAndGet(); + return delegate.updateConfigurationData(identifier, payload); + } + + @Override + public Response createConfigurationData(String identifier, Node payload) { + configPost.incrementAndGet(); + return delegate.createConfigurationData(identifier, payload); + } + + @Override + public Response createConfigurationData(Node payload) { + configPost.incrementAndGet(); + return delegate.createConfigurationData(payload); + } + + @Override + public Response deleteConfigurationData(String identifier) { + return delegate.deleteConfigurationData(identifier); + } + + @Override + public Response subscribeToStream(String identifier, UriInfo uriInfo) { + return delegate.subscribeToStream(identifier, uriInfo); + } + + @Override + public StructuredData getAvailableStreams(UriInfo uriInfo) { + return delegate.getAvailableStreams(uriInfo); + } + + public BigInteger getConfigDelete() { + return BigInteger.valueOf(configDelete.get()); + } + + public BigInteger getConfigGet() { + return BigInteger.valueOf(configGet.get()); + } + + public BigInteger getConfigPost() { + return BigInteger.valueOf(configPost.get()); + } + + public BigInteger getConfigPut() { + return BigInteger.valueOf(configPut.get()); + } + + public BigInteger getOperationalGet() { + return BigInteger.valueOf(operationalGet.get()); + } + + public BigInteger getRpc() { + return BigInteger.valueOf(rpc.get()); + } + +} diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/streams/websockets/WebSocketServer.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/streams/websockets/WebSocketServer.java index 67ed44f84e..0a5f5f0ff0 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/streams/websockets/WebSocketServer.java +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/streams/websockets/WebSocketServer.java @@ -16,11 +16,10 @@ import org.slf4j.LoggerFactory; public class WebSocketServer implements Runnable { private static final Logger logger = LoggerFactory.getLogger(WebSocketServer.class); - public static final String WEBSOCKET_SERVER_CONFIG_PROPERTY = "restconf.websocket.port"; public static final int DEFAULT_PORT = 8181; private EventLoopGroup bossGroup; private EventLoopGroup workerGroup; - private static WebSocketServer singleton = null; + private static WebSocketServer instance = null; private int port = DEFAULT_PORT; private WebSocketServer(int port) { @@ -35,14 +34,11 @@ public class WebSocketServer implements Runnable { * @return instance of {@link WebSocketServer} */ public static WebSocketServer createInstance(int port) { - if (singleton != null) { - throw new IllegalStateException("createInstance() has already been called"); - } - if (port < 1024) { - throw new IllegalArgumentException("Privileged port (below 1024) is not allowed"); - } - singleton = new WebSocketServer(port); - return singleton; + Preconditions.checkState(instance == null, "createInstance() has already been called"); + Preconditions.checkArgument(port > 1024, "Privileged port (below 1024) is not allowed"); + + instance = new WebSocketServer(port); + return instance; } /** @@ -58,18 +54,18 @@ public class WebSocketServer implements Runnable { * @return instance of {@link WebSocketServer} */ public static WebSocketServer getInstance() { - Preconditions.checkNotNull(singleton, "createInstance() must be called prior to getInstance()"); - return singleton; + Preconditions.checkNotNull(instance, "createInstance() must be called prior to getInstance()"); + return instance; } /** * Destroy this already created instance */ public static void destroyInstance() { - if (singleton == null) { - throw new IllegalStateException("createInstance() must be called prior to destroyInstance()"); - } - getInstance().stop(); + Preconditions.checkState(instance != null, "createInstance() must be called prior to destroyInstance()"); + + instance.stop(); + instance = null; } @Override @@ -99,9 +95,11 @@ public class WebSocketServer implements Runnable { Notificator.removeAllListeners(); if (bossGroup != null) { bossGroup.shutdownGracefully(); + bossGroup = null; } if (workerGroup != null) { workerGroup.shutdownGracefully(); + workerGroup = null; } } diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/yang/opendaylight-rest-connector.yang b/opendaylight/md-sal/sal-rest-connector/src/main/yang/opendaylight-rest-connector.yang index a8fc8ff4d5..6d2add6ff1 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/main/yang/opendaylight-rest-connector.yang +++ b/opendaylight/md-sal/sal-rest-connector/src/main/yang/opendaylight-rest-connector.yang @@ -27,6 +27,12 @@ module opendaylight-rest-connector { config:java-name-prefix RestConnector; } + grouping statistics { + leaf received-requests { + type uint64; + } + } + augment "/config:modules/config:module/config:configuration" { case rest-connector-impl { when "/config:modules/config:module/config:type = 'rest-connector-impl'"; @@ -44,4 +50,33 @@ module opendaylight-rest-connector { } } } + + augment "/config:modules/config:module/config:state" { + case rest-connector-impl { + when "/config:modules/config:module/config:type = 'rest-connector-impl'"; + container rpcs { + uses statistics; + } + + container config { + container get { + uses statistics; + } + + container post { + uses statistics; + } + + container put { + uses statistics; + } + } + + container operational { + container get { + uses statistics; + } + } + } + } } \ No newline at end of file diff --git a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/InvokeRpcMethodTest.java b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/InvokeRpcMethodTest.java index 2f045ce381..559a6b9e8e 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/InvokeRpcMethodTest.java +++ b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/InvokeRpcMethodTest.java @@ -40,7 +40,7 @@ import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint; import org.opendaylight.controller.sal.core.api.RpcProvisionRegistry; import org.opendaylight.controller.sal.restconf.impl.BrokerFacade; import org.opendaylight.controller.sal.restconf.impl.ControllerContext; -import org.opendaylight.controller.sal.restconf.impl.InstanceIdWithSchemaNode; +import org.opendaylight.controller.sal.restconf.impl.InstanceIdentifierContext; import org.opendaylight.controller.sal.restconf.impl.RestconfDocumentedException; import org.opendaylight.controller.sal.restconf.impl.RestconfError; import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorTag; @@ -324,7 +324,7 @@ public class InvokeRpcMethodTest { when(mockMountPoint.getService(eq(RpcProvisionRegistry.class))).thenReturn(Optional.of(mockedRpcProvisionRegistry)); when(mockMountPoint.getSchemaContext()).thenReturn(TestUtils.loadSchemaContext("/invoke-rpc")); - InstanceIdWithSchemaNode mockedInstanceId = mock(InstanceIdWithSchemaNode.class); + InstanceIdentifierContext mockedInstanceId = mock(InstanceIdentifierContext.class); when(mockedInstanceId.getMountPoint()).thenReturn(mockMountPoint); ControllerContext mockedContext = mock(ControllerContext.class); diff --git a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestGetAugmentedElementWhenEqualNamesTest.java b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestGetAugmentedElementWhenEqualNamesTest.java index bab06e9245..856d8c006b 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestGetAugmentedElementWhenEqualNamesTest.java +++ b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestGetAugmentedElementWhenEqualNamesTest.java @@ -17,7 +17,7 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.opendaylight.controller.sal.restconf.impl.ControllerContext; -import org.opendaylight.controller.sal.restconf.impl.InstanceIdWithSchemaNode; +import org.opendaylight.controller.sal.restconf.impl.InstanceIdentifierContext; import org.opendaylight.controller.sal.restconf.impl.RestconfDocumentedException; import org.opendaylight.yangtools.yang.model.api.SchemaContext; @@ -36,7 +36,7 @@ public class RestGetAugmentedElementWhenEqualNamesTest { @Test public void augmentedNodesInUri() { - InstanceIdWithSchemaNode iiWithData = controllerContext.toInstanceIdentifier("main:cont/augment-main-a:cont1"); + InstanceIdentifierContext iiWithData = controllerContext.toInstanceIdentifier("main:cont/augment-main-a:cont1"); assertEquals("ns:augment:main:a", iiWithData.getSchemaNode().getQName().getNamespace().toString()); iiWithData = controllerContext.toInstanceIdentifier("main:cont/augment-main-b:cont1"); assertEquals("ns:augment:main:b", iiWithData.getSchemaNode().getQName().getNamespace().toString()); diff --git a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestGetOperationTest.java b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestGetOperationTest.java index 539248a147..06cfd84b05 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestGetOperationTest.java +++ b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestGetOperationTest.java @@ -44,12 +44,14 @@ import javax.ws.rs.core.UriInfo; import org.glassfish.jersey.server.ResourceConfig; import org.glassfish.jersey.test.JerseyTest; import org.junit.BeforeClass; +import org.junit.Ignore; import org.junit.Test; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint; import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService; import org.opendaylight.controller.sal.rest.impl.JsonToCompositeNodeProvider; +import org.opendaylight.controller.sal.rest.impl.RestconfApplication; import org.opendaylight.controller.sal.rest.impl.RestconfDocumentedExceptionMapper; import org.opendaylight.controller.sal.rest.impl.StructuredDataToJsonProvider; import org.opendaylight.controller.sal.rest.impl.StructuredDataToXmlProvider; @@ -130,6 +132,7 @@ public class RestGetOperationTest extends JerseyTest { StructuredDataToJsonProvider.INSTANCE, XmlToCompositeNodeProvider.INSTANCE, JsonToCompositeNodeProvider.INSTANCE); resourceConfig.registerClasses(RestconfDocumentedExceptionMapper.class); + resourceConfig.registerClasses(new RestconfApplication().getClasses()); return resourceConfig; } @@ -717,6 +720,7 @@ public class RestGetOperationTest extends JerseyTest { } @Test + @Ignore public void getDataWithUriDepthParameterTest() throws UnsupportedEncodingException { ControllerContext.getInstance().setGlobalSchema(schemaContextModules); @@ -903,6 +907,7 @@ public class RestGetOperationTest extends JerseyTest { * Tests behavior when invalid value of depth URI parameter */ @Test + @Ignore public void getDataWithInvalidDepthParameterTest() { ControllerContext.getInstance().setGlobalSchema(schemaContextModules); @@ -930,7 +935,7 @@ public class RestGetOperationTest extends JerseyTest { try { QName qNameDepth1Cont = QName.create("urn:nested:module", "2014-06-3", "depth1-cont"); YangInstanceIdentifier ii = YangInstanceIdentifier.builder().node(qNameDepth1Cont).build(); - NormalizedNode value = (NormalizedNode)(Builders.containerBuilder().withNodeIdentifier(new NodeIdentifier(qNameDepth1Cont)).build()); + NormalizedNode value = (Builders.containerBuilder().withNodeIdentifier(new NodeIdentifier(qNameDepth1Cont)).build()); when(brokerFacade.readConfigurationData(eq(ii))).thenReturn(value); restconfImpl.readConfigurationData("nested-module:depth1-cont", uriInfo); fail("Expected RestconfDocumentedException"); diff --git a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestconfDocumentedExceptionMapperTest.java b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestconfDocumentedExceptionMapperTest.java index 4e9c96ac3e..3a16b18efc 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestconfDocumentedExceptionMapperTest.java +++ b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestconfDocumentedExceptionMapperTest.java @@ -53,6 +53,7 @@ import org.glassfish.jersey.server.ResourceConfig; import org.glassfish.jersey.test.JerseyTest; import org.junit.Before; import org.junit.BeforeClass; +import org.junit.Ignore; import org.junit.Test; import org.opendaylight.controller.sal.rest.api.Draft02; import org.opendaylight.controller.sal.rest.api.RestconfService; @@ -60,11 +61,11 @@ import org.opendaylight.controller.sal.rest.impl.RestconfDocumentedExceptionMapp import org.opendaylight.controller.sal.rest.impl.StructuredDataToJsonProvider; import org.opendaylight.controller.sal.rest.impl.StructuredDataToXmlProvider; import org.opendaylight.controller.sal.restconf.impl.ControllerContext; +import org.opendaylight.controller.sal.restconf.impl.NormalizedNodeContext; import org.opendaylight.controller.sal.restconf.impl.RestconfDocumentedException; import org.opendaylight.controller.sal.restconf.impl.RestconfError; import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorTag; import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorType; -import org.opendaylight.controller.sal.restconf.impl.StructuredData; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; @@ -679,13 +680,14 @@ public class RestconfDocumentedExceptionMapperTest extends JerseyTest { } @Test + @Ignore public void testToResponseWithStatusOnly() throws Exception { // The StructuredDataToJsonProvider should throw a // RestconfDocumentedException with no data when(mockRestConf.readOperationalData(any(String.class), any(UriInfo.class))).thenReturn( - new StructuredData(null, null, null)); + new NormalizedNodeContext(null, null)); Response resp = target("/operational/foo").request(MediaType.APPLICATION_JSON).get(); diff --git a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/TestUtils.java b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/TestUtils.java index c2b153f02b..15d73e3e7a 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/TestUtils.java +++ b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/TestUtils.java @@ -402,7 +402,7 @@ public final class TestUtils { return interfaceBuilder.toInstance(); } - static NormalizedNode prepareNormalizedNodeWithIetfInterfacesInterfacesData() throws ParseException { + static NormalizedNode prepareNormalizedNodeWithIetfInterfacesInterfacesData() throws ParseException { String ietfInterfacesDate = "2013-07-04"; String namespace = "urn:ietf:params:xml:ns:yang:ietf-interfaces"; DataContainerNodeAttrBuilder mapEntryNode = ImmutableMapEntryNodeBuilder.create(); diff --git a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/URITest.java b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/URITest.java index ed871bb527..4900e6a66f 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/URITest.java +++ b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/URITest.java @@ -26,7 +26,7 @@ import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint; import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService; import org.opendaylight.controller.sal.restconf.impl.BrokerFacade; import org.opendaylight.controller.sal.restconf.impl.ControllerContext; -import org.opendaylight.controller.sal.restconf.impl.InstanceIdWithSchemaNode; +import org.opendaylight.controller.sal.restconf.impl.InstanceIdentifierContext; import org.opendaylight.controller.sal.restconf.impl.RestconfDocumentedException; import org.opendaylight.controller.sal.restconf.impl.RestconfImpl; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; @@ -51,7 +51,7 @@ public class URITest { @Test public void testToInstanceIdentifierList() throws FileNotFoundException { - InstanceIdWithSchemaNode instanceIdentifier = controllerContext + InstanceIdentifierContext instanceIdentifier = controllerContext .toInstanceIdentifier("simple-nodes:userWithoutClass/foo"); assertEquals(instanceIdentifier.getSchemaNode().getQName().getLocalName(), "userWithoutClass"); @@ -80,7 +80,7 @@ public class URITest { @Test public void testToInstanceIdentifierContainer() throws FileNotFoundException { - InstanceIdWithSchemaNode instanceIdentifier = controllerContext.toInstanceIdentifier("simple-nodes:users"); + InstanceIdentifierContext instanceIdentifier = controllerContext.toInstanceIdentifier("simple-nodes:users"); assertEquals(instanceIdentifier.getSchemaNode().getQName().getLocalName(), "users"); assertTrue(instanceIdentifier.getSchemaNode() instanceof ContainerSchemaNode); assertEquals(2, ((ContainerSchemaNode) instanceIdentifier.getSchemaNode()).getChildNodes().size()); @@ -88,7 +88,7 @@ public class URITest { @Test public void testToInstanceIdentifierChoice() throws FileNotFoundException { - InstanceIdWithSchemaNode instanceIdentifier = controllerContext + InstanceIdentifierContext instanceIdentifier = controllerContext .toInstanceIdentifier("simple-nodes:food/nonalcoholic"); assertEquals(instanceIdentifier.getSchemaNode().getQName().getLocalName(), "nonalcoholic"); } @@ -120,7 +120,7 @@ public class URITest { @Test public void testMountPointWithExternModul() throws FileNotFoundException { initMountService(true); - InstanceIdWithSchemaNode instanceIdentifier = controllerContext + InstanceIdentifierContext instanceIdentifier = controllerContext .toInstanceIdentifier("simple-nodes:users/yang-ext:mount/test-interface2:class/student/name"); assertEquals( "[(urn:ietf:params:xml:ns:yang:test-interface2?revision=2014-08-01)class, (urn:ietf:params:xml:ns:yang:test-interface2?revision=2014-08-01)student[{(urn:ietf:params:xml:ns:yang:test-interface2?revision=2014-08-01)name=name}]]", @@ -130,7 +130,7 @@ public class URITest { @Test public void testMountPointWithoutExternModul() throws FileNotFoundException { initMountService(true); - InstanceIdWithSchemaNode instanceIdentifier = controllerContext + InstanceIdentifierContext instanceIdentifier = controllerContext .toInstanceIdentifier("simple-nodes:users/yang-ext:mount/"); assertTrue(Iterables.isEmpty(instanceIdentifier.getInstanceIdentifier().getPathArguments())); } @@ -140,7 +140,7 @@ public class URITest { exception.expect(RestconfDocumentedException.class); controllerContext.setMountService(null); - InstanceIdWithSchemaNode instanceIdentifier = controllerContext + InstanceIdentifierContext instanceIdentifier = controllerContext .toInstanceIdentifier("simple-nodes:users/yang-ext:mount/test-interface2:class/student/name"); } @@ -149,7 +149,7 @@ public class URITest { initMountService(false); exception.expect(RestconfDocumentedException.class); - InstanceIdWithSchemaNode instanceIdentifier = controllerContext + InstanceIdentifierContext instanceIdentifier = controllerContext .toInstanceIdentifier("simple-nodes:users/yang-ext:mount/test-interface2:class"); } diff --git a/opendaylight/md-sal/sal-rest-docgen/src/main/resources/WEB-INF/web.xml b/opendaylight/md-sal/sal-rest-docgen/src/main/resources/WEB-INF/web.xml index 2a778180d4..c470b3237e 100644 --- a/opendaylight/md-sal/sal-rest-docgen/src/main/resources/WEB-INF/web.xml +++ b/opendaylight/md-sal/sal-rest-docgen/src/main/resources/WEB-INF/web.xml @@ -2,7 +2,9 @@ - + + index.html + JAXRSApiDoc com.sun.jersey.spi.container.servlet.ServletContainer diff --git a/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/index.html b/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/index.html index 05a76a4cb7..f12616bc4b 100644 --- a/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/index.html +++ b/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/index.html @@ -32,7 +32,7 @@ var loadMount = function(mountIndex, mountPath) { $("#message").empty(); $("#message").append( "

Loading...

" ); - loadSwagger("http://localhost:8080/apidoc/apis/mounts/" + mountIndex, + loadSwagger("/apidoc/apis/mounts/" + mountIndex, "swagger-ui-container"); $("#message").empty(); $("#message").append( "

Showing mount points for " + mountPath + "

"); @@ -48,7 +48,7 @@ //loads the root swagger documenation (which comes from RestConf) var loadRootSwagger = function() { $("#message").empty(); - loadSwagger("http://localhost:8080/apidoc/apis", "swagger-ui-container"); + loadSwagger("/apidoc/apis", "swagger-ui-container"); } //main method to initialize the mount list / swagger docs / tabs on page load diff --git a/opendaylight/md-sal/statistics-manager/src/main/java/org/opendaylight/controller/md/statistics/manager/AbstractStatsTracker.java b/opendaylight/md-sal/statistics-manager/src/main/java/org/opendaylight/controller/md/statistics/manager/AbstractStatsTracker.java index e23ad0acdb..838aeb7dc3 100644 --- a/opendaylight/md-sal/statistics-manager/src/main/java/org/opendaylight/controller/md/statistics/manager/AbstractStatsTracker.java +++ b/opendaylight/md-sal/statistics-manager/src/main/java/org/opendaylight/controller/md/statistics/manager/AbstractStatsTracker.java @@ -97,14 +97,15 @@ abstract class AbstractStatsTracker { } protected abstract void cleanupSingleStat(DataModificationTransaction trans, K item); protected abstract K updateSingleStat(DataModificationTransaction trans, I item); + protected abstract K createInvariantKey(K item); public abstract void request(); public final synchronized void updateStats(List list) { final DataModificationTransaction trans = startTransaction(); - for (final I item : list) { - trackedItems.put(updateSingleStat(trans, item), requestCounter); + K key = updateSingleStat(trans, item); + trackedItems.put(createInvariantKey(key), requestCounter); } trans.commit(); diff --git a/opendaylight/md-sal/statistics-manager/src/main/java/org/opendaylight/controller/md/statistics/manager/FlowStatsEntry.java b/opendaylight/md-sal/statistics-manager/src/main/java/org/opendaylight/controller/md/statistics/manager/FlowStatsEntry.java index b5b39d94d2..c43c1ebaca 100644 --- a/opendaylight/md-sal/statistics-manager/src/main/java/org/opendaylight/controller/md/statistics/manager/FlowStatsEntry.java +++ b/opendaylight/md-sal/statistics-manager/src/main/java/org/opendaylight/controller/md/statistics/manager/FlowStatsEntry.java @@ -56,4 +56,9 @@ final class FlowStatsEntry { return false; return true; } + + @Override + public String toString() { + return "FlowStatsEntry [tableId=" + tableId + ", flow=" + flow + "]"; + } } diff --git a/opendaylight/md-sal/statistics-manager/src/main/java/org/opendaylight/controller/md/statistics/manager/FlowStatsTracker.java b/opendaylight/md-sal/statistics-manager/src/main/java/org/opendaylight/controller/md/statistics/manager/FlowStatsTracker.java index e92d0bd625..d540f115c3 100644 --- a/opendaylight/md-sal/statistics-manager/src/main/java/org/opendaylight/controller/md/statistics/manager/FlowStatsTracker.java +++ b/opendaylight/md-sal/statistics-manager/src/main/java/org/opendaylight/controller/md/statistics/manager/FlowStatsTracker.java @@ -301,4 +301,13 @@ final class FlowStatsTracker extends AbstractListeningStatsTracker path = linkPath(link); - transaction.put(LogicalDatastoreType.OPERATIONAL, path, link); + transaction.merge(LogicalDatastoreType.OPERATIONAL, path, link, true); } }); } 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 d4073e5c17..bd931c33d0 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 @@ -7,47 +7,209 @@ */ package org.opendaylight.controller.netconf.it; -import java.net.InetSocketAddress; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +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; +import io.netty.channel.ChannelFuture; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.local.LocalAddress; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.util.HashedWheelTimer; +import io.netty.util.concurrent.GlobalEventExecutor; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import org.apache.commons.io.IOUtils; import org.junit.After; import org.junit.Before; import org.opendaylight.controller.config.manager.impl.AbstractConfigTest; +import org.opendaylight.controller.config.manager.impl.factoriesresolver.HardcodedModuleFactoriesResolver; +import org.opendaylight.controller.config.spi.ModuleFactory; +import org.opendaylight.controller.config.yang.test.impl.DepTestImplModuleFactory; +import org.opendaylight.controller.config.yang.test.impl.IdentityTestModuleFactory; +import org.opendaylight.controller.config.yang.test.impl.MultipleDependenciesModuleFactory; +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.client.NetconfClientDispatcherImpl; import org.opendaylight.controller.netconf.client.SimpleNetconfClientSessionListener; import org.opendaylight.controller.netconf.client.conf.NetconfClientConfiguration; import org.opendaylight.controller.netconf.client.conf.NetconfClientConfigurationBuilder; +import org.opendaylight.controller.netconf.confignetconfconnector.osgi.NetconfOperationServiceFactoryImpl; +import org.opendaylight.controller.netconf.confignetconfconnector.osgi.YangStoreException; +import org.opendaylight.controller.netconf.confignetconfconnector.osgi.YangStoreService; +import org.opendaylight.controller.netconf.confignetconfconnector.osgi.YangStoreServiceImpl; +import org.opendaylight.controller.netconf.confignetconfconnector.osgi.YangStoreSnapshot; import org.opendaylight.controller.netconf.impl.DefaultCommitNotificationProducer; import org.opendaylight.controller.netconf.impl.NetconfServerDispatcher; 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.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.util.test.XmlFileLoader; import org.opendaylight.protocol.framework.NeverReconnectStrategy; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.opendaylight.yangtools.yang.model.api.SchemaContextProvider; +import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl; +import org.w3c.dom.Element; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.nio.NioEventLoopGroup; -import io.netty.util.HashedWheelTimer; -import io.netty.util.concurrent.GlobalEventExecutor; +public abstract class AbstractNetconfConfigTest extends AbstractConfigTest { -public class AbstractNetconfConfigTest extends AbstractConfigTest { + public static final String LOOPBACK_ADDRESS = "127.0.0.1"; + public static final int SERVER_CONNECTION_TIMEOUT_MILLIS = 5000; + + static ModuleFactory[] FACTORIES = { new TestImplModuleFactory(), + new DepTestImplModuleFactory(), new NetconfTestImplModuleFactory(), + new IdentityTestModuleFactory(), new MultipleDependenciesModuleFactory() }; private EventLoopGroup nettyThreadgroup; private HashedWheelTimer hashedWheelTimer; + private NetconfClientDispatcherImpl clientDispatcher; + private Channel serverTcpChannel; + + private NetconfMessage getConfig; + private NetconfMessage get; + + /** + * @Before in subclasses is called after this method. + */ @Before - public void setUpAbstractNetconfConfigTest() { + public void setUpAbstractNetconfConfigTest() throws Exception { + super.initConfigTransactionManagerImpl(new HardcodedModuleFactoriesResolver(mockedContext, FACTORIES)); + nettyThreadgroup = new NioEventLoopGroup(); hashedWheelTimer = new HashedWheelTimer(); + + loadMessages(); + + setUpTestInitial(); + + final NetconfOperationServiceFactoryListenerImpl factoriesListener = new NetconfOperationServiceFactoryListenerImpl(); + factoriesListener.onAddNetconfOperationServiceFactory(new NetconfOperationServiceFactoryImpl(getYangStore())); + + for (final NetconfOperationServiceFactory netconfOperationServiceFactory : getAdditionalServiceFactories()) { + factoriesListener.onAddNetconfOperationServiceFactory(netconfOperationServiceFactory); + } + + serverTcpChannel = startNetconfTcpServer(factoriesListener); + clientDispatcher = new NetconfClientDispatcherImpl(getNettyThreadgroup(), getNettyThreadgroup(), getHashedWheelTimer()); + } + + /** + * Called before setUp method is executed, so test classes can set up resources before setUpAbstractNetconfConfigTest method is called. + */ + protected void setUpTestInitial() throws Exception {} + + private void loadMessages() throws Exception { + this.getConfig = XmlFileLoader.xmlFileToNetconfMessage("netconfMessages/getConfig.xml"); + this.get = XmlFileLoader.xmlFileToNetconfMessage("netconfMessages/get.xml"); + } + + public NetconfMessage getGetConfig() { + return getConfig; + } + + public NetconfMessage getGet() { + return get; + } + + private Channel startNetconfTcpServer(final NetconfOperationServiceFactoryListenerImpl factoriesListener) throws Exception { + final NetconfServerDispatcher dispatch = createDispatcher(factoriesListener, getNetconfMonitoringService(), getNotificationProducer()); + + final ChannelFuture s; + if(getTcpServerAddress() instanceof LocalAddress) { + s = dispatch.createLocalServer(((LocalAddress) getTcpServerAddress())); + } else { + s = dispatch.createServer(((InetSocketAddress) getTcpServerAddress())); + } + s.await(); + return s.channel(); + } + + protected DefaultCommitNotificationProducer getNotificationProducer() { + final DefaultCommitNotificationProducer notificationProducer = mock(DefaultCommitNotificationProducer.class); + doNothing().when(notificationProducer).close(); + doNothing().when(notificationProducer).sendCommitNotification(anyString(), any(Element.class), anySetOf(String.class)); + return notificationProducer; + } + + protected Iterable getAdditionalServiceFactories() { + 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 abstract SocketAddress getTcpServerAddress(); + + public NetconfClientDispatcherImpl getClientDispatcher() { + return clientDispatcher; + } + + private HardcodedYangStoreService getYangStore() throws YangStoreException, IOException { + final Collection yangDependencies = getBasicYangs(); + return new HardcodedYangStoreService(yangDependencies); + } + + static Collection getBasicYangs() throws IOException { + + final List paths = Arrays.asList( + "/META-INF/yang/config.yang", + "/META-INF/yang/rpc-context.yang", + "/META-INF/yang/config-test.yang", + "/META-INF/yang/config-test-impl.yang", + "/META-INF/yang/test-types.yang", + "/META-INF/yang/ietf-inet-types.yang"); + + final Collection yangDependencies = new ArrayList<>(); + final List failedToFind = new ArrayList<>(); + for (final String path : paths) { + final InputStream resourceAsStream = NetconfITTest.class.getResourceAsStream(path); + if (resourceAsStream == null) { + failedToFind.add(path); + } else { + yangDependencies.add(resourceAsStream); + } + } + assertEquals("Some yang files were not found", Collections.emptyList(), failedToFind); + return yangDependencies; } protected NetconfServerDispatcher createDispatcher( - NetconfOperationServiceFactoryListenerImpl factoriesListener, SessionMonitoringService sessionMonitoringService, - DefaultCommitNotificationProducer commitNotifier) { - SessionIdProvider idProvider = new SessionIdProvider(); + final NetconfOperationServiceFactoryListenerImpl factoriesListener, final SessionMonitoringService sessionMonitoringService, + final DefaultCommitNotificationProducer commitNotifier) { + final SessionIdProvider idProvider = new SessionIdProvider(); - NetconfServerSessionNegotiatorFactory serverNegotiatorFactory = new NetconfServerSessionNegotiatorFactory( - hashedWheelTimer, factoriesListener, idProvider, 5000, commitNotifier, sessionMonitoringService); + final NetconfServerSessionNegotiatorFactory serverNegotiatorFactory = new NetconfServerSessionNegotiatorFactory( + hashedWheelTimer, factoriesListener, idProvider, SERVER_CONNECTION_TIMEOUT_MILLIS, commitNotifier, sessionMonitoringService); - NetconfServerDispatcher.ServerChannelInitializer serverChannelInitializer = new NetconfServerDispatcher.ServerChannelInitializer( + final NetconfServerDispatcher.ServerChannelInitializer serverChannelInitializer = new NetconfServerDispatcher.ServerChannelInitializer( serverNegotiatorFactory); return new NetconfServerDispatcher(serverChannelInitializer, nettyThreadgroup, nettyThreadgroup); } @@ -60,19 +222,58 @@ public class AbstractNetconfConfigTest extends AbstractConfigTest { return nettyThreadgroup; } + /** + * @After in subclasses is be called before this. + */ @After - public void cleanUpTimer() { + public void cleanUpNetconf() throws Exception { + serverTcpChannel.close().await(); hashedWheelTimer.stop(); - nettyThreadgroup.shutdownGracefully(); + nettyThreadgroup.shutdownGracefully().await(); } public NetconfClientConfiguration getClientConfiguration(final InetSocketAddress tcpAddress, final int timeout) { final NetconfClientConfigurationBuilder b = NetconfClientConfigurationBuilder.create(); b.withAddress(tcpAddress); b.withSessionListener(new SimpleNetconfClientSessionListener()); - b.withReconnectStrategy(new NeverReconnectStrategy(GlobalEventExecutor.INSTANCE, - timeout)); + b.withReconnectStrategy(new NeverReconnectStrategy(GlobalEventExecutor.INSTANCE, timeout)); b.withConnectionTimeoutMillis(timeout); return b.build(); } + + public static final class HardcodedYangStoreService implements YangStoreService { + + private final List byteArrayInputStreams; + + public HardcodedYangStoreService(final Collection inputStreams) throws YangStoreException, IOException { + byteArrayInputStreams = new ArrayList<>(); + for (final InputStream inputStream : inputStreams) { + assertNotNull(inputStream); + final byte[] content = IOUtils.toByteArray(inputStream); + final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(content); + byteArrayInputStreams.add(byteArrayInputStream); + } + } + + @Override + public YangStoreSnapshot getYangStoreSnapshot() throws YangStoreException { + for (final InputStream inputStream : byteArrayInputStreams) { + try { + inputStream.reset(); + } catch (final IOException e) { + throw new RuntimeException(e); + } + } + + final YangParserImpl yangParser = new YangParserImpl(); + final SchemaContext schemaContext = yangParser.resolveSchemaContext(new HashSet<>(yangParser.parseYangModelsFromStreamsMapped(byteArrayInputStreams).values())); + final YangStoreServiceImpl yangStoreService = new YangStoreServiceImpl(new SchemaContextProvider() { + @Override + public SchemaContext getSchemaContext() { + return schemaContext ; + } + }); + return yangStoreService.getYangStoreSnapshot(); + } + } } diff --git a/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/HardcodedYangStoreService.java b/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/HardcodedYangStoreService.java deleted file mode 100644 index 3fa1b0195a..0000000000 --- a/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/HardcodedYangStoreService.java +++ /dev/null @@ -1,66 +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.it; - -import static org.junit.Assert.assertNotNull; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashSet; -import java.util.List; - -import org.apache.commons.io.IOUtils; -import org.opendaylight.controller.netconf.confignetconfconnector.osgi.YangStoreException; -import org.opendaylight.controller.netconf.confignetconfconnector.osgi.YangStoreService; -import org.opendaylight.controller.netconf.confignetconfconnector.osgi.YangStoreServiceImpl; -import org.opendaylight.controller.netconf.confignetconfconnector.osgi.YangStoreSnapshot; -import org.opendaylight.yangtools.yang.model.api.SchemaContext; -import org.opendaylight.yangtools.yang.model.api.SchemaContextProvider; -import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl; - -public class HardcodedYangStoreService implements YangStoreService { - - private final List byteArrayInputStreams; - - public HardcodedYangStoreService( - Collection inputStreams) - throws YangStoreException, IOException { - byteArrayInputStreams = new ArrayList<>(); - for (InputStream inputStream : inputStreams) { - assertNotNull(inputStream); - byte[] content = IOUtils.toByteArray(inputStream); - ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream( - content); - byteArrayInputStreams.add(byteArrayInputStream); - } - } - - @Override - public YangStoreSnapshot getYangStoreSnapshot() throws YangStoreException { - for (InputStream inputStream : byteArrayInputStreams) { - try { - inputStream.reset(); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - YangParserImpl yangParser = new YangParserImpl(); - final SchemaContext schemaContext = yangParser.resolveSchemaContext(new HashSet<>(yangParser.parseYangModelsFromStreamsMapped(byteArrayInputStreams).values())); - YangStoreServiceImpl yangStoreService = new YangStoreServiceImpl(new SchemaContextProvider() { - @Override - public SchemaContext getSchemaContext() { - return schemaContext ; - } - }); - return yangStoreService.getYangStoreSnapshot(); - } -} 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 eb99be0dc0..4b49c0928b 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 @@ -11,7 +11,6 @@ import static junit.framework.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.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.opendaylight.controller.netconf.util.test.XmlUnitUtil.assertContainsElementWithName; @@ -20,41 +19,32 @@ import static org.opendaylight.controller.netconf.util.xml.XmlUtil.readXmlToDocu import com.google.common.collect.Lists; import com.google.common.collect.Sets; -import io.netty.channel.ChannelFuture; import java.io.IOException; -import java.io.InputStream; +import java.lang.management.ManagementFactory; import java.net.InetSocketAddress; -import java.util.Collection; +import java.net.SocketAddress; +import java.util.Collections; import java.util.List; import java.util.Set; import javax.management.InstanceNotFoundException; import javax.management.Notification; import javax.management.NotificationListener; -import org.junit.After; -import org.junit.Before; import org.junit.Test; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; -import org.opendaylight.controller.config.manager.impl.factoriesresolver.HardcodedModuleFactoriesResolver; import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder; import org.opendaylight.controller.config.persist.api.Persister; import org.opendaylight.controller.netconf.api.NetconfMessage; import org.opendaylight.controller.netconf.api.jmx.CommitJMXNotification; -import org.opendaylight.controller.netconf.api.monitoring.NetconfManagementSession; -import org.opendaylight.controller.netconf.client.NetconfClientDispatcher; -import org.opendaylight.controller.netconf.client.NetconfClientDispatcherImpl; import org.opendaylight.controller.netconf.client.test.TestingNetconfClient; -import org.opendaylight.controller.netconf.confignetconfconnector.osgi.NetconfOperationServiceFactoryImpl; -import org.opendaylight.controller.netconf.confignetconfconnector.osgi.YangStoreException; import org.opendaylight.controller.netconf.impl.DefaultCommitNotificationProducer; -import org.opendaylight.controller.netconf.impl.NetconfServerDispatcher; 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.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; @@ -65,64 +55,48 @@ import org.xml.sax.SAXException; public class NetconfConfigPersisterITTest extends AbstractNetconfConfigTest { - private static final InetSocketAddress tcpAddress = new InetSocketAddress("127.0.0.1", 12023); + public static final int PORT = 12026; + private static final InetSocketAddress TCP_ADDRESS = new InetSocketAddress(LOOPBACK_ADDRESS, PORT); - private NetconfClientDispatcher clientDispatcher; - private DefaultCommitNotificationProducer commitNotifier; + private NetconfMonitoringServiceImpl netconfMonitoringService; - @Before - public void setUp() throws Exception { - super.initConfigTransactionManagerImpl(new HardcodedModuleFactoriesResolver(mockedContext,NetconfITTest.FACTORIES)); - - NetconfMonitoringServiceImpl monitoringService = new NetconfMonitoringServiceImpl(getNetconfOperationProvider()); - - NetconfOperationServiceFactoryListenerImpl factoriesListener = new NetconfOperationServiceFactoryListenerImpl(); - factoriesListener.onAddNetconfOperationServiceFactory(new NetconfOperationServiceFactoryImpl(getYangStore())); - factoriesListener - .onAddNetconfOperationServiceFactory(new NetconfMonitoringActivator.NetconfMonitoringOperationServiceFactory( - new NetconfMonitoringOperationService(monitoringService))); - - - commitNotifier = new DefaultCommitNotificationProducer(platformMBeanServer); - NetconfServerDispatcher dispatch = createDispatcher(factoriesListener, mockSessionMonitoringService(), commitNotifier); - ChannelFuture s = dispatch.createServer(tcpAddress); - s.await(); - - clientDispatcher = new NetconfClientDispatcherImpl(getNettyThreadgroup(), getNettyThreadgroup(), getHashedWheelTimer()); + @Override + protected void setUpTestInitial() { + netconfMonitoringService = new NetconfMonitoringServiceImpl(getNetconfOperationProvider()); } - @After - public void cleanUp(){ - commitNotifier.close(); + @Override + protected SessionMonitoringService getNetconfMonitoringService() throws Exception { + return netconfMonitoringService; } - private HardcodedYangStoreService getYangStore() throws YangStoreException, IOException { - final Collection yangDependencies = NetconfITTest.getBasicYangs(); - return new HardcodedYangStoreService(yangDependencies); + @Override + protected SocketAddress getTcpServerAddress() { + return TCP_ADDRESS; } - - protected SessionMonitoringService mockSessionMonitoringService() { - SessionMonitoringService mockedSessionMonitor = mock(SessionMonitoringService.class); - doNothing().when(mockedSessionMonitor).onSessionUp(any(NetconfManagementSession.class)); - doNothing().when(mockedSessionMonitor).onSessionDown(any(NetconfManagementSession.class)); - return mockedSessionMonitor; + @Override + protected Iterable getAdditionalServiceFactories() { + return Collections.singletonList(new NetconfMonitoringActivator.NetconfMonitoringOperationServiceFactory( + new NetconfMonitoringOperationService(netconfMonitoringService))); } - + @Override + protected DefaultCommitNotificationProducer getNotificationProducer() { + return new DefaultCommitNotificationProducer(ManagementFactory.getPlatformMBeanServer()); + } @Test public void testNetconfCommitNotifications() throws Exception { + final VerifyingNotificationListener notificationVerifier = createCommitNotificationListener(); + final VerifyingPersister mockedAggregator = mockAggregator(); - VerifyingNotificationListener notificationVerifier = createCommitNotificationListener(); - VerifyingPersister mockedAggregator = mockAggregator(); - - try (TestingNetconfClient persisterClient = new TestingNetconfClient("persister", clientDispatcher, getClientConfiguration(tcpAddress, 4000))) { + try (TestingNetconfClient persisterClient = new TestingNetconfClient("persister", getClientDispatcher(), getClientConfiguration(TCP_ADDRESS, 4000))) { try (ConfigPersisterNotificationHandler configPersisterNotificationHandler = new ConfigPersisterNotificationHandler( platformMBeanServer, mockedAggregator)) { - try (TestingNetconfClient netconfClient = new TestingNetconfClient("client", clientDispatcher, getClientConfiguration(tcpAddress, 4000))) { + try (TestingNetconfClient netconfClient = new TestingNetconfClient("client", getClientDispatcher(), getClientConfiguration(TCP_ADDRESS, 4000))) { NetconfMessage response = netconfClient.sendMessage(loadGetConfigMessage()); assertContainsElementWithName(response.getDocument(), "modules"); assertContainsElementWithName(response.getDocument(), "services"); @@ -152,7 +126,7 @@ public class NetconfConfigPersisterITTest extends AbstractNetconfConfigTest { } private VerifyingNotificationListener createCommitNotificationListener() throws InstanceNotFoundException { - VerifyingNotificationListener listener = new VerifyingNotificationListener(); + final VerifyingNotificationListener listener = new VerifyingNotificationListener(); platformMBeanServer.addNotificationListener(DefaultCommitNotificationProducer.OBJECT_NAME, listener, null, null); return listener; } @@ -171,12 +145,12 @@ public class NetconfConfigPersisterITTest extends AbstractNetconfConfigTest { public NetconfOperationProvider getNetconfOperationProvider() { - NetconfOperationProvider factoriesListener = mock(NetconfOperationProvider.class); - NetconfOperationServiceSnapshotImpl snap = mock(NetconfOperationServiceSnapshotImpl.class); - NetconfOperationService service = mock(NetconfOperationService.class); - Set caps = Sets.newHashSet(); + 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(); - Set services = Sets.newHashSet(service); + final Set services = Sets.newHashSet(service); doReturn(services).when(snap).getServices(); doReturn(snap).when(factoriesListener).openSnapshot(anyString()); @@ -187,23 +161,23 @@ public class NetconfConfigPersisterITTest extends AbstractNetconfConfigTest { public List notifications = Lists.newArrayList(); @Override - public void handleNotification(Notification notification, Object handback) { + public void handleNotification(final Notification notification, final Object handback) { this.notifications.add(notification); } - void assertNotificationCount(Object size) { + void assertNotificationCount(final Object size) { assertEquals(size, notifications.size()); } - void assertNotificationContent(int notificationIndex, int expectedModulesSize, int expectedServicesSize, int expectedCapsSize) { - Notification notification = notifications.get(notificationIndex); + void assertNotificationContent(final int notificationIndex, final int expectedModulesSize, final int expectedServicesSize, final int expectedCapsSize) { + final Notification notification = notifications.get(notificationIndex); assertEquals(CommitJMXNotification.class, notification.getClass()); - int capsSize = ((CommitJMXNotification) notification).getCapabilities().size(); + final int capsSize = ((CommitJMXNotification) notification).getCapabilities().size(); assertEquals("Expected capabilities count", expectedCapsSize, capsSize); - Element configSnapshot = ((CommitJMXNotification) notification).getConfigSnapshot(); - int modulesSize = configSnapshot.getElementsByTagName("module").getLength(); + final Element configSnapshot = ((CommitJMXNotification) notification).getConfigSnapshot(); + final int modulesSize = configSnapshot.getElementsByTagName("module").getLength(); assertEquals("Expected modules count", expectedModulesSize, modulesSize); - int servicesSize = configSnapshot.getElementsByTagName("instance").getLength(); + final int servicesSize = configSnapshot.getElementsByTagName("instance").getLength(); assertEquals("Expected services count", expectedServicesSize, servicesSize); } } @@ -214,12 +188,12 @@ public class NetconfConfigPersisterITTest extends AbstractNetconfConfigTest { private Persister mockedPersister; public VerifyingPersister() throws IOException { - Persister mockedAggregator = mock(Persister.class); + final Persister mockedAggregator = mock(Persister.class); doAnswer(new Answer() { @Override - public Object answer(InvocationOnMock invocation) throws Throwable { - ConfigSnapshotHolder configSnapshot = (ConfigSnapshotHolder) invocation.getArguments()[0]; + public Object answer(final InvocationOnMock invocation) throws Throwable { + final ConfigSnapshotHolder configSnapshot = (ConfigSnapshotHolder) invocation.getArguments()[0]; snapshots.add(configSnapshot); return null; } @@ -228,22 +202,22 @@ public class NetconfConfigPersisterITTest extends AbstractNetconfConfigTest { this.mockedPersister = mockedAggregator; } - void assertSnapshotCount(Object size) { + void assertSnapshotCount(final Object size) { assertEquals(size, snapshots.size()); } - void assertSnapshotContent(int notificationIndex, int expectedModulesSize, int expectedServicesSize, int expectedCapsSize) + void assertSnapshotContent(final int notificationIndex, final int expectedModulesSize, final int expectedServicesSize, final int expectedCapsSize) throws SAXException, IOException { - ConfigSnapshotHolder snapshot = snapshots.get(notificationIndex); - int capsSize = snapshot.getCapabilities().size(); + final ConfigSnapshotHolder snapshot = snapshots.get(notificationIndex); + final int capsSize = snapshot.getCapabilities().size(); assertEquals("Expected capabilities count", expectedCapsSize, capsSize); - Document configSnapshot = readXmlToDocument(snapshot.getConfigSnapshot()); + final Document configSnapshot = readXmlToDocument(snapshot.getConfigSnapshot()); assertElementsCount(configSnapshot, "module", expectedModulesSize); assertElementsCount(configSnapshot, "instance", expectedServicesSize); } @Override - public void persistConfig(ConfigSnapshotHolder configSnapshotHolder) throws IOException { + public void persistConfig(final ConfigSnapshotHolder configSnapshotHolder) throws IOException { mockedPersister.persistConfig(configSnapshotHolder); } 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 new file mode 100644 index 0000000000..72a2f8f7ac --- /dev/null +++ b/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITMonitoringTest.java @@ -0,0 +1,206 @@ +/* + * 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.it; + +import static org.junit.Assert.assertEquals; +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 static org.opendaylight.controller.netconf.util.test.XmlUnitUtil.assertContainsElementWithText; + +import com.google.common.base.Charsets; +import com.google.common.base.Optional; +import com.google.common.collect.Sets; +import java.io.BufferedReader; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import org.junit.Test; +import org.opendaylight.controller.netconf.api.NetconfMessage; +import org.opendaylight.controller.netconf.api.monitoring.NetconfManagementSession; +import org.opendaylight.controller.netconf.client.test.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.mapping.api.NetconfOperationService; +import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactory; +import org.opendaylight.controller.netconf.monitoring.osgi.NetconfMonitoringActivator; +import org.opendaylight.controller.netconf.monitoring.osgi.NetconfMonitoringOperationService; +import org.opendaylight.controller.netconf.util.test.XmlFileLoader; +import org.opendaylight.controller.netconf.util.xml.XmlUtil; +import org.slf4j.Logger; +import org.w3c.dom.Document; + +public class NetconfITMonitoringTest extends AbstractNetconfConfigTest { + + public static final int PORT = 12025; + 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 logger, final NetconfMonitoringServiceImpl monitor) { + return new SessionMonitoringService() { + @Override + public void onSessionUp(final NetconfManagementSession session) { + logger.debug("Management session up {}", session); + monitor.onSessionUp(session); + } + + @Override + public void onSessionDown(final NetconfManagementSession session) { + logger.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))) { + try (TestingNetconfClient netconfClient2 = new TestingNetconfClient("client-monitoring2", getClientDispatcher(), getClientConfiguration(TCP_ADDRESS, 10000))) { + Thread.sleep(500); + final NetconfMessage response = netconfClient2.sendMessage(getGet()); + assertSessionElementsInResponse(response.getDocument(), 2); + } + Thread.sleep(500); + final NetconfMessage response = netconfClient.sendMessage(getGet()); + assertSessionElementsInResponse(response.getDocument(), 1); + } + } + + + @Test(timeout = 13 * 10000) + public void testClientHelloWithAuth() throws Exception { + String fileName = "netconfMessages/client_hello_with_auth.xml"; + final String hello = XmlFileLoader.fileToString(fileName); + + fileName = "netconfMessages/get.xml"; + final String get = XmlFileLoader.fileToString(fileName); + + final Socket sock = new Socket(TCP_ADDRESS.getHostName(), TCP_ADDRESS.getPort()); + sock.getOutputStream().write(hello.getBytes(Charsets.UTF_8)); + final String separator = "]]>]]>"; + + sock.getOutputStream().write(separator.getBytes(Charsets.UTF_8)); + sock.getOutputStream().write(get.getBytes(Charsets.UTF_8)); + sock.getOutputStream().write(separator.getBytes(Charsets.UTF_8)); + + final StringBuilder responseBuilder = new StringBuilder(); + + try (InputStream inputStream = sock.getInputStream(); + InputStreamReader reader = new InputStreamReader(inputStream); + BufferedReader buff = new BufferedReader(reader)) { + String line; + while ((line = buff.readLine()) != null) { + + responseBuilder.append(line); + responseBuilder.append(System.lineSeparator()); + + if(line.contains("")) + break; + } + } + + sock.close(); + + final String helloMsg = responseBuilder.substring(0, responseBuilder.indexOf(separator)); + Document doc = XmlUtil.readXmlToDocument(helloMsg); + assertContainsElementWithText(doc, "urn:ietf:params:netconf:capability:candidate:1.0"); + + final String replyMsg = responseBuilder.substring(responseBuilder.indexOf(separator) + separator.length()); + doc = XmlUtil.readXmlToDocument(replyMsg); + assertContainsElementWithText(doc, "tomas"); + } + + private void assertSessionElementsInResponse(final Document document, final int i) { + final int elementSize = document.getElementsByTagName("session-id").getLength(); + 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); + 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()); + + return factoriesListener; + } + + private static class TestingCapability implements Capability { + @Override + public String getCapabilityUri() { + return "namespaceModuleRevision"; + } + + @Override + public Optional getModuleNamespace() { + return Optional.of("namespace"); + } + + @Override + public Optional getModuleName() { + return Optional.of("name"); + } + + @Override + public Optional getRevision() { + return Optional.of("revision"); + } + + @Override + public Optional getCapabilitySchema() { + return Optional.of("content"); + } + + @Override + public Optional> getLocation() { + return Optional.absent(); + } + } +} diff --git a/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITSecureTest.java b/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITSecureTest.java index e5e34548b3..67ccf0c02c 100644 --- a/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITSecureTest.java +++ b/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITSecureTest.java @@ -8,27 +8,25 @@ package org.opendaylight.controller.netconf.it; -import static java.util.Arrays.asList; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; +import com.google.common.collect.Lists; +import io.netty.channel.local.LocalAddress; +import io.netty.util.concurrent.Future; +import io.netty.util.concurrent.GenericFutureListener; +import io.netty.util.concurrent.GlobalEventExecutor; import java.io.IOException; -import java.io.InputStream; -import java.lang.management.ManagementFactory; import java.net.InetSocketAddress; -import java.util.Collection; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; - -import junit.framework.Assert; - import org.junit.After; import org.junit.Before; import org.junit.Test; -import org.opendaylight.controller.config.manager.impl.factoriesresolver.HardcodedModuleFactoriesResolver; -import org.opendaylight.controller.config.spi.ModuleFactory; import org.opendaylight.controller.netconf.api.NetconfMessage; import org.opendaylight.controller.netconf.auth.AuthProvider; import org.opendaylight.controller.netconf.client.NetconfClientDispatcher; @@ -37,87 +35,44 @@ import org.opendaylight.controller.netconf.client.SimpleNetconfClientSessionList import org.opendaylight.controller.netconf.client.conf.NetconfClientConfiguration; import org.opendaylight.controller.netconf.client.conf.NetconfClientConfigurationBuilder; import org.opendaylight.controller.netconf.client.test.TestingNetconfClient; -import org.opendaylight.controller.netconf.confignetconfconnector.osgi.NetconfOperationServiceFactoryImpl; -import org.opendaylight.controller.netconf.confignetconfconnector.osgi.YangStoreException; -import org.opendaylight.controller.netconf.impl.DefaultCommitNotificationProducer; -import org.opendaylight.controller.netconf.impl.NetconfServerDispatcher; -import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationServiceFactoryListenerImpl; import org.opendaylight.controller.netconf.nettyutil.handler.ssh.authentication.AuthenticationHandler; import org.opendaylight.controller.netconf.nettyutil.handler.ssh.authentication.LoginPassword; import org.opendaylight.controller.netconf.ssh.NetconfSSHServer; import org.opendaylight.controller.netconf.ssh.authentication.PEMGenerator; import org.opendaylight.controller.netconf.util.messages.NetconfMessageUtil; import org.opendaylight.controller.netconf.util.osgi.NetconfConfigUtil; -import org.opendaylight.controller.netconf.util.test.XmlFileLoader; import org.opendaylight.controller.netconf.util.xml.XmlUtil; import org.opendaylight.protocol.framework.NeverReconnectStrategy; -import com.google.common.collect.Lists; - -import io.netty.channel.ChannelFuture; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.nio.NioEventLoopGroup; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.GenericFutureListener; -import io.netty.util.concurrent.GlobalEventExecutor; - public class NetconfITSecureTest extends AbstractNetconfConfigTest { - private static final InetSocketAddress tlsAddress = new InetSocketAddress("127.0.0.1", 12024); + public static final int PORT = 12024; + private static final InetSocketAddress TLS_ADDRESS = new InetSocketAddress("127.0.0.1", PORT); + + public static final String USERNAME = "user"; + public static final String PASSWORD = "pwd"; - private DefaultCommitNotificationProducer commitNot; private NetconfSSHServer sshServer; - private NetconfMessage getConfig; @Before public void setUp() throws Exception { - this.getConfig = XmlFileLoader.xmlFileToNetconfMessage("netconfMessages/getConfig.xml"); - - super.initConfigTransactionManagerImpl(new HardcodedModuleFactoriesResolver(mockedContext, getModuleFactories().toArray( - new ModuleFactory[0]))); - - final NetconfOperationServiceFactoryListenerImpl factoriesListener = new NetconfOperationServiceFactoryListenerImpl(); - factoriesListener.onAddNetconfOperationServiceFactory(new NetconfOperationServiceFactoryImpl(getYangStore())); - - commitNot = new DefaultCommitNotificationProducer(ManagementFactory.getPlatformMBeanServer()); - - - final NetconfServerDispatcher dispatchS = createDispatcher(factoriesListener); - ChannelFuture s = dispatchS.createLocalServer(NetconfConfigUtil.getNetconfLocalAddress()); - s.await(); - EventLoopGroup bossGroup = new NioEventLoopGroup(); - final char[] pem = PEMGenerator.generate().toCharArray(); - sshServer = NetconfSSHServer.start(tlsAddress.getPort(), NetconfConfigUtil.getNetconfLocalAddress(), bossGroup, pem); + sshServer = NetconfSSHServer.start(TLS_ADDRESS.getPort(), NetconfConfigUtil.getNetconfLocalAddress(), getNettyThreadgroup(), pem); sshServer.setAuthProvider(getAuthProvider()); } - private NetconfServerDispatcher createDispatcher(final NetconfOperationServiceFactoryListenerImpl factoriesListener) { - return super.createDispatcher(factoriesListener, NetconfITTest.getNetconfMonitoringListenerService(), commitNot); - } - @After public void tearDown() throws Exception { sshServer.close(); - commitNot.close(); sshServer.join(); } - private HardcodedYangStoreService getYangStore() throws YangStoreException, IOException { - final Collection yangDependencies = NetconfITTest.getBasicYangs(); - return new HardcodedYangStoreService(yangDependencies); - } - - protected List getModuleFactories() { - return asList(NetconfITTest.FACTORIES); - } - @Test public void testSecure() throws Exception { final NetconfClientDispatcher dispatch = new NetconfClientDispatcherImpl(getNettyThreadgroup(), getNettyThreadgroup(), getHashedWheelTimer()); try (TestingNetconfClient netconfClient = new TestingNetconfClient("testing-ssh-client", dispatch, getClientConfiguration())) { - NetconfMessage response = netconfClient.sendMessage(getConfig); - Assert.assertFalse("Unexpected error message " + XmlUtil.toString(response.getDocument()), + NetconfMessage response = netconfClient.sendMessage(getGetConfig()); + assertFalse("Unexpected error message " + XmlUtil.toString(response.getDocument()), NetconfMessageUtil.isErrorMessage(response)); final NetconfMessage gs = new NetconfMessage(XmlUtil.readXmlToDocument("\n")); response = netconfClient.sendMessage(gs); - Assert.assertFalse("Unexpected error message " + XmlUtil.toString(response.getDocument()), + assertFalse("Unexpected error message " + XmlUtil.toString(response.getDocument()), NetconfMessageUtil.isErrorMessage(response)); } } @@ -146,7 +101,7 @@ public class NetconfITSecureTest extends AbstractNetconfConfigTest { final int requests = 1000; for (int i = 0; i < requests; i++) { - final Future netconfMessageFuture = netconfClient.sendRequest(getConfig); + final Future netconfMessageFuture = netconfClient.sendRequest(getGetConfig()); futures.add(netconfMessageFuture); netconfMessageFuture.addListener(new GenericFutureListener>() { @Override @@ -164,13 +119,13 @@ public class NetconfITSecureTest extends AbstractNetconfConfigTest { // Give future listeners some time to finish counter incrementation Thread.sleep(5000); - org.junit.Assert.assertEquals(requests, responseCounter.get()); + assertEquals(requests, responseCounter.get()); } } public NetconfClientConfiguration getClientConfiguration() throws IOException { final NetconfClientConfigurationBuilder b = NetconfClientConfigurationBuilder.create(); - b.withAddress(tlsAddress); + b.withAddress(TLS_ADDRESS); b.withSessionListener(new SimpleNetconfClientSessionListener()); b.withReconnectStrategy(new NeverReconnectStrategy(GlobalEventExecutor.INSTANCE, 5000)); b.withProtocol(NetconfClientConfiguration.NetconfClientProtocol.SSH); @@ -180,12 +135,18 @@ public class NetconfITSecureTest extends AbstractNetconfConfigTest { } public AuthProvider getAuthProvider() throws Exception { - AuthProvider mock = mock(AuthProvider.class); - doReturn(true).when(mock).authenticated(anyString(), anyString()); - return mock; + final AuthProvider mockAuth = mock(AuthProvider.class); + doReturn("mockedAuth").when(mockAuth).toString(); + doReturn(true).when(mockAuth).authenticated(anyString(), anyString()); + return mockAuth; } public AuthenticationHandler getAuthHandler() throws IOException { - return new LoginPassword("user", "pwd"); + return new LoginPassword(USERNAME, PASSWORD); + } + + @Override + protected LocalAddress getTcpServerAddress() { + return NetconfConfigUtil.getNetconfLocalAddress(); } } diff --git a/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITTest.java b/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITTest.java index 8e69e6a345..a7a9d7494a 100644 --- a/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITTest.java +++ b/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITTest.java @@ -8,11 +8,11 @@ package org.opendaylight.controller.netconf.it; +import static org.hamcrest.CoreMatchers.containsString; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; -import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; @@ -20,15 +20,8 @@ import com.google.common.base.Function; import com.google.common.base.Throwables; import com.google.common.collect.Lists; import com.google.common.collect.Sets; -import io.netty.channel.ChannelFuture; import java.io.IOException; -import java.io.InputStream; -import java.lang.management.ManagementFactory; import java.net.InetSocketAddress; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -36,35 +29,19 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeoutException; import javax.management.ObjectName; import javax.xml.parsers.ParserConfigurationException; -import org.junit.After; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; -import org.junit.matchers.JUnitMatchers; import org.opendaylight.controller.config.api.jmx.ObjectNameUtil; -import org.opendaylight.controller.config.manager.impl.factoriesresolver.HardcodedModuleFactoriesResolver; -import org.opendaylight.controller.config.spi.ModuleFactory; import org.opendaylight.controller.config.util.ConfigTransactionJMXClient; import org.opendaylight.controller.config.yang.test.impl.DepTestImplModuleFactory; -import org.opendaylight.controller.config.yang.test.impl.IdentityTestModuleFactory; import org.opendaylight.controller.config.yang.test.impl.MultipleDependenciesModuleFactory; import org.opendaylight.controller.config.yang.test.impl.MultipleDependenciesModuleMXBean; import org.opendaylight.controller.config.yang.test.impl.NetconfTestImplModuleFactory; import org.opendaylight.controller.config.yang.test.impl.NetconfTestImplModuleMXBean; -import org.opendaylight.controller.config.yang.test.impl.TestImplModuleFactory; import org.opendaylight.controller.netconf.api.NetconfDocumentedException; import org.opendaylight.controller.netconf.api.NetconfMessage; -import org.opendaylight.controller.netconf.client.NetconfClientDispatcherImpl; +import org.opendaylight.controller.netconf.client.NetconfClientDispatcher; import org.opendaylight.controller.netconf.client.test.TestingNetconfClient; -import org.opendaylight.controller.netconf.confignetconfconnector.osgi.NetconfOperationServiceFactoryImpl; -import org.opendaylight.controller.netconf.confignetconfconnector.osgi.YangStoreException; -import org.opendaylight.controller.netconf.impl.DefaultCommitNotificationProducer; -import org.opendaylight.controller.netconf.impl.NetconfServerDispatcher; -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.mapping.api.NetconfOperationProvider; -import org.opendaylight.controller.netconf.mapping.api.NetconfOperationService; import org.opendaylight.controller.netconf.util.test.XmlFileLoader; import org.opendaylight.controller.netconf.util.xml.XmlElement; import org.opendaylight.controller.netconf.util.xml.XmlUtil; @@ -80,129 +57,57 @@ import org.xml.sax.SAXException; public class NetconfITTest extends AbstractNetconfConfigTest { - // TODO refactor, pull common code up to AbstractNetconfITTest + public static final int PORT = 12023; + public static final InetSocketAddress TCP_ADDRESS = new InetSocketAddress(LOOPBACK_ADDRESS, PORT); - private static final InetSocketAddress tcpAddress = new InetSocketAddress("127.0.0.1", 12023); - - - private NetconfMessage getConfig, getConfigCandidate, editConfig, closeSession; - private DefaultCommitNotificationProducer commitNotificationProducer; - private NetconfServerDispatcher dispatch; - - private NetconfClientDispatcherImpl clientDispatcher; - - static ModuleFactory[] FACTORIES = {new TestImplModuleFactory(), new DepTestImplModuleFactory(), - new NetconfTestImplModuleFactory(), new IdentityTestModuleFactory(), - new MultipleDependenciesModuleFactory()}; + private NetconfMessage getConfigCandidate, editConfig, closeSession; + private NetconfClientDispatcher clientDispatcher; @Before public void setUp() throws Exception { - initConfigTransactionManagerImpl(new HardcodedModuleFactoriesResolver(mockedContext, - FACTORIES - )); - loadMessages(); - - NetconfOperationServiceFactoryListenerImpl factoriesListener = new NetconfOperationServiceFactoryListenerImpl(); - factoriesListener.onAddNetconfOperationServiceFactory(new NetconfOperationServiceFactoryImpl(getYangStore())); - - commitNotificationProducer = new DefaultCommitNotificationProducer(ManagementFactory.getPlatformMBeanServer()); - - dispatch = createDispatcher(factoriesListener); - ChannelFuture s = dispatch.createServer(tcpAddress); - s.await(); - - clientDispatcher = new NetconfClientDispatcherImpl(getNettyThreadgroup(), getNettyThreadgroup(), getHashedWheelTimer()); - } - - private NetconfServerDispatcher createDispatcher(NetconfOperationServiceFactoryListenerImpl factoriesListener) { - return super.createDispatcher(factoriesListener, getNetconfMonitoringListenerService(), commitNotificationProducer); + clientDispatcher = getClientDispatcher(); } - static NetconfMonitoringServiceImpl getNetconfMonitoringListenerService() { - NetconfOperationProvider netconfOperationProvider = mock(NetconfOperationProvider.class); - NetconfOperationServiceSnapshotImpl snap = mock(NetconfOperationServiceSnapshotImpl.class); - doReturn(Collections.emptySet()).when(snap).getServices(); - doReturn(snap).when(netconfOperationProvider).openSnapshot(anyString()); - return new NetconfMonitoringServiceImpl(netconfOperationProvider); - } - - @After - public void tearDown() throws Exception { - commitNotificationProducer.close(); - clientDispatcher.close(); + @Override + protected InetSocketAddress getTcpServerAddress() { + return TCP_ADDRESS; } private void loadMessages() throws IOException, SAXException, ParserConfigurationException { this.editConfig = XmlFileLoader.xmlFileToNetconfMessage("netconfMessages/edit_config.xml"); - this.getConfig = XmlFileLoader.xmlFileToNetconfMessage("netconfMessages/getConfig.xml"); this.getConfigCandidate = XmlFileLoader.xmlFileToNetconfMessage("netconfMessages/getConfig_candidate.xml"); this.closeSession = XmlFileLoader.xmlFileToNetconfMessage("netconfMessages/closeSession.xml"); } - private HardcodedYangStoreService getYangStore() throws YangStoreException, IOException { - final Collection yangDependencies = getBasicYangs(); - return new HardcodedYangStoreService(yangDependencies); - } - - static Collection getBasicYangs() throws IOException { - - List paths = Arrays.asList("/META-INF/yang/config.yang", "/META-INF/yang/rpc-context.yang", - "/META-INF/yang/config-test.yang", "/META-INF/yang/config-test-impl.yang", "/META-INF/yang/test-types.yang", - "/META-INF/yang/ietf-inet-types.yang"); - final Collection yangDependencies = new ArrayList<>(); - List failedToFind = new ArrayList<>(); - for (String path : paths) { - InputStream resourceAsStream = NetconfITTest.class.getResourceAsStream(path); - if (resourceAsStream == null) { - failedToFind.add(path); - } else { - yangDependencies.add(resourceAsStream); - } - } - assertEquals("Some yang files were not found", Collections.emptyList(), failedToFind); - return yangDependencies; - } - - @Test public void testNetconfClientDemonstration() throws Exception { - try (TestingNetconfClient netconfClient = new TestingNetconfClient("client", clientDispatcher, getClientConfiguration(tcpAddress, 4000))) { + try (TestingNetconfClient netconfClient = new TestingNetconfClient("client", clientDispatcher, getClientConfiguration(TCP_ADDRESS, 4000))) { - Set capabilitiesFromNetconfServer = netconfClient.getCapabilities(); - long sessionId = netconfClient.getSessionId(); + final Set capabilitiesFromNetconfServer = netconfClient.getCapabilities(); + final long sessionId = netconfClient.getSessionId(); // NetconfMessage can be created : // new NetconfMessage(XmlUtil.readXmlToDocument("")); - NetconfMessage response = netconfClient.sendMessage(getConfig); + final NetconfMessage response = netconfClient.sendMessage(getGetConfig()); response.getDocument(); } } @Test public void testTwoSessions() throws Exception { - try (TestingNetconfClient netconfClient = new TestingNetconfClient("1", clientDispatcher, getClientConfiguration(tcpAddress, 10000))) { - try (TestingNetconfClient netconfClient2 = new TestingNetconfClient("2", clientDispatcher, getClientConfiguration(tcpAddress, 10000))) { + try (TestingNetconfClient netconfClient = new TestingNetconfClient("1", clientDispatcher, getClientConfiguration(TCP_ADDRESS, 10000))) { + try (TestingNetconfClient netconfClient2 = new TestingNetconfClient("2", clientDispatcher, getClientConfiguration(TCP_ADDRESS, 10000))) { assertNotNull(netconfClient2.getCapabilities()); } } } - @Ignore - @Test - public void waitingTest() throws Exception { - final ConfigTransactionJMXClient transaction = this.configRegistryClient.createTransaction(); - transaction.createModule(DepTestImplModuleFactory.NAME, "eb"); - transaction.commit(); - Thread.currentThread().suspend(); - } - @Test public void rpcReplyContainsAllAttributesTest() throws Exception { - try (TestingNetconfClient netconfClient = createSession(tcpAddress, "1")) { - final String rpc = "" - + "" + ""; + try (TestingNetconfClient netconfClient = createSession(TCP_ADDRESS, "1")) { + final String rpc = "" + ""; final Document doc = XmlUtil.readXmlToDocument(rpc); final NetconfMessage message = netconfClient.sendMessage(new NetconfMessage(doc)); assertNotNull(message); @@ -228,9 +133,8 @@ public class NetconfITTest extends AbstractNetconfConfigTest { @Test public void rpcReplyErrorContainsAllAttributesTest() throws Exception { - try (TestingNetconfClient netconfClient = createSession(tcpAddress, "1")) { - final String rpc = "" - + "" + ""; + try (TestingNetconfClient netconfClient = createSession(TCP_ADDRESS, "1")) { + final String rpc = "" + ""; final Document doc = XmlUtil.readXmlToDocument(rpc); final NetconfMessage message = netconfClient.sendMessage(new NetconfMessage(doc)); final NamedNodeMap expectedAttributes = doc.getDocumentElement().getAttributes(); @@ -243,16 +147,16 @@ public class NetconfITTest extends AbstractNetconfConfigTest { @Test public void rpcOutputContainsCorrectNamespace() throws Exception { final ConfigTransactionJMXClient transaction = this.configRegistryClient.createTransaction(); - ObjectName dep = transaction.createModule(DepTestImplModuleFactory.NAME, "instanceD"); - ObjectName impl = transaction.createModule(NetconfTestImplModuleFactory.NAME, "instance"); - NetconfTestImplModuleMXBean proxy = configRegistryClient + final ObjectName dep = transaction.createModule(DepTestImplModuleFactory.NAME, "instanceD"); + final ObjectName impl = transaction.createModule(NetconfTestImplModuleFactory.NAME, "instance"); + final NetconfTestImplModuleMXBean proxy = configRegistryClient .newMXBeanProxy(impl, NetconfTestImplModuleMXBean.class); proxy.setTestingDep(dep); proxy.setSimpleShort((short) 0); transaction.commit(); - try (TestingNetconfClient netconfClient = createSession(tcpAddress, "1")) { + try (TestingNetconfClient netconfClient = createSession(TCP_ADDRESS, "1")) { final String expectedNamespace = "urn:opendaylight:params:xml:ns:yang:controller:test:impl"; final String rpc = "" @@ -275,7 +179,7 @@ public class NetconfITTest extends AbstractNetconfConfigTest { @Test public void testCloseSession() throws Exception { - try (TestingNetconfClient netconfClient = createSession(tcpAddress, "1")) { + try (TestingNetconfClient netconfClient = createSession(TCP_ADDRESS, "1")) { // edit config Document rpcReply = netconfClient.sendMessage(this.editConfig) @@ -291,7 +195,7 @@ public class NetconfITTest extends AbstractNetconfConfigTest { @Test public void testEditConfig() throws Exception { - try (TestingNetconfClient netconfClient = createSession(tcpAddress, "1")) { + try (TestingNetconfClient netconfClient = createSession(TCP_ADDRESS, "1")) { // send edit_config.xml final Document rpcReply = netconfClient.sendMessage(this.editConfig).getDocument(); assertIsOK(rpcReply); @@ -300,7 +204,7 @@ public class NetconfITTest extends AbstractNetconfConfigTest { @Test public void testValidate() throws Exception { - try (TestingNetconfClient netconfClient = createSession(tcpAddress, "1")) { + try (TestingNetconfClient netconfClient = createSession(TCP_ADDRESS, "1")) { // begin transaction Document rpcReply = netconfClient.sendMessage(getConfigCandidate).getDocument(); assertEquals("data", XmlElement.fromDomDocument(rpcReply).getOnlyChildElement().getName()); @@ -318,7 +222,7 @@ public class NetconfITTest extends AbstractNetconfConfigTest { } private Document assertGetConfigWorks(final TestingNetconfClient netconfClient) throws InterruptedException, ExecutionException, TimeoutException, NetconfDocumentedException { - return assertGetConfigWorks(netconfClient, this.getConfig); + return assertGetConfigWorks(netconfClient, getGetConfig()); } private Document assertGetConfigWorks(final TestingNetconfClient netconfClient, final NetconfMessage getConfigMessage) @@ -331,14 +235,14 @@ public class NetconfITTest extends AbstractNetconfConfigTest { @Test public void testGetConfig() throws Exception { - try (TestingNetconfClient netconfClient = createSession(tcpAddress, "1")) { + try (TestingNetconfClient netconfClient = createSession(TCP_ADDRESS, "1")) { assertGetConfigWorks(netconfClient); } } @Test public void createYangTestBasedOnYuma() throws Exception { - try (TestingNetconfClient netconfClient = createSession(tcpAddress, "1")) { + try (TestingNetconfClient netconfClient = createSession(TCP_ADDRESS, "1")) { Document rpcReply = netconfClient.sendMessage( XmlFileLoader.xmlFileToNetconfMessage("netconfMessages/editConfig_merge_yang-test.xml")) .getDocument(); @@ -351,7 +255,7 @@ public class NetconfITTest extends AbstractNetconfConfigTest { final ObjectName on = new ObjectName( "org.opendaylight.controller:instanceName=impl-dep-instance,type=Module,moduleFactoryName=impl-dep"); - Set cfgBeans = configRegistryClient.lookupConfigBeans(); + final Set cfgBeans = configRegistryClient.lookupConfigBeans(); assertEquals(cfgBeans, Sets.newHashSet(on)); } } @@ -364,21 +268,21 @@ public class NetconfITTest extends AbstractNetconfConfigTest { @Test public void testIdRef() throws Exception { - NetconfMessage editId = XmlFileLoader.xmlFileToNetconfMessage("netconfMessages/editConfig_identities.xml"); - NetconfMessage commit = XmlFileLoader.xmlFileToNetconfMessage("netconfMessages/commit.xml"); + final NetconfMessage editId = XmlFileLoader.xmlFileToNetconfMessage("netconfMessages/editConfig_identities.xml"); + final NetconfMessage commit = XmlFileLoader.xmlFileToNetconfMessage("netconfMessages/commit.xml"); - try (TestingNetconfClient netconfClient = createSession(tcpAddress, "1")) { + try (TestingNetconfClient netconfClient = createSession(TCP_ADDRESS, "1")) { assertIsOK(netconfClient.sendMessage(editId).getDocument()); assertIsOK(netconfClient.sendMessage(commit).getDocument()); - NetconfMessage response = netconfClient.sendMessage(getConfig); + final NetconfMessage response = netconfClient.sendMessage(getGetConfig()); - assertThat(XmlUtil.toString(response.getDocument()), JUnitMatchers.containsString("prefix:test-identity1")); - assertThat(XmlUtil.toString(response.getDocument()), JUnitMatchers.containsString("prefix:test-identity2")); - assertThat(XmlUtil.toString(response.getDocument()), JUnitMatchers.containsString("prefix:test-identity2")); - assertThat(XmlUtil.toString(response.getDocument()), JUnitMatchers.containsString("prefix:test-identity1")); + assertThat(XmlUtil.toString(response.getDocument()), containsString("prefix:test-identity1")); + assertThat(XmlUtil.toString(response.getDocument()), containsString("prefix:test-identity2")); + assertThat(XmlUtil.toString(response.getDocument()), containsString("prefix:test-identity2")); + assertThat(XmlUtil.toString(response.getDocument()), containsString("prefix:test-identity1")); - } catch (Exception e) { + } catch (final Exception e) { fail(Throwables.getStackTraceAsString(e)); } } @@ -394,25 +298,24 @@ public class NetconfITTest extends AbstractNetconfConfigTest { return ret; } - @Test public void testMultipleDependencies() throws Exception { // push first xml, should add parent and d1,d2 dependencies - try (TestingNetconfClient netconfClient = createSession(tcpAddress, "1")) { - Document rpcReply = netconfClient.sendMessage( + try (TestingNetconfClient netconfClient = createSession(TCP_ADDRESS, "1")) { + final Document rpcReply = netconfClient.sendMessage( XmlFileLoader.xmlFileToNetconfMessage("netconfMessages/editConfig_merge_multiple-deps1.xml")) .getDocument(); assertIsOK(rpcReply); commit(netconfClient); } // verify that parent.getTestingDeps == d1,d2 - MultipleDependenciesModuleMXBean parentProxy = configRegistryClient.newMXBeanProxy( + final MultipleDependenciesModuleMXBean parentProxy = configRegistryClient.newMXBeanProxy( configRegistryClient.lookupConfigBean(MultipleDependenciesModuleFactory.NAME, "parent"), MultipleDependenciesModuleMXBean.class); { - List testingDeps = parentProxy.getTestingDeps(); + final List testingDeps = parentProxy.getTestingDeps(); assertEquals(2, testingDeps.size()); - Set actualRefs = getServiceReferences(testingDeps); + final Set actualRefs = getServiceReferences(testingDeps); assertEquals(Sets.newHashSet("ref_d1", "ref_d2"), actualRefs); } @@ -422,35 +325,35 @@ public class NetconfITTest extends AbstractNetconfConfigTest { mergeD3(parentProxy); } - public void mergeD3(MultipleDependenciesModuleMXBean parentProxy) throws Exception { + public void mergeD3(final MultipleDependenciesModuleMXBean parentProxy) throws Exception { try (TestingNetconfClient netconfClient = new TestingNetconfClient( - "test " + tcpAddress.toString(), clientDispatcher, getClientConfiguration(tcpAddress, 5000))) { + "test " + TCP_ADDRESS.toString(), clientDispatcher, getClientConfiguration(TCP_ADDRESS, 5000))) { - Document rpcReply = netconfClient.sendMessage( + final Document rpcReply = netconfClient.sendMessage( XmlFileLoader.xmlFileToNetconfMessage("netconfMessages/editConfig_merge_multiple-deps2.xml")) .getDocument(); assertIsOK(rpcReply); commit(netconfClient); } { - List testingDeps = parentProxy.getTestingDeps(); + final List testingDeps = parentProxy.getTestingDeps(); assertEquals(3, testingDeps.size()); - Set actualRefs = getServiceReferences(testingDeps); + final Set actualRefs = getServiceReferences(testingDeps); assertEquals(Sets.newHashSet("ref_d1", "ref_d2", "ref_d3"), actualRefs); } } - public Set getServiceReferences(List testingDeps) { + public Set getServiceReferences(final List testingDeps) { return new HashSet<>(Lists.transform(testingDeps, new Function() { @Override - public String apply(ObjectName input) { + public String apply(final ObjectName input) { return ObjectNameUtil.getReferenceName(input); } })); } - public void commit(TestingNetconfClient netconfClient) throws Exception { - Document rpcReply; + public void commit(final TestingNetconfClient netconfClient) throws Exception { + final Document rpcReply; rpcReply = netconfClient.sendMessage(XmlFileLoader.xmlFileToNetconfMessage("netconfMessages/commit.xml")) .getDocument(); assertIsOK(rpcReply); diff --git a/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfMonitoringITTest.java b/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfMonitoringITTest.java deleted file mode 100644 index 05e32577fe..0000000000 --- a/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfMonitoringITTest.java +++ /dev/null @@ -1,230 +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.it; - -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 static org.opendaylight.controller.netconf.util.test.XmlUnitUtil.assertContainsElementWithText; - -import com.google.common.base.Charsets; -import com.google.common.base.Optional; -import com.google.common.collect.Sets; -import io.netty.channel.ChannelFuture; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.net.InetSocketAddress; -import java.net.Socket; -import java.util.Collection; -import java.util.List; -import java.util.Set; -import junit.framework.Assert; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mock; -import org.opendaylight.controller.config.manager.impl.factoriesresolver.HardcodedModuleFactoriesResolver; -import org.opendaylight.controller.netconf.api.NetconfMessage; -import org.opendaylight.controller.netconf.api.monitoring.NetconfManagementSession; -import org.opendaylight.controller.netconf.client.NetconfClientDispatcherImpl; -import org.opendaylight.controller.netconf.client.test.TestingNetconfClient; -import org.opendaylight.controller.netconf.confignetconfconnector.osgi.NetconfOperationServiceFactoryImpl; -import org.opendaylight.controller.netconf.confignetconfconnector.osgi.YangStoreException; -import org.opendaylight.controller.netconf.impl.DefaultCommitNotificationProducer; -import org.opendaylight.controller.netconf.impl.NetconfServerDispatcher; -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.Capability; -import org.opendaylight.controller.netconf.mapping.api.NetconfOperationProvider; -import org.opendaylight.controller.netconf.mapping.api.NetconfOperationService; -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.slf4j.LoggerFactory; -import org.w3c.dom.Document; - -public class NetconfMonitoringITTest extends AbstractNetconfConfigTest { - - private static final Logger logger = LoggerFactory.getLogger(NetconfITTest.class); - - private static final InetSocketAddress tcpAddress = new InetSocketAddress("127.0.0.1", 12023); - - @Mock - private DefaultCommitNotificationProducer commitNot; - private NetconfServerDispatcher dispatch; - - private NetconfClientDispatcherImpl clientDispatcher; - - private NetconfMonitoringServiceImpl monitoringService; - - @Before - public void setUp() throws Exception { - super.initConfigTransactionManagerImpl(new HardcodedModuleFactoriesResolver(mockedContext, NetconfITTest.FACTORIES)); - - monitoringService = new NetconfMonitoringServiceImpl(getNetconfOperationProvider()); - - NetconfOperationServiceFactoryListenerImpl factoriesListener = new NetconfOperationServiceFactoryListenerImpl(); - factoriesListener.onAddNetconfOperationServiceFactory(new NetconfOperationServiceFactoryImpl(getYangStore())); - factoriesListener - .onAddNetconfOperationServiceFactory(new NetconfMonitoringActivator.NetconfMonitoringOperationServiceFactory( - new NetconfMonitoringOperationService(monitoringService))); - - - dispatch = createDispatcher(factoriesListener); - ChannelFuture s = dispatch.createServer(tcpAddress); - s.await(); - - clientDispatcher = new NetconfClientDispatcherImpl(getNettyThreadgroup(), getNettyThreadgroup(), getHashedWheelTimer()); - } - - private HardcodedYangStoreService getYangStore() throws YangStoreException, IOException { - final Collection yangDependencies = NetconfITTest.getBasicYangs(); - return new HardcodedYangStoreService(yangDependencies); - } - - private NetconfServerDispatcher createDispatcher( - NetconfOperationServiceFactoryListenerImpl factoriesListener) { - return super.createDispatcher(factoriesListener, getNetconfMonitoringListenerService(logger, monitoringService), commitNot); - } - - static SessionMonitoringService getNetconfMonitoringListenerService(final Logger logger, final NetconfMonitoringServiceImpl monitor) { - return new SessionMonitoringService() { - @Override - public void onSessionUp(NetconfManagementSession session) { - logger.debug("Management session up {}", session); - monitor.onSessionUp(session); - } - - @Override - public void onSessionDown(NetconfManagementSession session) { - logger.debug("Management session down {}", session); - monitor.onSessionDown(session); - } - }; - } - - - @Test - public void testGetResponseFromMonitoring() throws Exception { - try (TestingNetconfClient netconfClient = new TestingNetconfClient("client-monitoring", clientDispatcher, getClientConfiguration(tcpAddress, 4000))) { - try (TestingNetconfClient netconfClient2 = new TestingNetconfClient("client-monitoring2", clientDispatcher, getClientConfiguration(tcpAddress, 4000))) { - NetconfMessage response = netconfClient.sendMessage(loadGetMessage()); - assertSessionElementsInResponse(response.getDocument(), 2); - } - NetconfMessage response = netconfClient.sendMessage(loadGetMessage()); - assertSessionElementsInResponse(response.getDocument(), 1); - } - } - - - @Test(timeout = 13 * 10000) - public void testClientHelloWithAuth() throws Exception { - String fileName = "netconfMessages/client_hello_with_auth.xml"; - String hello = XmlFileLoader.fileToString(fileName); - - fileName = "netconfMessages/get.xml"; - String get = XmlFileLoader.fileToString(fileName); - - Socket sock = new Socket(tcpAddress.getHostName(), tcpAddress.getPort()); - sock.getOutputStream().write(hello.getBytes(Charsets.UTF_8)); - String separator = "]]>]]>"; - - sock.getOutputStream().write(separator.getBytes(Charsets.UTF_8)); - sock.getOutputStream().write(get.getBytes(Charsets.UTF_8)); - sock.getOutputStream().write(separator.getBytes(Charsets.UTF_8)); - - StringBuilder responseBuilder = new StringBuilder(); - - try (InputStream inputStream = sock.getInputStream(); - InputStreamReader reader = new InputStreamReader(inputStream); - BufferedReader buff = new BufferedReader(reader)) { - String line; - while ((line = buff.readLine()) != null) { - - responseBuilder.append(line); - responseBuilder.append(System.lineSeparator()); - - if(line.contains("")) - break; - } - } - - sock.close(); - - String helloMsg = responseBuilder.substring(0, responseBuilder.indexOf(separator)); - Document doc = XmlUtil.readXmlToDocument(helloMsg); - assertContainsElementWithText(doc, "urn:ietf:params:netconf:capability:candidate:1.0"); - - String replyMsg = responseBuilder.substring(responseBuilder.indexOf(separator) + separator.length()); - doc = XmlUtil.readXmlToDocument(replyMsg); - assertContainsElementWithText(doc, "tomas"); - } - - private void assertSessionElementsInResponse(Document document, int i) { - int elementSize = document.getElementsByTagName("session-id").getLength(); - Assert.assertEquals("Incorrect number of session-id tags in " + XmlUtil.toString(document),i, elementSize); - } - - private NetconfMessage loadGetMessage() throws Exception { - return XmlFileLoader.xmlFileToNetconfMessage("netconfMessages/get.xml"); - } - - public static NetconfOperationProvider getNetconfOperationProvider() throws Exception { - NetconfOperationProvider factoriesListener = mock(NetconfOperationProvider.class); - NetconfOperationServiceSnapshotImpl snap = mock(NetconfOperationServiceSnapshotImpl.class); - doNothing().when(snap).close(); - NetconfOperationService service = mock(NetconfOperationService.class); - Set caps = Sets.newHashSet(); - caps.add(new Capability() { - @Override - public String getCapabilityUri() { - return "namespaceModuleRevision"; - } - - @Override - public Optional getModuleNamespace() { - return Optional.of("namespace"); - } - - @Override - public Optional getModuleName() { - return Optional.of("name"); - } - - @Override - public Optional getRevision() { - return Optional.of("revision"); - } - - @Override - public Optional getCapabilitySchema() { - return Optional.of("content"); - } - - @Override - public Optional> getLocation() { - return Optional.absent(); - } - }); - - doReturn(caps).when(service).getCapabilities(); - Set services = Sets.newHashSet(service); - doReturn(services).when(snap).getServices(); - doReturn(snap).when(factoriesListener).openSnapshot(anyString()); - - return factoriesListener; - } - - -} diff --git a/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/SSLUtil.java b/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/SSLUtil.java index 4d232a619a..f76bf4f4c3 100644 --- a/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/SSLUtil.java +++ b/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/SSLUtil.java @@ -24,8 +24,7 @@ import com.google.common.base.Preconditions; public final class SSLUtil { - private SSLUtil() { - } + private SSLUtil() {} public static SSLContext initializeSecureContext(final String pass, final InputStream ksKeysFile, final InputStream ksTrustFile, final String algorithm) throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException, diff --git a/opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/AcceptingAuthProvider.java b/opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/AcceptingAuthProvider.java index 35f2345248..4721acf209 100644 --- a/opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/AcceptingAuthProvider.java +++ b/opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/AcceptingAuthProvider.java @@ -8,29 +8,13 @@ package org.opendaylight.controller.netconf.test.tool; -import java.io.File; -import java.io.IOException; -import org.opendaylight.controller.netconf.ssh.authentication.AuthProvider; -import org.opendaylight.controller.netconf.ssh.authentication.PEMGenerator; +import org.opendaylight.controller.netconf.auth.AuthProvider; class AcceptingAuthProvider implements AuthProvider { - private final String privateKeyPEMString; - - public AcceptingAuthProvider() { - try { - this.privateKeyPEMString = PEMGenerator.readOrGeneratePK(new File("PK")); - } catch (final IOException e) { - throw new RuntimeException(e); - } - } @Override public synchronized boolean authenticated(final String username, final String password) { return true; } - @Override - public char[] getPEMAsCharArray() { - return privateKeyPEMString.toCharArray(); - } } 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 3a52f0a85e..2cd5b19bd1 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 @@ -23,6 +23,7 @@ import io.netty.channel.local.LocalAddress; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.util.HashedWheelTimer; import java.io.Closeable; +import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.lang.management.ManagementFactory; @@ -55,6 +56,7 @@ import org.opendaylight.controller.netconf.mapping.api.NetconfOperationService; import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceSnapshot; import org.opendaylight.controller.netconf.monitoring.osgi.NetconfMonitoringOperationService; import org.opendaylight.controller.netconf.ssh.NetconfSSHServer; +import org.opendaylight.controller.netconf.ssh.authentication.PEMGenerator; import org.opendaylight.yangtools.yang.model.api.SchemaContext; import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceException; import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation; @@ -165,7 +167,8 @@ public class NetconfDeviceSimulator implements Closeable { server = dispatcher.createLocalServer(tcpLocalAddress); try { - NetconfSSHServer.start(currentPort, tcpLocalAddress, new AcceptingAuthProvider(), nettyThreadgroup); + final NetconfSSHServer sshServer = NetconfSSHServer.start(currentPort, tcpLocalAddress, nettyThreadgroup, getPemArray()); + sshServer.setAuthProvider(new AcceptingAuthProvider()); } catch (final Exception e) { LOG.warn("Cannot start simulated device on {}, skipping", address, e); // Close local server and continue @@ -219,6 +222,14 @@ public class NetconfDeviceSimulator implements Closeable { return openDevices; } + private char[] getPemArray() { + try { + return PEMGenerator.readOrGeneratePK(new File("PK")).toCharArray(); + } catch (final IOException e) { + throw new RuntimeException(e); + } + } + private Map parseSchemasToModuleBuilders(final Main.Params params) { final SharedSchemaRepository consumer = new SharedSchemaRepository("netconf-simulator"); consumer.registerSchemaSourceListener(TextToASTTransformer.create(consumer, consumer)); diff --git a/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/NeutronRouter_Interface.java b/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/NeutronRouter_Interface.java index e2df13b9aa..c7ae4eb172 100644 --- a/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/NeutronRouter_Interface.java +++ b/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/NeutronRouter_Interface.java @@ -59,6 +59,10 @@ public class NeutronRouter_Interface implements Serializable { this.portUUID = portUUID; } + public String getID() { + return id; + } + public void setID(String id) { this.id = id; } diff --git a/opendaylight/samples/northbound/loadbalancer/pom.xml b/opendaylight/samples/northbound/loadbalancer/pom.xml index 5854d5577f..d39241757f 100644 --- a/opendaylight/samples/northbound/loadbalancer/pom.xml +++ b/opendaylight/samples/northbound/loadbalancer/pom.xml @@ -81,7 +81,7 @@ org.opendaylight.controller sal - 0.7.1-SNAPSHOT + ${sal.version}