From: Tony Tkacik Date: Fri, 5 Sep 2014 07:10:12 +0000 (+0000) Subject: Merge "BUG-1690: catch wildcard InstanceIdentifiers" X-Git-Tag: release/helium~132 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=controller.git;a=commitdiff_plain;h=c33b2b55b2eae406df001619885a0610800cb951;hp=f4e390f72ae047278cc2bf32cd9bd7f4535de16d Merge "BUG-1690: catch wildcard InstanceIdentifiers" --- 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/mdsal/pom.xml b/features/mdsal/pom.xml index c6856c89fb..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 @@ -97,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 diff --git a/features/mdsal/src/main/resources/features.xml b/features/mdsal/src/main/resources/features.xml index 408be621f5..619eaee8a8 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 @@ -38,6 +40,11 @@ mvn:org.opendaylight.controller/sal-netconf-connector/${project.version} mvn:org.opendaylight.controller.model/model-inventory/${project.version} mvn:org.opendaylight.controller/netconf-config-dispatcher/${config.version} + mvn:org.opendaylight.controller/netconf-config/${netconf.version}/xml/config + + + odl-netconf-ssh + odl-mdsal-netconf-connector mvn:org.opendaylight.controller/netconf-connector-config/${netconf.version}/xml/config @@ -87,4 +94,33 @@ 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 d18d227f00..46f83fb514 100644 --- a/features/netconf/pom.xml +++ b/features/netconf/pom.xml @@ -38,6 +38,22 @@ org.opendaylight.controller netconf-auth + + org.opendaylight.controller + netconf-tcp + + + org.opendaylight.controller + netconf-ssh + + + org.bouncycastle + bcpkix-jdk15on + + + org.bouncycastle + bcprov-jdk15on + org.opendaylight.controller ietf-netconf-monitoring @@ -117,10 +133,24 @@ xml config + + org.opendaylight.controller + netconf-connector-config + ${config.version} + xml + config + org.opendaylight.controller netconf-monitoring + + org.opendaylight.aaa + features-aaa + ${aaa.version} + features + xml + diff --git a/features/netconf/src/main/resources/features.xml b/features/netconf/src/main/resources/features.xml index 0a6356231a..4157212f2e 100644 --- a/features/netconf/src/main/resources/features.xml +++ b/features/netconf/src/main/resources/features.xml @@ -5,11 +5,15 @@ xsi:schemaLocation="http://karaf.apache.org/xmlns/features/v1.2.0 http://karaf.apache.org/xmlns/features/v1.2.0"> mvn:org.opendaylight.controller/features-protocol-framework/${protocol-framework.version}/xml/features mvn:org.opendaylight.controller/features-config/${config.version}/xml/features + mvn:org.opendaylight.aaa/features-aaa/${aaa.version}/xml/features + odl-netconf-api odl-netconf-mapping-api odl-netconf-util odl-netconf-impl + odl-netconf-tcp + odl-netconf-ssh odl-config-netconf-connector odl-netconf-netty-util odl-netconf-client @@ -38,8 +42,23 @@ odl-netconf-mapping-api odl-netconf-util odl-netconf-netty-util + + odl-config-netconf-connector + + odl-netconf-monitoring mvn:org.opendaylight.controller/netconf-impl/${project.version} + + odl-netconf-tcp + odl-aaa-authn-plugin + mvn:org.opendaylight.controller/netconf-ssh/${project.version} + mvn:org.bouncycastle/bcpkix-jdk15on/${bouncycastle.version} + mvn:org.bouncycastle/bcprov-jdk15on/${bouncycastle.version} + + + odl-netconf-impl + mvn:org.opendaylight.controller/netconf-tcp/${project.version} + odl-config-manager odl-netconf-api @@ -64,7 +83,6 @@ odl-netconf-netty-util mvn:org.opendaylight.controller/netconf-client/${project.version} - mvn:org.opendaylight.controller/netconf-config/${netconf.version}/xml/config odl-netconf-util 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/commons/opendaylight/pom.xml b/opendaylight/commons/opendaylight/pom.xml index 2bc099d24c..2e817b97f3 100644 --- a/opendaylight/commons/opendaylight/pom.xml +++ b/opendaylight/commons/opendaylight/pom.xml @@ -67,7 +67,9 @@ 0.5.2-SNAPSHOT 1.4 0.2.5-SNAPSHOT + 0.1.0-SNAPSHOT etc/opendaylight/karaf + 05-clustering.xml 00-netty.xml 01-mdsal.xml 04-xsql.xml @@ -199,7 +201,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 diff --git a/opendaylight/distribution/opendaylight-karaf-resources/pom.xml b/opendaylight/distribution/opendaylight-karaf-resources/pom.xml index 00495a3201..e34a5d3c2c 100644 --- a/opendaylight/distribution/opendaylight-karaf-resources/pom.xml +++ b/opendaylight/distribution/opendaylight-karaf-resources/pom.xml @@ -18,4 +18,35 @@ opendaylight-karaf-resources Resources for opendaylight-karaf jar + + + + org.apache.maven.plugins + maven-dependency-plugin + 2.6 + + + copy + + copy + + + generate-resources + + + + + org.bouncycastle + bcprov-jdk15on + ${bouncycastle.version} + target/classes/lib/ext + bcprov-jdk15on-${bouncycastle.version}.jar + + + + + + + + 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..8a2aa59dfe 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 @@ -37,6 +37,8 @@ netconf.tcp.client.port=8383 netconf.ssh.address=0.0.0.0 netconf.ssh.port=1830 netconf.ssh.pk.path = ./configuration/RSA.pk +# Set security provider to BouncyCastle +org.apache.karaf.security.providers = org.bouncycastle.jce.provider.BouncyCastleProvider netconf.config.persister.active=1 @@ -92,6 +94,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-karaf/pom.xml b/opendaylight/distribution/opendaylight-karaf/pom.xml index cdc592428f..795f68c397 100644 --- a/opendaylight/distribution/opendaylight-karaf/pom.xml +++ b/opendaylight/distribution/opendaylight-karaf/pom.xml @@ -226,6 +226,14 @@ target/assembly/lib karaf.branding-${branding.version}.jar + + + org.bouncycastle + bcprov-jdk15on + ${bouncycastle.version} + target/assembly/lib/ext + bcprov-jdk15on-${bouncycastle.version}.jar + diff --git a/opendaylight/distribution/opendaylight/pom.xml b/opendaylight/distribution/opendaylight/pom.xml index 7f9f56f6cd..fcb452f422 100644 --- a/opendaylight/distribution/opendaylight/pom.xml +++ b/opendaylight/distribution/opendaylight/pom.xml @@ -1066,10 +1066,6 @@ org.opendaylight.controller sal-restconf-broker - - org.opendaylight.controller - sal-remoterpc-connector - @@ -1316,10 +1312,6 @@ jeromq 0.3.1 - - org.opendaylight.controller - sal-distributed-datastore - org.opendaylight.controller sal-clustering-config 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/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..8135d837d3 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); @@ -264,6 +314,10 @@ public abstract class RaftActor extends UntypedPersistentActor { protected ActorSelection getLeader(){ String leaderAddress = getLeaderAddress(); + if(leaderAddress == null){ + return null; + } + return context.actorSelection(leaderAddress); } @@ -344,7 +398,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 +410,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 +477,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 +558,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 +577,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 +611,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-clustering-commons/pom.xml b/opendaylight/md-sal/sal-clustering-commons/pom.xml index b8980cd0be..a3619ec4d2 100644 --- a/opendaylight/md-sal/sal-clustering-commons/pom.xml +++ b/opendaylight/md-sal/sal-clustering-commons/pom.xml @@ -209,6 +209,7 @@ + org.jacoco @@ -238,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/protobuff/messages/persistent/PersistentMessages.java b/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/protobuff/messages/persistent/PersistentMessages.java index fcf1f45bc2..c867fce75d 100644 --- a/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/protobuff/messages/persistent/PersistentMessages.java +++ b/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/protobuff/messages/persistent/PersistentMessages.java @@ -926,6 +926,16 @@ public final class PersistentMessages { */ org.opendaylight.controller.protobuff.messages.persistent.PersistentMessages.ModificationOrBuilder getModificationOrBuilder( int index); + + // optional int64 timeStamp = 2; + /** + * optional int64 timeStamp = 2; + */ + boolean hasTimeStamp(); + /** + * optional int64 timeStamp = 2; + */ + long getTimeStamp(); } /** * Protobuf type {@code org.opendaylight.controller.mdsal.CompositeModification} @@ -986,6 +996,11 @@ public final class PersistentMessages { modification_.add(input.readMessage(org.opendaylight.controller.protobuff.messages.persistent.PersistentMessages.Modification.PARSER, extensionRegistry)); break; } + case 16: { + bitField0_ |= 0x00000001; + timeStamp_ = input.readInt64(); + break; + } } } } catch (com.google.protobuf.InvalidProtocolBufferException e) { @@ -1028,6 +1043,7 @@ public final class PersistentMessages { return PARSER; } + private int bitField0_; // repeated .org.opendaylight.controller.mdsal.Modification modification = 1; public static final int MODIFICATION_FIELD_NUMBER = 1; private java.util.List modification_; @@ -1064,8 +1080,25 @@ public final class PersistentMessages { return modification_.get(index); } + // optional int64 timeStamp = 2; + public static final int TIMESTAMP_FIELD_NUMBER = 2; + private long timeStamp_; + /** + * optional int64 timeStamp = 2; + */ + public boolean hasTimeStamp() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional int64 timeStamp = 2; + */ + public long getTimeStamp() { + return timeStamp_; + } + private void initFields() { modification_ = java.util.Collections.emptyList(); + timeStamp_ = 0L; } private byte memoizedIsInitialized = -1; public final boolean isInitialized() { @@ -1088,6 +1121,9 @@ public final class PersistentMessages { for (int i = 0; i < modification_.size(); i++) { output.writeMessage(1, modification_.get(i)); } + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeInt64(2, timeStamp_); + } getUnknownFields().writeTo(output); } @@ -1101,6 +1137,10 @@ public final class PersistentMessages { size += com.google.protobuf.CodedOutputStream .computeMessageSize(1, modification_.get(i)); } + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeInt64Size(2, timeStamp_); + } size += getUnknownFields().getSerializedSize(); memoizedSerializedSize = size; return size; @@ -1224,6 +1264,8 @@ public final class PersistentMessages { } else { modificationBuilder_.clear(); } + timeStamp_ = 0L; + bitField0_ = (bitField0_ & ~0x00000002); return this; } @@ -1251,6 +1293,7 @@ public final class PersistentMessages { public org.opendaylight.controller.protobuff.messages.persistent.PersistentMessages.CompositeModification buildPartial() { org.opendaylight.controller.protobuff.messages.persistent.PersistentMessages.CompositeModification result = new org.opendaylight.controller.protobuff.messages.persistent.PersistentMessages.CompositeModification(this); int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; if (modificationBuilder_ == null) { if (((bitField0_ & 0x00000001) == 0x00000001)) { modification_ = java.util.Collections.unmodifiableList(modification_); @@ -1260,6 +1303,11 @@ public final class PersistentMessages { } else { result.modification_ = modificationBuilder_.build(); } + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000001; + } + result.timeStamp_ = timeStamp_; + result.bitField0_ = to_bitField0_; onBuilt(); return result; } @@ -1301,6 +1349,9 @@ public final class PersistentMessages { } } } + if (other.hasTimeStamp()) { + setTimeStamp(other.getTimeStamp()); + } this.mergeUnknownFields(other.getUnknownFields()); return this; } @@ -1574,6 +1625,39 @@ public final class PersistentMessages { return modificationBuilder_; } + // optional int64 timeStamp = 2; + private long timeStamp_ ; + /** + * optional int64 timeStamp = 2; + */ + public boolean hasTimeStamp() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional int64 timeStamp = 2; + */ + public long getTimeStamp() { + return timeStamp_; + } + /** + * optional int64 timeStamp = 2; + */ + public Builder setTimeStamp(long value) { + bitField0_ |= 0x00000002; + timeStamp_ = value; + onChanged(); + return this; + } + /** + * optional int64 timeStamp = 2; + */ + public Builder clearTimeStamp() { + bitField0_ = (bitField0_ & ~0x00000002); + timeStamp_ = 0L; + onChanged(); + return this; + } + // @@protoc_insertion_point(builder_scope:org.opendaylight.controller.mdsal.CompositeModification) } @@ -1610,11 +1694,12 @@ public final class PersistentMessages { "e\030\001 \002(\t\022C\n\004path\030\002 \002(\01325.org.opendaylight" + ".controller.mdsal.InstanceIdentifier\0225\n\004" + "data\030\003 \001(\0132\'.org.opendaylight.controller" + - ".mdsal.Node\"^\n\025CompositeModification\022E\n\014" + + ".mdsal.Node\"q\n\025CompositeModification\022E\n\014" + "modification\030\001 \003(\0132/.org.opendaylight.co" + - "ntroller.mdsal.ModificationBO\n9org.opend" + - "aylight.controller.protobuff.messages.pe", - "rsistentB\022PersistentMessages" + "ntroller.mdsal.Modification\022\021\n\ttimeStamp" + + "\030\002 \001(\003BO\n9org.opendaylight.controller.pr", + "otobuff.messages.persistentB\022PersistentM" + + "essages" }; com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner = new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() { @@ -1632,7 +1717,7 @@ public final class PersistentMessages { internal_static_org_opendaylight_controller_mdsal_CompositeModification_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_org_opendaylight_controller_mdsal_CompositeModification_descriptor, - new java.lang.String[] { "Modification", }); + new java.lang.String[] { "Modification", "TimeStamp", }); return null; } }; 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/main/resources/Persistent.proto b/opendaylight/md-sal/sal-clustering-commons/src/main/resources/Persistent.proto index 8e834494cb..72651ab260 100644 --- a/opendaylight/md-sal/sal-clustering-commons/src/main/resources/Persistent.proto +++ b/opendaylight/md-sal/sal-clustering-commons/src/main/resources/Persistent.proto @@ -11,10 +11,12 @@ message Modification { required string type=1; required InstanceIdentifier path=2; optional Node data=3; + } message CompositeModification { repeated Modification modification=1; + optional int64 timeStamp = 2; } 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 05322137aa..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 @@ -21,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 @@ -30,9 +30,14 @@ 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" + ] + } } } @@ -51,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-distributed-datastore/pom.xml b/opendaylight/md-sal/sal-distributed-datastore/pom.xml index dd5a7f2979..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} + + @@ -178,6 +183,7 @@ !*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/DatastoreContext.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DatastoreContext.java index af8a987c73..1021ddeee7 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DatastoreContext.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DatastoreContext.java @@ -9,13 +9,15 @@ package org.opendaylight.controller.cluster.datastore; 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. + * Contains contextual data for a data store. * * @author Thomas Pantelis */ @@ -23,16 +25,24 @@ public class DatastoreContext { private final InMemoryDOMDataStoreConfigProperties dataStoreProperties; private final Duration shardTransactionIdleTimeout; + private final int operationTimeoutInSeconds; + private final String dataStoreMXBeanType; public DatastoreContext() { this.dataStoreProperties = null; + this.dataStoreMXBeanType = "DistributedDatastore"; this.shardTransactionIdleTimeout = Duration.create(10, TimeUnit.MINUTES); + this.operationTimeoutInSeconds = 5; } - public DatastoreContext(InMemoryDOMDataStoreConfigProperties dataStoreProperties, - Duration shardTransactionIdleTimeout) { + public DatastoreContext(String dataStoreMXBeanType, + InMemoryDOMDataStoreConfigProperties dataStoreProperties, + Duration shardTransactionIdleTimeout, + int operationTimeoutInSeconds) { + this.dataStoreMXBeanType = dataStoreMXBeanType; this.dataStoreProperties = Preconditions.checkNotNull(dataStoreProperties); - this.shardTransactionIdleTimeout = Preconditions.checkNotNull(shardTransactionIdleTimeout); + this.shardTransactionIdleTimeout = shardTransactionIdleTimeout; + this.operationTimeoutInSeconds = operationTimeoutInSeconds; } public InMemoryDOMDataStoreConfigProperties getDataStoreProperties() { @@ -43,5 +53,11 @@ public class DatastoreContext { return shardTransactionIdleTimeout; } + public String getDataStoreMXBeanType() { + return dataStoreMXBeanType; + } + public int getOperationTimeoutInSeconds() { + return operationTimeoutInSeconds; + } } 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 0a137e07df..db01d51535 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 @@ -8,8 +8,6 @@ package org.opendaylight.controller.cluster.datastore; -import java.util.concurrent.TimeUnit; - import akka.actor.ActorRef; import akka.actor.ActorSystem; @@ -22,7 +20,6 @@ import org.opendaylight.controller.cluster.datastore.shardstrategy.ShardStrategy import org.opendaylight.controller.cluster.datastore.utils.ActorContext; import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker; import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener; -import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStoreConfigProperties; 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; @@ -36,8 +33,6 @@ import org.opendaylight.yangtools.yang.model.api.SchemaContextListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import scala.concurrent.duration.Duration; - /** * */ @@ -46,42 +41,30 @@ public class DistributedDataStore implements DOMStore, SchemaContextListener, Au private static final Logger LOG = LoggerFactory.getLogger(DistributedDataStore.class); private final ActorContext actorContext; - private final DatastoreContext datastoreContext; public DistributedDataStore(ActorSystem actorSystem, String type, ClusterWrapper cluster, - Configuration configuration, DistributedDataStoreProperties dataStoreProperties) { + Configuration configuration, DatastoreContext datastoreContext) { Preconditions.checkNotNull(actorSystem, "actorSystem should not be null"); Preconditions.checkNotNull(type, "type should not be null"); Preconditions.checkNotNull(cluster, "cluster should not be null"); Preconditions.checkNotNull(configuration, "configuration should not be null"); - + Preconditions.checkNotNull(datastoreContext, "datastoreContext should not be null"); String shardManagerId = ShardManagerIdentifier.builder().type(type).build().toString(); LOG.info("Creating ShardManager : {}", shardManagerId); - 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, datastoreContext) + .withMailbox(ActorContext.MAILBOX), 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()); + actorContext.setOperationTimeout(datastoreContext.getOperationTimeoutInSeconds()); } public DistributedDataStore(ActorContext actorContext) { this.actorContext = Preconditions.checkNotNull(actorContext, "actorContext should not be null"); - this.datastoreContext = new DatastoreContext(); } - @SuppressWarnings("unchecked") @Override public >> 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..8739ed1966 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,16 +12,17 @@ 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) { + DatastoreContext datastoreContext, 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), - config, dataStoreProperties ); + config, datastoreContext ); ShardStrategyFactory.setConfiguration(config); schemaService.registerSchemaContextListener(dataStore); return dataStore; 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 df3245ffb2..e69de29bb2 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 @@ -1,60 +0,0 @@ -/* - * 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.cluster.datastore; - -/** - * Wrapper class for DistributedDataStore configuration properties. - * - * @author Thomas Pantelis - */ -public class DistributedDataStoreProperties { - private final int maxShardDataChangeListenerQueueSize; - 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 operationTimeoutInSeconds) { - this.maxShardDataChangeListenerQueueSize = maxShardDataChangeListenerQueueSize; - this.maxShardDataChangeExecutorQueueSize = maxShardDataChangeExecutorQueueSize; - this.maxShardDataChangeExecutorPoolSize = maxShardDataChangeExecutorPoolSize; - this.shardTransactionIdleTimeoutInMinutes = shardTransactionIdleTimeoutInMinutes; - this.operationTimeoutInSeconds = operationTimeoutInSeconds; - } - - public int getMaxShardDataChangeListenerQueueSize() { - return maxShardDataChangeListenerQueueSize; - } - - public int getMaxShardDataChangeExecutorQueueSize() { - return maxShardDataChangeExecutorQueueSize; - } - - public int getMaxShardDataChangeExecutorPoolSize() { - return maxShardDataChangeExecutorPoolSize; - } - - 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 43a9faa3e4..7d570046d4 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 @@ -10,18 +10,22 @@ package org.opendaylight.controller.cluster.datastore; import akka.actor.ActorRef; import akka.actor.ActorSelection; +import akka.actor.PoisonPill; 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.annotations.VisibleForTesting; import com.google.common.base.Optional; 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 com.google.common.util.concurrent.ListenableFuture; - +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; 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; @@ -34,30 +38,35 @@ import org.opendaylight.controller.cluster.datastore.messages.CreateTransactionR import org.opendaylight.controller.cluster.datastore.messages.EnableNotification; import org.opendaylight.controller.cluster.datastore.messages.ForwardedCommitTransaction; import org.opendaylight.controller.cluster.datastore.messages.PeerAddressResolved; +import org.opendaylight.controller.cluster.datastore.messages.ReadData; +import org.opendaylight.controller.cluster.datastore.messages.ReadDataReply; import org.opendaylight.controller.cluster.datastore.messages.RegisterChangeListener; import org.opendaylight.controller.cluster.datastore.messages.RegisterChangeListenerReply; import org.opendaylight.controller.cluster.datastore.messages.UpdateSchemaContext; import org.opendaylight.controller.cluster.datastore.modification.Modification; import org.opendaylight.controller.cluster.datastore.modification.MutableCompositeModification; +import org.opendaylight.controller.cluster.datastore.node.NormalizedNodeToNodeCodec; import org.opendaylight.controller.cluster.raft.ConfigParams; import org.opendaylight.controller.cluster.raft.DefaultConfigParamsImpl; import org.opendaylight.controller.cluster.raft.RaftActor; import org.opendaylight.controller.cluster.raft.ReplicatedLogEntry; +import org.opendaylight.controller.cluster.raft.base.messages.CaptureSnapshotReply; import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener; +import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException; import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStore; import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStoreFactory; -import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadWriteTransaction; +import org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages; +import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadTransaction; import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort; import org.opendaylight.controller.sal.core.spi.data.DOMStoreTransactionChain; +import org.opendaylight.controller.sal.core.spi.data.DOMStoreWriteTransaction; import org.opendaylight.yangtools.concepts.ListenerRegistration; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; import org.opendaylight.yangtools.yang.model.api.SchemaContext; - import scala.concurrent.duration.FiniteDuration; import java.util.ArrayList; -import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -101,12 +110,15 @@ public class Shard extends RaftActor { private SchemaContext schemaContext; + private ActorRef createSnapshotTransaction; + private Shard(ShardIdentifier name, Map peerAddresses, - DatastoreContext datastoreContext) { + DatastoreContext datastoreContext, SchemaContext schemaContext) { super(name.toString(), mapPeerAddresses(peerAddresses), Optional.of(configParams)); this.name = name; this.datastoreContext = datastoreContext; + this.schemaContext = schemaContext; String setting = System.getProperty("shard.persistent"); @@ -117,8 +129,14 @@ public class Shard extends RaftActor { store = InMemoryDOMDataStoreFactory.create(name.toString(), null, datastoreContext.getDataStoreProperties()); - shardMBean = ShardMBeanFactory.getShardStatsMBean(name.toString()); + if(schemaContext != null) { + store.onGlobalContextUpdated(schemaContext); + } + shardMBean = ShardMBeanFactory.getShardStatsMBean(name.toString(), + datastoreContext.getDataStoreMXBeanType()); + shardMBean.setDataStoreExecutor(store.getDomStoreExecutor()); + shardMBean.setNotificationManager(store.getDataChangeListenerNotificationManager()); } @@ -136,16 +154,28 @@ public class Shard extends RaftActor { public static Props props(final ShardIdentifier name, final Map peerAddresses, - DatastoreContext datastoreContext) { + DatastoreContext datastoreContext, SchemaContext schemaContext) { Preconditions.checkNotNull(name, "name should not be null"); Preconditions.checkNotNull(peerAddresses, "peerAddresses should not be null"); - Preconditions.checkNotNull(datastoreContext, "shardContext should not be null"); + Preconditions.checkNotNull(datastoreContext, "dataStoreContext should not be null"); + Preconditions.checkNotNull(schemaContext, "schemaContext should not be null"); - return Props.create(new ShardCreator(name, peerAddresses, datastoreContext)); + return Props.create(new ShardCreator(name, peerAddresses, datastoreContext, schemaContext)); + } + + @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() @@ -155,6 +185,15 @@ public class Shard extends RaftActor { } else if (getLeader() != null) { getLeader().forward(message, getContext()); } + } else if(message.getClass().equals(ReadDataReply.SERIALIZABLE_CLASS)) { + // This must be for install snapshot. Don't want to open this up and trigger + // deSerialization + self().tell(new CaptureSnapshotReply(ReadDataReply.getNormalizedNodeByteString(message)), self()); + + // Send a PoisonPill instead of sending close transaction because we do not really need + // a response + getSender().tell(PoisonPill.getInstance(), self()); + } else if (message instanceof RegisterChangeListener) { registerChangeListener((RegisterChangeListener) message); } else if (message instanceof UpdateSchemaContext) { @@ -178,59 +217,79 @@ public class Shard extends RaftActor { } private ActorRef createTypedTransactionActor( - CreateTransaction createTransaction, + int transactionType, ShardTransactionIdentifier transactionId) { - if (createTransaction.getTransactionType() + + if(this.schemaContext == null){ + throw new NullPointerException("schemaContext should not be null"); + } + + if (transactionType == TransactionProxy.TransactionType.READ_ONLY.ordinal()) { shardMBean.incrementReadOnlyTransactionCount(); return getContext().actorOf( ShardTransaction.props(store.newReadOnlyTransaction(), getSelf(), - schemaContext,datastoreContext, name.toString()), transactionId.toString()); + schemaContext,datastoreContext, shardMBean), transactionId.toString()); - } else if (createTransaction.getTransactionType() + } else if (transactionType == TransactionProxy.TransactionType.READ_WRITE.ordinal()) { shardMBean.incrementReadWriteTransactionCount(); return getContext().actorOf( ShardTransaction.props(store.newReadWriteTransaction(), getSelf(), - schemaContext, datastoreContext,name.toString()), transactionId.toString()); + schemaContext, datastoreContext, shardMBean), transactionId.toString()); - } else if (createTransaction.getTransactionType() + } else if (transactionType == TransactionProxy.TransactionType.WRITE_ONLY.ordinal()) { shardMBean.incrementWriteOnlyTransactionCount(); return getContext().actorOf( ShardTransaction.props(store.newWriteOnlyTransaction(), getSelf(), - schemaContext, datastoreContext, name.toString()), transactionId.toString()); + schemaContext, datastoreContext, shardMBean), transactionId.toString()); } else { throw new IllegalArgumentException( "Shard="+name + ":CreateTransaction message has unidentified transaction type=" - + createTransaction.getTransactionType()); + + transactionType); } } private void createTransaction(CreateTransaction createTransaction) { + createTransaction(createTransaction.getTransactionType(), + createTransaction.getTransactionId()); + } + + private ActorRef createTransaction(int transactionType, String remoteTransactionId) { ShardTransactionIdentifier transactionId = ShardTransactionIdentifier.builder() - .remoteTransactionId(createTransaction.getTransactionId()) + .remoteTransactionId(remoteTransactionId) .build(); LOG.debug("Creating transaction : {} ", transactionId); ActorRef transactionActor = - createTypedTransactionActor(createTransaction, transactionId); + createTypedTransactionActor(transactionType, transactionId); getSender() .tell(new CreateTransactionReply( Serialization.serializedActorPath(transactionActor), - createTransaction.getTransactionId()).toSerializable(), + remoteTransactionId).toSerializable(), getSelf()); + + return transactionActor; + } + + private void syncCommitTransaction(DOMStoreWriteTransaction transaction) + throws ExecutionException, InterruptedException { + DOMStoreThreePhaseCommitCohort commitCohort = transaction.ready(); + commitCohort.preCommit().get(); + commitCohort.commit().get(); } + private void commit(final ActorRef sender, Object serialized) { Modification modification = MutableCompositeModification .fromSerializable(serialized, schemaContext); @@ -240,16 +299,11 @@ public class Shard extends RaftActor { LOG.debug( "Could not find cohort for modification : {}. Writing modification using a new transaction", modification); - DOMStoreReadWriteTransaction transaction = - store.newReadWriteTransaction(); + DOMStoreWriteTransaction transaction = + store.newWriteOnlyTransaction(); modification.apply(transaction); - DOMStoreThreePhaseCommitCohort commitCohort = transaction.ready(); - ListenableFuture future = - commitCohort.preCommit(); try { - future.get(); - future = commitCohort.commit(); - future.get(); + syncCommitTransaction(transaction); } catch (InterruptedException | ExecutionException e) { shardMBean.incrementFailedTransactionsCount(); LOG.error("Failed to commit", e); @@ -266,9 +320,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(System.currentTimeMillis()); } @Override @@ -298,9 +352,14 @@ public class Shard extends RaftActor { private void updateSchemaContext(UpdateSchemaContext message) { this.schemaContext = message.getSchemaContext(); + updateSchemaContext(message.getSchemaContext()); store.onGlobalContextUpdated(message.getSchemaContext()); } + @VisibleForTesting void updateSchemaContext(SchemaContext schemaContext) { + store.onGlobalContextUpdated(schemaContext); + } + private void registerChangeListener( RegisterChangeListener registerChangeListener) { @@ -345,9 +404,9 @@ public class Shard extends RaftActor { private void createTransactionChain() { DOMStoreTransactionChain chain = store.createTransactionChain(); ActorRef transactionChain = getContext().actorOf( - ShardTransactionChain.props(chain, schemaContext, datastoreContext,name.toString() )); + ShardTransactionChain.props(chain, schemaContext, datastoreContext, shardMBean)); getSender().tell(new CreateTransactionChainReply(transactionChain.path()).toSerializable(), - getSelf()); + getSelf()); } @Override protected void applyState(ActorRef clientActor, String identifier, @@ -365,7 +424,6 @@ public class Shard extends RaftActor { identifier, clientActor.path().toString()); } - } else { LOG.error("Unknown state received {}", data); } @@ -383,12 +441,40 @@ public class Shard extends RaftActor { } - @Override protected Object createSnapshot() { - throw new UnsupportedOperationException("createSnapshot"); + @Override protected void createSnapshot() { + if (createSnapshotTransaction == null) { + + // Create a transaction. We are really going to treat the transaction as a worker + // so that this actor does not get block building the snapshot + createSnapshotTransaction = createTransaction( + TransactionProxy.TransactionType.READ_ONLY.ordinal(), + "createSnapshot"); + + createSnapshotTransaction.tell( + new ReadData(YangInstanceIdentifier.builder().build()).toSerializable(), self()); + + } } - @Override protected void applySnapshot(Object snapshot) { - throw new UnsupportedOperationException("applySnapshot"); + @VisibleForTesting @Override protected void applySnapshot(ByteString snapshot) { + // Since this will be done only on Recovery or when this actor is a Follower + // we can safely commit everything in here. We not need to worry about event notifications + // as they would have already been disabled on the follower + try { + DOMStoreWriteTransaction transaction = store.newWriteOnlyTransaction(); + NormalizedNodeMessages.Node serializedNode = NormalizedNodeMessages.Node.parseFrom(snapshot); + NormalizedNode node = new NormalizedNodeToNodeCodec(schemaContext) + .decode(YangInstanceIdentifier.builder().build(), serializedNode); + + // delete everything first + transaction.delete(YangInstanceIdentifier.builder().build()); + + // Add everything from the remote node back + transaction.write(YangInstanceIdentifier.builder().build(), node); + syncCommitTransaction(transaction); + } catch (InvalidProtocolBufferException | InterruptedException | ExecutionException e) { + LOG.error(e, "An exception occurred when applying snapshot"); + } } @Override protected void onStateChanged() { @@ -426,17 +512,42 @@ public class Shard extends RaftActor { final ShardIdentifier name; final Map peerAddresses; final DatastoreContext datastoreContext; + final SchemaContext schemaContext; ShardCreator(ShardIdentifier name, Map peerAddresses, - DatastoreContext datastoreContext) { + DatastoreContext datastoreContext, SchemaContext schemaContext) { this.name = name; this.peerAddresses = peerAddresses; this.datastoreContext = datastoreContext; + this.schemaContext = schemaContext; } @Override public Shard create() throws Exception { - return new Shard(name, peerAddresses, datastoreContext); + return new Shard(name, peerAddresses, datastoreContext, schemaContext); } } + + @VisibleForTesting NormalizedNode readStore() throws ExecutionException, InterruptedException { + DOMStoreReadTransaction transaction = store.newReadOnlyTransaction(); + + CheckedFuture>, ReadFailedException> future = + transaction.read(YangInstanceIdentifier.builder().build()); + + NormalizedNode node = future.get().get(); + + transaction.close(); + + return node; + } + + @VisibleForTesting void writeToStore(YangInstanceIdentifier id, NormalizedNode node) + throws ExecutionException, InterruptedException { + DOMStoreWriteTransaction transaction = store.newWriteOnlyTransaction(); + + transaction.write(id, node); + + syncCommitTransaction(transaction); + } + } 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 e51d49bff2..58cdefe537 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 @@ -17,9 +17,7 @@ import akka.actor.SupervisorStrategy; import akka.cluster.ClusterEvent; import akka.japi.Creator; import akka.japi.Function; - import com.google.common.base.Preconditions; - import org.opendaylight.controller.cluster.datastore.identifiers.ShardIdentifier; import org.opendaylight.controller.cluster.datastore.identifiers.ShardManagerIdentifier; import org.opendaylight.controller.cluster.datastore.jmx.mbeans.shardmanager.ShardManagerInfo; @@ -32,8 +30,8 @@ import org.opendaylight.controller.cluster.datastore.messages.PeerAddressResolve import org.opendaylight.controller.cluster.datastore.messages.PrimaryFound; import org.opendaylight.controller.cluster.datastore.messages.PrimaryNotFound; import org.opendaylight.controller.cluster.datastore.messages.UpdateSchemaContext; - import org.opendaylight.controller.cluster.datastore.utils.ActorContext; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; import scala.concurrent.duration.Duration; import java.util.ArrayList; @@ -89,9 +87,7 @@ public class ShardManager extends AbstractUntypedActor { // Subscribe this actor to cluster member events cluster.subscribeToMemberEvents(getSelf()); - // Create all the local Shards and make them a child of the ShardManager - // TODO: This may need to be initiated when we first get the schema context - createLocalShards(); + //createLocalShards(null); } public static Props props(final String type, @@ -162,8 +158,14 @@ public class ShardManager extends AbstractUntypedActor { * @param message */ private void updateSchemaContext(Object message) { - for(ShardInformation info : localShards.values()){ - info.getActor().tell(message,getSelf()); + SchemaContext schemaContext = ((UpdateSchemaContext) message).getSchemaContext(); + + if(localShards.size() == 0){ + createLocalShards(schemaContext); + } else { + for (ShardInformation info : localShards.values()) { + info.getActor().tell(message, getSelf()); + } } } @@ -235,7 +237,7 @@ public class ShardManager extends AbstractUntypedActor { * runs * */ - private void createLocalShards() { + private void createLocalShards(SchemaContext schemaContext) { String memberName = this.cluster.getCurrentMemberName(); List memberShardNames = this.configuration.getMemberShardNames(memberName); @@ -245,16 +247,14 @@ public class ShardManager extends AbstractUntypedActor { ShardIdentifier shardId = getShardIdentifier(memberName, shardName); Map peerAddresses = getPeerAddresses(shardName); ActorRef actor = getContext() - .actorOf(Shard.props(shardId, peerAddresses, datastoreContext). + .actorOf(Shard.props(shardId, peerAddresses, datastoreContext, schemaContext). withMailbox(ActorContext.MAILBOX), shardId.toString()); - localShardActorNames.add(shardId.toString()); localShards.put(shardName, new ShardInformation(shardName, actor, peerAddresses)); } - mBean = ShardManagerInfo - .createShardManagerMBean("shard-manager-" + this.type, localShardActorNames); - + mBean = ShardManagerInfo.createShardManagerMBean("shard-manager-" + this.type, + datastoreContext.getDataStoreMXBeanType(), localShardActorNames); } /** 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 91d629432f..0e9fd113c5 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 @@ -12,6 +12,7 @@ package org.opendaylight.controller.cluster.datastore; import akka.actor.ActorRef; +import org.opendaylight.controller.cluster.datastore.jmx.mbeans.shard.ShardStats; import org.opendaylight.controller.cluster.datastore.messages.DataExists; import org.opendaylight.controller.cluster.datastore.messages.ReadData; import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadTransaction; @@ -26,8 +27,8 @@ public class ShardReadTransaction extends ShardTransaction { private final DOMStoreReadTransaction transaction; public ShardReadTransaction(DOMStoreReadTransaction transaction, ActorRef shardActor, - SchemaContext schemaContext,String shardName) { - super(shardActor, schemaContext, shardName); + SchemaContext schemaContext, ShardStats shardStats) { + super(shardActor, schemaContext, shardStats); 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 bd71c27fd6..d04ec233ea 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 @@ -12,6 +12,7 @@ package org.opendaylight.controller.cluster.datastore; import akka.actor.ActorRef; +import org.opendaylight.controller.cluster.datastore.jmx.mbeans.shard.ShardStats; import org.opendaylight.controller.cluster.datastore.messages.DataExists; import org.opendaylight.controller.cluster.datastore.messages.DeleteData; import org.opendaylight.controller.cluster.datastore.messages.MergeData; @@ -30,8 +31,8 @@ public class ShardReadWriteTransaction extends ShardTransaction { private final DOMStoreReadWriteTransaction transaction; public ShardReadWriteTransaction(DOMStoreReadWriteTransaction transaction, ActorRef shardActor, - SchemaContext schemaContext,String shardName) { - super(shardActor, schemaContext, shardName); + SchemaContext schemaContext, ShardStats shardStats) { + super(shardActor, schemaContext, shardStats); 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 3b0e0934d9..65f865b0c4 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,10 +13,12 @@ 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.jmx.mbeans.shard.ShardStats; import org.opendaylight.controller.cluster.datastore.messages.CloseTransaction; import org.opendaylight.controller.cluster.datastore.messages.CloseTransactionReply; import org.opendaylight.controller.cluster.datastore.messages.DataExists; @@ -73,22 +75,21 @@ public abstract class ShardTransaction extends AbstractUntypedActor { private final ActorRef shardActor; protected final SchemaContext schemaContext; - private final String shardName; - + private final ShardStats shardStats; private final MutableCompositeModification modification = new MutableCompositeModification(); protected ShardTransaction(ActorRef shardActor, SchemaContext schemaContext, - String shardName) { + ShardStats shardStats) { this.shardActor = shardActor; this.schemaContext = schemaContext; - this.shardName = shardName; + this.shardStats = shardStats; } public static Props props(DOMStoreTransaction transaction, ActorRef shardActor, - SchemaContext schemaContext,DatastoreContext datastoreContext, String shardName) { + SchemaContext schemaContext,DatastoreContext datastoreContext, ShardStats shardStats) { return Props.create(new ShardTransactionCreator(transaction, shardActor, schemaContext, - datastoreContext, shardName)); + datastoreContext, shardStats)); } protected abstract DOMStoreTransaction getDOMStoreTransaction(); @@ -137,7 +138,7 @@ public abstract class ShardTransaction extends AbstractUntypedActor { sender.tell(new ReadDataReply(schemaContext,null).toSerializable(), self); } } catch (Exception e) { - ShardMBeanFactory.getShardStatsMBean(shardName).incrementFailedReadTransactionsCount(); + shardStats.incrementFailedReadTransactionsCount(); sender.tell(new akka.actor.Status.Failure(e), self); } @@ -196,7 +197,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, shardName), "cohort"); + ThreePhaseCommitCohort.props(cohort, shardActor, modification, shardStats), "cohort"); getSender() .tell(new ReadyTransactionReply(cohortActor.path()).toSerializable(), getSelf()); @@ -210,13 +211,14 @@ public abstract class ShardTransaction extends AbstractUntypedActor { final ActorRef shardActor; final SchemaContext schemaContext; final DatastoreContext datastoreContext; - final String shardName; + final ShardStats shardStats; ShardTransactionCreator(DOMStoreTransaction transaction, ActorRef shardActor, - SchemaContext schemaContext, DatastoreContext datastoreContext, String shardName) { + SchemaContext schemaContext, DatastoreContext datastoreContext, + ShardStats shardStats) { this.transaction = transaction; this.shardActor = shardActor; - this.shardName = shardName; + this.shardStats = shardStats; this.schemaContext = schemaContext; this.datastoreContext = datastoreContext; } @@ -226,13 +228,13 @@ public abstract class ShardTransaction extends AbstractUntypedActor { ShardTransaction tx; if(transaction instanceof DOMStoreReadWriteTransaction) { tx = new ShardReadWriteTransaction((DOMStoreReadWriteTransaction)transaction, - shardActor, schemaContext, shardName); + shardActor, schemaContext, shardStats); } else if(transaction instanceof DOMStoreReadTransaction) { tx = new ShardReadTransaction((DOMStoreReadTransaction)transaction, shardActor, - schemaContext, shardName); + schemaContext, shardStats); } else { tx = new ShardWriteTransaction((DOMStoreWriteTransaction)transaction, - shardActor, schemaContext, shardName); + shardActor, schemaContext, shardStats); } tx.getContext().setReceiveTimeout(datastoreContext.getShardTransactionIdleTimeout()); 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 e7a181865e..484bd54a07 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 @@ -12,6 +12,7 @@ import akka.actor.ActorRef; import akka.actor.Props; import akka.japi.Creator; +import org.opendaylight.controller.cluster.datastore.jmx.mbeans.shard.ShardStats; import org.opendaylight.controller.cluster.datastore.messages.CloseTransactionChain; import org.opendaylight.controller.cluster.datastore.messages.CloseTransactionChainReply; import org.opendaylight.controller.cluster.datastore.messages.CreateTransaction; @@ -27,14 +28,14 @@ public class ShardTransactionChain extends AbstractUntypedActor { private final DOMStoreTransactionChain chain; private final DatastoreContext datastoreContext; private final SchemaContext schemaContext; - private final String shardName; + private final ShardStats shardStats; public ShardTransactionChain(DOMStoreTransactionChain chain, SchemaContext schemaContext, - DatastoreContext datastoreContext,String shardName) { + DatastoreContext datastoreContext, ShardStats shardStats) { this.chain = chain; this.datastoreContext = datastoreContext; this.schemaContext = schemaContext; - this.shardName = shardName; + this.shardStats = shardStats; } @Override @@ -60,17 +61,17 @@ public class ShardTransactionChain extends AbstractUntypedActor { TransactionProxy.TransactionType.READ_ONLY.ordinal()) { return getContext().actorOf( ShardTransaction.props( chain.newReadOnlyTransaction(), getShardActor(), - schemaContext, datastoreContext,shardName), transactionId); + schemaContext, datastoreContext, shardStats), transactionId); } else if (createTransaction.getTransactionType() == TransactionProxy.TransactionType.READ_WRITE.ordinal()) { return getContext().actorOf( ShardTransaction.props( chain.newReadWriteTransaction(), getShardActor(), - schemaContext, datastoreContext,shardName), transactionId); + schemaContext, datastoreContext, shardStats), transactionId); } else if (createTransaction.getTransactionType() == TransactionProxy.TransactionType.WRITE_ONLY.ordinal()) { return getContext().actorOf( ShardTransaction.props( chain.newWriteOnlyTransaction(), getShardActor(), - schemaContext, datastoreContext,shardName), transactionId); + schemaContext, datastoreContext, shardStats), transactionId); } else { throw new IllegalArgumentException ( "CreateTransaction message has unidentified transaction type=" + @@ -87,8 +88,9 @@ public class ShardTransactionChain extends AbstractUntypedActor { } public static Props props(DOMStoreTransactionChain chain, SchemaContext schemaContext, - DatastoreContext datastoreContext, String shardName) { - return Props.create(new ShardTransactionChainCreator(chain, schemaContext, datastoreContext, shardName)); + DatastoreContext datastoreContext, ShardStats shardStats) { + return Props.create(new ShardTransactionChainCreator(chain, schemaContext, + datastoreContext, shardStats)); } private static class ShardTransactionChainCreator implements Creator { @@ -97,20 +99,20 @@ public class ShardTransactionChain extends AbstractUntypedActor { final DOMStoreTransactionChain chain; final DatastoreContext datastoreContext; final SchemaContext schemaContext; - final String shardName; + final ShardStats shardStats; ShardTransactionChainCreator(DOMStoreTransactionChain chain, SchemaContext schemaContext, - DatastoreContext datastoreContext, String shardName) { + DatastoreContext datastoreContext, ShardStats shardStats) { this.chain = chain; this.datastoreContext = datastoreContext; this.schemaContext = schemaContext; - this.shardName = shardName; + this.shardStats = shardStats; } @Override public ShardTransactionChain create() throws Exception { - return new ShardTransactionChain(chain, schemaContext, datastoreContext,shardName); + return new ShardTransactionChain(chain, schemaContext, datastoreContext, shardStats); } } } 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 41c46c3375..396b27a042 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 @@ -12,6 +12,7 @@ package org.opendaylight.controller.cluster.datastore; import akka.actor.ActorRef; +import org.opendaylight.controller.cluster.datastore.jmx.mbeans.shard.ShardStats; import org.opendaylight.controller.cluster.datastore.messages.DeleteData; import org.opendaylight.controller.cluster.datastore.messages.MergeData; import org.opendaylight.controller.cluster.datastore.messages.ReadyTransaction; @@ -28,8 +29,8 @@ public class ShardWriteTransaction extends ShardTransaction { private final DOMStoreWriteTransaction transaction; public ShardWriteTransaction(DOMStoreWriteTransaction transaction, ActorRef shardActor, - SchemaContext schemaContext,String shardName) { - super(shardActor, schemaContext, shardName); + SchemaContext schemaContext, ShardStats shardStats) { + super(shardActor, schemaContext, shardStats); 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 5a6d0eca5c..2dce6a1079 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,7 +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.jmx.mbeans.shard.ShardStats; import org.opendaylight.controller.cluster.datastore.messages.AbortTransaction; import org.opendaylight.controller.cluster.datastore.messages.AbortTransactionReply; import org.opendaylight.controller.cluster.datastore.messages.CanCommitTransaction; @@ -35,25 +35,25 @@ public class ThreePhaseCommitCohort extends AbstractUntypedActor { private final DOMStoreThreePhaseCommitCohort cohort; private final ActorRef shardActor; private final CompositeModification modification; - private final String shardName; + private final ShardStats shardStats; public ThreePhaseCommitCohort(DOMStoreThreePhaseCommitCohort cohort, - ActorRef shardActor, CompositeModification modification,String shardName) { + ActorRef shardActor, CompositeModification modification, ShardStats shardStats) { this.cohort = cohort; this.shardActor = shardActor; this.modification = modification; - this.shardName = shardName; + this.shardStats = shardStats; } private final LoggingAdapter log = Logging.getLogger(getContext().system(), this); public static Props props(final DOMStoreThreePhaseCommitCohort cohort, - final ActorRef shardActor, final CompositeModification modification, - String shardName) { + final ActorRef shardActor, final CompositeModification modification, + ShardStats shardStats) { return Props.create(new ThreePhaseCommitCohortCreator(cohort, shardActor, modification, - shardName)); + shardStats)); } @Override @@ -83,7 +83,7 @@ public class ThreePhaseCommitCohort extends AbstractUntypedActor { Futures.addCallback(future, new FutureCallback() { @Override public void onSuccess(Void v) { - ShardMBeanFactory.getShardStatsMBean(shardName).incrementAbortTransactionsCount(); + shardStats.incrementAbortTransactionsCount(); sender .tell(new AbortTransactionReply().toSerializable(), self); @@ -154,19 +154,19 @@ public class ThreePhaseCommitCohort extends AbstractUntypedActor { final DOMStoreThreePhaseCommitCohort cohort; final ActorRef shardActor; final CompositeModification modification; - final String shardName; + final ShardStats shardStats; ThreePhaseCommitCohortCreator(DOMStoreThreePhaseCommitCohort cohort, - ActorRef shardActor, CompositeModification modification, String shardName) { + ActorRef shardActor, CompositeModification modification, ShardStats shardStats) { this.cohort = cohort; this.shardActor = shardActor; this.modification = modification; - this.shardName = shardName; + this.shardStats = shardStats; } @Override public ThreePhaseCommitCohort create() throws Exception { - return new ThreePhaseCommitCohort(cohort, shardActor, modification, shardName); + return new ThreePhaseCommitCohort(cohort, shardActor, modification, shardStats); } } } 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 3c46935d98..e69de29bb2 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 @@ -1,139 +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.cluster.datastore.jmx.mbeans; - - -import com.google.common.base.Preconditions; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.management.InstanceNotFoundException; -import javax.management.MBeanRegistrationException; -import javax.management.MBeanServer; -import javax.management.MalformedObjectNameException; -import javax.management.ObjectName; -import java.lang.management.ManagementFactory; - -/** - * All MBeans should extend this class that help in registering and - * unregistering the MBeans. - * @author Basheeruddin - */ - - -public abstract class AbstractBaseMBean { - - - public static String BASE_JMX_PREFIX = "org.opendaylight.controller:"; - public static String JMX_TYPE_DISTRIBUTED_DATASTORE = "DistributedDatastore"; - public static String JMX_CATEGORY_SHARD = "Shard"; - public static String JMX_CATEGORY_SHARD_MANAGER = "ShardManager"; - - private static final Logger LOG = LoggerFactory - .getLogger(AbstractBaseMBean.class); - - MBeanServer server = ManagementFactory.getPlatformMBeanServer(); - /** - * gets the MBean ObjectName - * - * @return Object name of the MBean - * @throws MalformedObjectNameException - The bean name does not have the right format. - * @throws NullPointerException - The bean name is null - */ - protected ObjectName getMBeanObjectName() - throws MalformedObjectNameException, NullPointerException { - String name = BASE_JMX_PREFIX + "type="+getMBeanType()+",Category="+ - getMBeanCategory() + ",name="+ - getMBeanName(); - - - return new ObjectName(name); - } - - public boolean registerMBean() { - boolean registered = false; - try { - // Object to identify MBean - final ObjectName mbeanName = this.getMBeanObjectName(); - - Preconditions.checkArgument(mbeanName != null, - "Object name of the MBean cannot be null"); - - 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); - - LOG.debug("MBean {} registered successfully", - mbeanName.getCanonicalName()); - registered = true; - } catch (Exception e) { - - LOG.error("registration failed {}", e); - - } - return registered; - } - - - public boolean unregisterMBean() { - boolean unregister = false; - try { - ObjectName mbeanName = this.getMBeanObjectName(); - unregister = true; - unregisterMBean(mbeanName); - } catch (Exception e) { - - LOG.error("Failed when unregistering MBean {}", e); - } - return unregister; - } - - private void unregisterMBean(ObjectName mbeanName) - throws MBeanRegistrationException, InstanceNotFoundException { - - server.unregisterMBean(mbeanName); - - } - - - /** - * @return name of bean - */ - protected abstract String getMBeanName(); - - /** - * @return type of the MBean - */ - protected abstract String getMBeanType(); - - - /** - * @return Category name of teh bean - */ - protected abstract String getMBeanCategory(); - - //require for test cases - public MBeanServer getMBeanServer() { - return server; - } -} 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 2a409c0300..946e525a6d 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 @@ -7,28 +7,41 @@ */ package org.opendaylight.controller.cluster.datastore.jmx.mbeans.shard; -import java.util.HashMap; -import java.util.Map; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; /** * @author Basheeruddin syedbahm@cisco.com * */ public class ShardMBeanFactory { - private static Map shardMBeans = - new HashMap(); - public static ShardStats getShardStatsMBean(String shardName) { - if (shardMBeans.containsKey(shardName)) { - return shardMBeans.get(shardName); - } else { - ShardStats shardStatsMBeanImpl = new ShardStats(shardName); + private static final Logger LOG = LoggerFactory.getLogger(ShardMBeanFactory.class); - if (shardStatsMBeanImpl.registerMBean()) { - shardMBeans.put(shardName, shardStatsMBeanImpl); - } - return shardStatsMBeanImpl; + private static Cache shardMBeansCache = + CacheBuilder.newBuilder().weakValues().build(); + + public static ShardStats getShardStatsMBean(final String shardName, final String mxBeanType) { + final String finalMXBeanType = mxBeanType != null ? mxBeanType : "DistDataStore"; + try { + return shardMBeansCache.get(shardName, new Callable() { + @Override + public ShardStats call() throws Exception { + ShardStats shardStatsMBeanImpl = new ShardStats(shardName, finalMXBeanType); + shardStatsMBeanImpl.registerMBean(); + return shardStatsMBeanImpl; + } + }); + } catch(ExecutionException e) { + LOG.error(String.format("Could not create MXBean for shard: %s", shardName), e); + // Just return an instance that isn't registered. + return new ShardStats(shardName, finalMXBeanType); } } - } 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 22ad8e7f5a..0a1964b053 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 @@ -8,149 +8,193 @@ package org.opendaylight.controller.cluster.datastore.jmx.mbeans.shard; -import org.opendaylight.controller.cluster.datastore.jmx.mbeans.AbstractBaseMBean; +import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.atomic.AtomicLong; + +import org.opendaylight.controller.md.sal.common.util.jmx.AbstractMXBean; +import org.opendaylight.controller.md.sal.common.util.jmx.QueuedNotificationManagerMXBeanImpl; +import org.opendaylight.controller.md.sal.common.util.jmx.ThreadExecutorStats; +import org.opendaylight.controller.md.sal.common.util.jmx.ThreadExecutorStatsMXBeanImpl; +import org.opendaylight.yangtools.util.concurrent.ListenerNotificationQueueStats; +import org.opendaylight.yangtools.util.concurrent.QueuedNotificationManager; import java.text.SimpleDateFormat; import java.util.Date; /** + * Maintains statistics for a shard. + * * @author Basheeruddin syedbahm@cisco.com */ -public class ShardStats extends AbstractBaseMBean implements ShardStatsMBean { +public class ShardStats extends AbstractMXBean implements ShardStatsMXBean { + public static String JMX_CATEGORY_SHARD = "Shards"; - private final String shardName; + private final AtomicLong committedTransactionsCount = new AtomicLong(); - private long committedTransactionsCount = 0L; + private final AtomicLong readOnlyTransactionCount = new AtomicLong(); - private long readOnlyTransactionCount = 0L; + private final AtomicLong writeOnlyTransactionCount = new AtomicLong(); - private long writeOnlyTransactionCount = 0L; - - private long readWriteTransactionCount = 0L; + private final AtomicLong readWriteTransactionCount = new AtomicLong(); private String leader; private String raftState; - private long lastLogTerm = -1L; + private volatile long lastLogTerm = -1L; + + private volatile long lastLogIndex = -1L; - private long lastLogIndex = -1L; + private volatile long currentTerm = -1L; - private long currentTerm = -1L; + private volatile long commitIndex = -1L; - private long commitIndex = -1L; + private volatile long lastApplied = -1L; - private long lastApplied = -1L; + private volatile long lastCommittedTransactionTime; - private Date lastCommittedTransactionTime = new Date(0L); + private final AtomicLong failedTransactionsCount = new AtomicLong(); - private long failedTransactionsCount = 0L; + private final AtomicLong failedReadTransactionsCount = new AtomicLong(); - private long failedReadTransactionsCount = 0L; + private final AtomicLong abortTransactionsCount = new AtomicLong(); - private long abortTransactionsCount = 0L; + private ThreadExecutorStatsMXBeanImpl notificationExecutorStatsBean; - private SimpleDateFormat sdf = + private ThreadExecutorStatsMXBeanImpl dataStoreExecutorStatsBean; + + private QueuedNotificationManagerMXBeanImpl notificationManagerStatsBean; + + private final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); - ShardStats(String shardName) { - this.shardName = shardName; + public ShardStats(String shardName, String mxBeanType) { + super(shardName, mxBeanType, JMX_CATEGORY_SHARD); + } + + public void setDataStoreExecutor(ExecutorService dsExecutor) { + this.dataStoreExecutorStatsBean = new ThreadExecutorStatsMXBeanImpl(dsExecutor, + "notification-executor", getMBeanType(), getMBeanCategory()); } + public void setNotificationManager(QueuedNotificationManager manager) { + this.notificationManagerStatsBean = new QueuedNotificationManagerMXBeanImpl(manager, + "notification-manager", getMBeanType(), getMBeanCategory()); + + this.notificationExecutorStatsBean = new ThreadExecutorStatsMXBeanImpl(manager.getExecutor(), + "data-store-executor", getMBeanType(), getMBeanCategory()); + } @Override public String getShardName() { - return shardName; + return getMBeanName(); } @Override public long getCommittedTransactionsCount() { - return committedTransactionsCount; + return committedTransactionsCount.get(); } - @Override public String getLeader() { + @Override + public String getLeader() { return leader; } - @Override public String getRaftState() { + @Override + public String getRaftState() { return raftState; } - @Override public long getReadOnlyTransactionCount() { - return readOnlyTransactionCount; + @Override + public long getReadOnlyTransactionCount() { + return readOnlyTransactionCount.get(); } - @Override public long getWriteOnlyTransactionCount() { - return writeOnlyTransactionCount; + @Override + public long getWriteOnlyTransactionCount() { + return writeOnlyTransactionCount.get(); } - @Override public long getReadWriteTransactionCount() { - return readWriteTransactionCount; + @Override + public long getReadWriteTransactionCount() { + return readWriteTransactionCount.get(); } - @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; } @Override public String getLastCommittedTransactionTime() { - return sdf.format(lastCommittedTransactionTime); + return sdf.format(new Date(lastCommittedTransactionTime)); } - @Override public long getFailedTransactionsCount() { - return failedTransactionsCount; + @Override + public long getFailedTransactionsCount() { + return failedTransactionsCount.get(); } - @Override public long getFailedReadTransactionsCount() { - return failedReadTransactionsCount; + @Override + public long getFailedReadTransactionsCount() { + return failedReadTransactionsCount.get(); } - @Override public long getAbortTransactionsCount() { - return abortTransactionsCount; + @Override + public long getAbortTransactionsCount() { + return abortTransactionsCount.get(); } public long incrementCommittedTransactionCount() { - return committedTransactionsCount++; + return committedTransactionsCount.incrementAndGet(); } public long incrementReadOnlyTransactionCount() { - return readOnlyTransactionCount++; + return readOnlyTransactionCount.incrementAndGet(); } public long incrementWriteOnlyTransactionCount() { - return writeOnlyTransactionCount++; + return writeOnlyTransactionCount.incrementAndGet(); } public long incrementReadWriteTransactionCount() { - return readWriteTransactionCount++; + return readWriteTransactionCount.incrementAndGet(); } public long incrementFailedTransactionsCount() { - return failedTransactionsCount++; + return failedTransactionsCount.incrementAndGet(); } public long incrementFailedReadTransactionsCount() { - return failedReadTransactionsCount++; + return failedReadTransactionsCount.incrementAndGet(); } - public long incrementAbortTransactionsCount () { return abortTransactionsCount++;} + public long incrementAbortTransactionsCount () + { + return abortTransactionsCount.incrementAndGet(); + } public void setLeader(String leader) { this.leader = leader; @@ -180,49 +224,50 @@ public class ShardStats extends AbstractBaseMBean implements ShardStatsMBean { this.lastApplied = lastApplied; } - - public void setLastCommittedTransactionTime( - Date lastCommittedTransactionTime) { + public void setLastCommittedTransactionTime(long lastCommittedTransactionTime) { this.lastCommittedTransactionTime = lastCommittedTransactionTime; } @Override - protected String getMBeanName() { - return shardName; + public ThreadExecutorStats getDataStoreExecutorStats() { + return dataStoreExecutorStatsBean.toThreadExecutorStats(); + } + + @Override + public ThreadExecutorStats getNotificationMgrExecutorStats() { + return notificationExecutorStatsBean.toThreadExecutorStats(); } @Override - protected String getMBeanType() { - return JMX_TYPE_DISTRIBUTED_DATASTORE; + public List getCurrentNotificationMgrListenerQueueStats() { + return notificationManagerStatsBean.getCurrentListenerQueueStats(); } @Override - protected String getMBeanCategory() { - return JMX_CATEGORY_SHARD; + public int getMaxNotificationMgrListenerQueueSize() { + return notificationManagerStatsBean.getMaxListenerQueueSize(); } /** * resets the counters related to transactions */ - + @Override public void resetTransactionCounters(){ - committedTransactionsCount = 0L; + committedTransactionsCount.set(0); - readOnlyTransactionCount = 0L; + readOnlyTransactionCount.set(0); - writeOnlyTransactionCount = 0L; + writeOnlyTransactionCount.set(0); - readWriteTransactionCount = 0L; + readWriteTransactionCount.set(0); - lastCommittedTransactionTime = new Date(0L); + lastCommittedTransactionTime = 0; - failedTransactionsCount = 0L; + failedTransactionsCount.set(0); - failedReadTransactionsCount = 0L; + failedReadTransactionsCount.set(0); - abortTransactionsCount = 0L; + abortTransactionsCount.set(0); } - - } 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 c16f8421bf..e69de29bb2 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 @@ -1,41 +0,0 @@ -package org.opendaylight.controller.cluster.datastore.jmx.mbeans.shard; - -/** - * @author: syedbahm - */ -public interface ShardStatsMBean { - String getShardName(); - - long getCommittedTransactionsCount(); - - String getLeader(); - - String getRaftState(); - - long getReadOnlyTransactionCount(); - - long getWriteOnlyTransactionCount(); - - long getReadWriteTransactionCount(); - - long getLastLogIndex(); - - long getLastLogTerm(); - - long getCurrentTerm(); - - long getCommitIndex(); - - long getLastApplied(); - - String getLastCommittedTransactionTime(); - - 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/jmx/mbeans/shard/ShardStatsMXBean.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/jmx/mbeans/shard/ShardStatsMXBean.java new file mode 100644 index 0000000000..8deb0ae6db --- /dev/null +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/jmx/mbeans/shard/ShardStatsMXBean.java @@ -0,0 +1,54 @@ +package org.opendaylight.controller.cluster.datastore.jmx.mbeans.shard; + +import java.util.List; + +import org.opendaylight.controller.md.sal.common.util.jmx.ThreadExecutorStats; +import org.opendaylight.yangtools.util.concurrent.ListenerNotificationQueueStats; + +/** + * @author: syedbahm + */ +public interface ShardStatsMXBean { + + String getShardName(); + + long getCommittedTransactionsCount(); + + long getReadOnlyTransactionCount(); + + long getWriteOnlyTransactionCount(); + + long getReadWriteTransactionCount(); + + long getLastLogIndex(); + + long getLastLogTerm(); + + long getCurrentTerm(); + + long getCommitIndex(); + + long getLastApplied(); + + String getLastCommittedTransactionTime(); + + long getFailedTransactionsCount(); + + long getAbortTransactionsCount(); + + long getFailedReadTransactionsCount(); + + String getLeader(); + + String getRaftState(); + + ThreadExecutorStats getDataStoreExecutorStats(); + + ThreadExecutorStats getNotificationMgrExecutorStats(); + + List getCurrentNotificationMgrListenerQueueStats(); + + int getMaxNotificationMgrListenerQueueSize(); + + void resetTransactionCounters(); +} diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/jmx/mbeans/shardmanager/ShardManagerInfo.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/jmx/mbeans/shardmanager/ShardManagerInfo.java index 0c609b459e..99c8daf87d 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/jmx/mbeans/shardmanager/ShardManagerInfo.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/jmx/mbeans/shardmanager/ShardManagerInfo.java @@ -8,44 +8,32 @@ package org.opendaylight.controller.cluster.datastore.jmx.mbeans.shardmanager; -import org.opendaylight.controller.cluster.datastore.jmx.mbeans.AbstractBaseMBean; - import java.util.List; -public class ShardManagerInfo extends AbstractBaseMBean implements - ShardManagerInfoMBean { - - private final String name; - private final List localShards; - - public ShardManagerInfo(String name, List localShards) { - this.name = name; - this.localShards = localShards; - } +import org.opendaylight.controller.md.sal.common.util.jmx.AbstractMXBean; +public class ShardManagerInfo extends AbstractMXBean implements ShardManagerInfoMBean { - @Override protected String getMBeanName() { - return name; - } + public static String JMX_CATEGORY_SHARD_MANAGER = "ShardManager"; - @Override protected String getMBeanType() { - return JMX_TYPE_DISTRIBUTED_DATASTORE; - } + private final List localShards; - @Override protected String getMBeanCategory() { - return JMX_CATEGORY_SHARD_MANAGER; + public ShardManagerInfo(String name, String mxBeanType, List localShards) { + super(name, mxBeanType, JMX_CATEGORY_SHARD_MANAGER); + this.localShards = localShards; } - public static ShardManagerInfo createShardManagerMBean(String name, List localShards){ - ShardManagerInfo shardManagerInfo = new ShardManagerInfo(name, - localShards); + public static ShardManagerInfo createShardManagerMBean(String name, String mxBeanType, + List localShards){ + ShardManagerInfo shardManagerInfo = new ShardManagerInfo(name, mxBeanType, localShards); shardManagerInfo.registerMBean(); return shardManagerInfo; } - @Override public List getLocalShards() { + @Override + public List getLocalShards() { return localShards; } } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/ReadDataReply.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/ReadDataReply.java index c5498ca228..fc6bcff64a 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/ReadDataReply.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/ReadDataReply.java @@ -8,6 +8,7 @@ package org.opendaylight.controller.cluster.datastore.messages; +import com.google.protobuf.ByteString; import org.opendaylight.controller.cluster.datastore.node.NormalizedNodeToNodeCodec; import org.opendaylight.controller.protobuff.messages.transaction.ShardTransactionMessages; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; @@ -46,4 +47,9 @@ public class ReadDataReply implements SerializableMessage{ ShardTransactionMessages.ReadDataReply o = (ShardTransactionMessages.ReadDataReply) serializable; return new ReadDataReply(schemaContext,new NormalizedNodeToNodeCodec(schemaContext).decode(id, o.getNormalizedNode())); } + + public static ByteString getNormalizedNodeByteString(Object serializable){ + ShardTransactionMessages.ReadDataReply o = (ShardTransactionMessages.ReadDataReply) serializable; + return ((ShardTransactionMessages.ReadDataReply) serializable).getNormalizedNode().toByteString(); + } } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/modification/MutableCompositeModification.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/modification/MutableCompositeModification.java index 1a005d856e..04854d26b2 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/modification/MutableCompositeModification.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/modification/MutableCompositeModification.java @@ -52,6 +52,8 @@ public class MutableCompositeModification PersistentMessages.CompositeModification.Builder builder = PersistentMessages.CompositeModification.newBuilder(); + builder.setTimeStamp(System.nanoTime()); + for (Modification m : modifications) { builder.addModification( (PersistentMessages.Modification) m.toSerializable()); 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 c26be148ee..e7a7aab406 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 @@ -1,10 +1,18 @@ package org.opendaylight.controller.config.yang.config.distributed_datastore_provider; +import java.util.concurrent.TimeUnit; + +import org.opendaylight.controller.cluster.datastore.DatastoreContext; import org.opendaylight.controller.cluster.datastore.DistributedDataStoreFactory; -import org.opendaylight.controller.cluster.datastore.DistributedDataStoreProperties; +import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStoreConfigProperties; +import org.osgi.framework.BundleContext; + +import scala.concurrent.duration.Duration; 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) { @@ -32,12 +40,21 @@ public class DistributedConfigDataStoreProviderModule extends props = new ConfigProperties(); } - return DistributedDataStoreFactory.createInstance("config", getConfigSchemaServiceDependency(), - new DistributedDataStoreProperties( + DatastoreContext datastoreContext = new DatastoreContext("DistributedConfigDatastore", + InMemoryDOMDataStoreConfigProperties.create( props.getMaxShardDataChangeExecutorPoolSize().getValue(), props.getMaxShardDataChangeExecutorQueueSize().getValue(), props.getMaxShardDataChangeListenerQueueSize().getValue(), - props.getShardTransactionIdleTimeoutInMinutes().getValue(), - props.getOperationTimeoutInSeconds().getValue())); + props.getMaxShardDataStoreExecutorQueueSize().getValue()), + Duration.create(props.getShardTransactionIdleTimeoutInMinutes().getValue(), + TimeUnit.MINUTES), + props.getOperationTimeoutInSeconds().getValue()); + + return DistributedDataStoreFactory.createInstance("config", getConfigSchemaServiceDependency(), + datastoreContext, 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 a88d09457a..814e6f606a 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 @@ -1,10 +1,18 @@ package org.opendaylight.controller.config.yang.config.distributed_datastore_provider; +import java.util.concurrent.TimeUnit; + +import org.opendaylight.controller.cluster.datastore.DatastoreContext; import org.opendaylight.controller.cluster.datastore.DistributedDataStoreFactory; -import org.opendaylight.controller.cluster.datastore.DistributedDataStoreProperties; +import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStoreConfigProperties; +import org.osgi.framework.BundleContext; + +import scala.concurrent.duration.Duration; 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) { @@ -32,14 +40,22 @@ public class DistributedOperationalDataStoreProviderModule extends props = new OperationalProperties(); } - return DistributedDataStoreFactory.createInstance("operational", - getOperationalSchemaServiceDependency(), - new DistributedDataStoreProperties( + DatastoreContext datastoreContext = new DatastoreContext("DistributedOperationalDatastore", + InMemoryDOMDataStoreConfigProperties.create( props.getMaxShardDataChangeExecutorPoolSize().getValue(), props.getMaxShardDataChangeExecutorQueueSize().getValue(), props.getMaxShardDataChangeListenerQueueSize().getValue(), - props.getShardTransactionIdleTimeoutInMinutes().getValue(), - props.getOperationTimeoutInSeconds().getValue())); + props.getMaxShardDataStoreExecutorQueueSize().getValue()), + Duration.create(props.getShardTransactionIdleTimeoutInMinutes().getValue(), + TimeUnit.MINUTES), + props.getOperationTimeoutInSeconds().getValue()); + + return DistributedDataStoreFactory.createInstance("operational", + getOperationalSchemaServiceDependency(), datastoreContext, 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/yang/distributed-datastore-provider.yang b/opendaylight/md-sal/sal-distributed-datastore/src/main/yang/distributed-datastore-provider.yang index d50be2ca0e..82bc5e29bc 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 @@ -66,7 +66,13 @@ module distributed-datastore-provider { type non-zero-uint16-type; description "The maximum queue size for each shard's data store data change listeners."; } - + + leaf max-shard-data-store-executor-queue-size { + default 5000; + type non-zero-uint16-type; + description "The maximum queue size for each shard's data store executor."; + } + leaf shard-transaction-idle-timeout-in-minutes { default 10; type non-zero-uint16-type; diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/AbstractActorTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/AbstractActorTest.java index e23a76b0b2..4c550a768c 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/AbstractActorTest.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/AbstractActorTest.java @@ -22,11 +22,6 @@ public abstract class AbstractActorTest { @BeforeClass public static void setUpClass() throws IOException { - File journal = new File("journal"); - - if(journal.exists()) { - FileUtils.deleteDirectory(journal); - } System.setProperty("shard.persistent", "false"); system = ActorSystem.create("test"); @@ -36,12 +31,21 @@ public abstract class AbstractActorTest { public static void tearDownClass() throws IOException { JavaTestKit.shutdownActorSystem(system); system = null; + } + protected static void deletePersistenceFiles() throws IOException { File journal = new File("journal"); if(journal.exists()) { FileUtils.deleteDirectory(journal); } + + File snapshots = new File("snapshots"); + + if(snapshots.exists()){ + FileUtils.deleteDirectory(snapshots); + } + } protected ActorSystem getSystem() { 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 6f131f301f..50367e66ce 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 @@ -64,7 +64,7 @@ public class BasicIntegrationTest extends AbstractActorTest { final SchemaContext schemaContext = TestModel.createTestContext(); DatastoreContext datastoreContext = new DatastoreContext(); - final Props props = Shard.props(identifier, Collections.EMPTY_MAP, datastoreContext); + final Props props = Shard.props(identifier, Collections.EMPTY_MAP, datastoreContext, TestModel.createTestContext()); 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/DistributedDataStoreIntegrationTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/DistributedDataStoreIntegrationTest.java index 21aa00e9e0..8a7b50d20c 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/DistributedDataStoreIntegrationTest.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/DistributedDataStoreIntegrationTest.java @@ -78,7 +78,7 @@ public class DistributedDataStoreIntegrationTest { final DistributedDataStore distributedDataStore = new DistributedDataStore(getSystem(), "config", new MockClusterWrapper(), configuration, - new DistributedDataStoreProperties()); + new DatastoreContext()); distributedDataStore.onGlobalContextUpdated(TestModel.createTestContext()); diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/DistributedDataStoreTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/DistributedDataStoreTest.java index cb473cb936..aeb47de888 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/DistributedDataStoreTest.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/DistributedDataStoreTest.java @@ -71,7 +71,7 @@ public class DistributedDataStoreTest extends AbstractActorTest{ new DistributedDataStore(actorSystem, "config", mock(ClusterWrapper.class), mock(Configuration.class), - new DistributedDataStoreProperties()); + new DatastoreContext()); verify(actorSystem).actorOf(any(Props.class), eq("shardmanager-config")); } 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 1feefd1c1f..02201f7cd1 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 @@ -15,8 +15,10 @@ import org.opendaylight.controller.cluster.datastore.messages.LocalShardFound; import org.opendaylight.controller.cluster.datastore.messages.LocalShardNotFound; 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.MockClusterWrapper; import org.opendaylight.controller.cluster.datastore.utils.MockConfiguration; +import org.opendaylight.controller.md.cluster.datastore.model.TestModel; import scala.concurrent.duration.Duration; import static junit.framework.Assert.assertEquals; @@ -71,6 +73,8 @@ public class ShardManagerTest { final TestActorRef subject = TestActorRef.create(system, props); + subject.tell(new UpdateSchemaContext(TestModel.createTestContext()), getRef()); + new Within(duration("10 seconds")) { @Override protected void run() { @@ -132,6 +136,8 @@ public class ShardManagerTest { final TestActorRef subject = TestActorRef.create(system, props); + subject.tell(new UpdateSchemaContext(TestModel.createTestContext()), getRef()); + 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 4466e50f96..766dcb7268 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 @@ -1,10 +1,15 @@ package org.opendaylight.controller.cluster.datastore; import akka.actor.ActorRef; +import akka.actor.ActorSystem; import akka.actor.Props; import akka.event.Logging; import akka.testkit.JavaTestKit; - +import akka.testkit.TestActorRef; +import com.google.common.base.Optional; +import com.google.common.util.concurrent.CheckedFuture; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.MoreExecutors; import org.junit.Assert; import org.junit.Test; import org.opendaylight.controller.cluster.datastore.identifiers.ShardIdentifier; @@ -16,21 +21,32 @@ import org.opendaylight.controller.cluster.datastore.messages.PeerAddressResolve import org.opendaylight.controller.cluster.datastore.messages.RegisterChangeListener; import org.opendaylight.controller.cluster.datastore.messages.RegisterChangeListenerReply; import org.opendaylight.controller.cluster.datastore.messages.UpdateSchemaContext; +import org.opendaylight.controller.cluster.datastore.node.NormalizedNodeToNodeCodec; +import org.opendaylight.controller.cluster.raft.base.messages.CaptureSnapshot; import org.opendaylight.controller.md.cluster.datastore.model.SchemaContextHelper; import org.opendaylight.controller.md.cluster.datastore.model.TestModel; import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker; 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.ReadFailedException; +import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStore; +import org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages; import org.opendaylight.controller.protobuff.messages.transaction.ShardTransactionMessages.CreateTransactionReply; +import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadTransaction; +import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort; +import org.opendaylight.controller.sal.core.spi.data.DOMStoreWriteTransaction; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes; +import java.io.IOException; import java.util.Collections; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.ExecutionException; -import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; public class ShardTest extends AbstractActorTest { @@ -44,7 +60,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, DATA_STORE_CONTEXT); + final Props props = Shard.props(identifier, Collections.EMPTY_MAP, DATA_STORE_CONTEXT, TestModel.createTestContext()); final ActorRef subject = getSystem().actorOf(props, "testCreateTransactionChain"); @@ -103,7 +119,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, DATA_STORE_CONTEXT); + final Props props = Shard.props(identifier, Collections.EMPTY_MAP, DATA_STORE_CONTEXT, TestModel.createTestContext()); final ActorRef subject = getSystem().actorOf(props, "testRegisterChangeListener"); @@ -165,7 +181,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, DATA_STORE_CONTEXT); + final Props props = Shard.props(identifier, Collections.EMPTY_MAP, DATA_STORE_CONTEXT, TestModel.createTestContext()); final ActorRef subject = getSystem().actorOf(props, "testCreateTransaction"); @@ -227,7 +243,7 @@ public class ShardTest extends AbstractActorTest { .shardName("inventory").type("config").build(); peerAddresses.put(identifier, null); - final Props props = Shard.props(identifier, peerAddresses, DATA_STORE_CONTEXT); + final Props props = Shard.props(identifier, peerAddresses, DATA_STORE_CONTEXT, TestModel.createTestContext()); final ActorRef subject = getSystem().actorOf(props, "testPeerAddressResolved"); @@ -245,6 +261,157 @@ public class ShardTest extends AbstractActorTest { }}; } + @Test + public void testApplySnapshot() throws ExecutionException, InterruptedException { + Map peerAddresses = new HashMap<>(); + + final ShardIdentifier identifier = + ShardIdentifier.builder().memberName("member-1") + .shardName("inventory").type("config").build(); + + peerAddresses.put(identifier, null); + final Props props = Shard.props(identifier, peerAddresses, DATA_STORE_CONTEXT, TestModel.createTestContext()); + + TestActorRef ref = TestActorRef.create(getSystem(), props); + + ref.underlyingActor().updateSchemaContext(TestModel.createTestContext()); + + NormalizedNodeToNodeCodec codec = + new NormalizedNodeToNodeCodec(TestModel.createTestContext()); + + ref.underlyingActor().writeToStore(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME)); + + NormalizedNode expected = ref.underlyingActor().readStore(); + + NormalizedNodeMessages.Container encode = codec + .encode(YangInstanceIdentifier.builder().build(), expected); + + + ref.underlyingActor().applySnapshot(encode.getNormalizedNode().toByteString()); + + NormalizedNode actual = ref.underlyingActor().readStore(); + + assertEquals(expected, actual); + } + + private static class ShardTestKit extends JavaTestKit { + + private ShardTestKit(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); + + } + + } + + @Test + public void testCreateSnapshot() throws IOException, InterruptedException { + new ShardTestKit(getSystem()) {{ + final ShardIdentifier identifier = + ShardIdentifier.builder().memberName("member-1") + .shardName("inventory").type("config").build(); + + final Props props = Shard.props(identifier, Collections.EMPTY_MAP, DATA_STORE_CONTEXT, TestModel.createTestContext()); + final ActorRef subject = + getSystem().actorOf(props, "testCreateSnapshot"); + + // Wait for a specific log message to show up + this.waitForLogMessage(Logging.Info.class, subject, "Switching from state Candidate to Leader"); + + + new Within(duration("3 seconds")) { + @Override + protected void run() { + + subject.tell( + new UpdateSchemaContext(TestModel.createTestContext()), + getRef()); + + subject.tell(new CaptureSnapshot(-1,-1,-1,-1), + getRef()); + + waitForLogMessage(Logging.Debug.class, subject, "CaptureSnapshotReply received by actor"); + } + }; + + Thread.sleep(2000); + deletePersistenceFiles(); + }}; + } + + /** + * This test simply verifies that the applySnapShot logic will work + * @throws ReadFailedException + */ + @Test + public void testInMemoryDataStoreRestore() throws ReadFailedException { + InMemoryDOMDataStore store = new InMemoryDOMDataStore("test", MoreExecutors.listeningDecorator( + MoreExecutors.sameThreadExecutor()), MoreExecutors.sameThreadExecutor()); + + store.onGlobalContextUpdated(TestModel.createTestContext()); + + DOMStoreWriteTransaction putTransaction = store.newWriteOnlyTransaction(); + putTransaction.write(TestModel.TEST_PATH, + ImmutableNodes.containerNode(TestModel.TEST_QNAME)); + commitTransaction(putTransaction); + + + NormalizedNode expected = readStore(store); + + DOMStoreWriteTransaction writeTransaction = store.newWriteOnlyTransaction(); + + writeTransaction.delete(YangInstanceIdentifier.builder().build()); + writeTransaction.write(YangInstanceIdentifier.builder().build(), expected); + + commitTransaction(writeTransaction); + + NormalizedNode actual = readStore(store); + + assertEquals(expected, actual); + + } + + private NormalizedNode readStore(InMemoryDOMDataStore store) throws ReadFailedException { + DOMStoreReadTransaction transaction = store.newReadOnlyTransaction(); + CheckedFuture>, ReadFailedException> read = + transaction.read(YangInstanceIdentifier.builder().build()); + + Optional> optional = read.checkedGet(); + + NormalizedNode normalizedNode = optional.get(); + + transaction.close(); + + return normalizedNode; + } + + private void commitTransaction(DOMStoreWriteTransaction transaction) { + DOMStoreThreePhaseCommitCohort commitCohort = transaction.ready(); + ListenableFuture future = + commitCohort.preCommit(); + try { + future.get(); + future = commitCohort.commit(); + future.get(); + } catch (InterruptedException | ExecutionException e) { + } + } + private AsyncDataChangeListener> noOpDataChangeListener() { return new AsyncDataChangeListener>() { @Override 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 71eb1f1603..c5968c358f 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 @@ -9,6 +9,7 @@ import com.google.common.util.concurrent.MoreExecutors; import org.junit.BeforeClass; import org.junit.Test; +import org.opendaylight.controller.cluster.datastore.jmx.mbeans.shard.ShardStats; import org.opendaylight.controller.cluster.datastore.messages.CloseTransactionChain; import org.opendaylight.controller.cluster.datastore.messages.CloseTransactionChainReply; import org.opendaylight.controller.cluster.datastore.messages.CreateTransaction; @@ -32,6 +33,8 @@ public class ShardTransactionChainTest extends AbstractActorTest { private static final String mockShardName = "mockShardName"; + private final ShardStats shardStats = new ShardStats(mockShardName, "DataStore"); + @BeforeClass public static void staticSetup() { store.onGlobalContextUpdated(testSchemaContext); @@ -41,7 +44,7 @@ public class ShardTransactionChainTest extends AbstractActorTest { public void testOnReceiveCreateTransaction() throws Exception { new JavaTestKit(getSystem()) {{ final Props props = ShardTransactionChain.props(store.createTransactionChain(), - testSchemaContext, DATA_STORE_CONTEXT, mockShardName); + testSchemaContext, DATA_STORE_CONTEXT, shardStats); final ActorRef subject = getSystem().actorOf(props, "testCreateTransaction"); new Within(duration("1 seconds")) { @@ -79,7 +82,7 @@ public class ShardTransactionChainTest extends AbstractActorTest { public void testOnReceiveCloseTransactionChain() throws Exception { new JavaTestKit(getSystem()) {{ final Props props = ShardTransactionChain.props(store.createTransactionChain(), - testSchemaContext, DATA_STORE_CONTEXT,mockShardName ); + testSchemaContext, DATA_STORE_CONTEXT, shardStats ); 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 4fe60f6467..869f475787 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 @@ -20,6 +20,7 @@ import com.google.common.util.concurrent.MoreExecutors; import org.junit.BeforeClass; import org.junit.Test; import org.opendaylight.controller.cluster.datastore.identifiers.ShardIdentifier; +import org.opendaylight.controller.cluster.datastore.jmx.mbeans.shard.ShardStats; import org.opendaylight.controller.cluster.datastore.node.utils.serialization.NormalizedNodeSerializer; import org.opendaylight.controller.md.cluster.datastore.model.TestModel; import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException; @@ -59,19 +60,24 @@ public class ShardTransactionFailureTest extends AbstractActorTest { private final DatastoreContext datastoreContext = new DatastoreContext(); + private final ShardStats shardStats = new ShardStats(SHARD_IDENTIFIER.toString(), "DataStore"); + @BeforeClass public static void staticSetup() { store.onGlobalContextUpdated(testSchemaContext); } + private ActorRef createShard(){ + return getSystem().actorOf(Shard.props(SHARD_IDENTIFIER, Collections.EMPTY_MAP, new DatastoreContext(), TestModel.createTestContext())); + } + @Test(expected = ReadFailedException.class) public void testNegativeReadWithReadOnlyTransactionClosed() throws Throwable { - final ActorRef shard = - getSystem().actorOf(Shard.props(SHARD_IDENTIFIER, Collections.EMPTY_MAP, new DatastoreContext())); + final ActorRef shard = createShard(); final Props props = ShardTransaction.props(store.newReadOnlyTransaction(), shard, - testSchemaContext, datastoreContext,SHARD_IDENTIFIER.toString()); + testSchemaContext, datastoreContext, shardStats); final TestActorRef subject = TestActorRef .create(getSystem(), props, @@ -98,10 +104,9 @@ public class ShardTransactionFailureTest extends AbstractActorTest { public void testNegativeReadWithReadWriteTransactionClosed() throws Throwable { - final ActorRef shard = - getSystem().actorOf(Shard.props(SHARD_IDENTIFIER, Collections.EMPTY_MAP, new DatastoreContext())); + final ActorRef shard = createShard(); final Props props = ShardTransaction.props(store.newReadWriteTransaction(), shard, - testSchemaContext, datastoreContext,SHARD_IDENTIFIER.toString()); + testSchemaContext, datastoreContext, shardStats); final TestActorRef subject = TestActorRef .create(getSystem(), props, @@ -128,10 +133,9 @@ public class ShardTransactionFailureTest extends AbstractActorTest { public void testNegativeExistsWithReadWriteTransactionClosed() throws Throwable { - final ActorRef shard = - getSystem().actorOf(Shard.props(SHARD_IDENTIFIER, Collections.EMPTY_MAP, new DatastoreContext())); + final ActorRef shard = createShard(); final Props props = ShardTransaction.props(store.newReadWriteTransaction(), shard, - testSchemaContext, datastoreContext,SHARD_IDENTIFIER.toString()); + testSchemaContext, datastoreContext, shardStats); final TestActorRef subject = TestActorRef .create(getSystem(), props, @@ -158,10 +162,9 @@ public class ShardTransactionFailureTest extends AbstractActorTest { public void testNegativeWriteWithTransactionReady() throws Exception { - final ActorRef shard = - getSystem().actorOf(Shard.props(SHARD_IDENTIFIER, Collections.EMPTY_MAP, new DatastoreContext())); + final ActorRef shard = createShard(); final Props props = ShardTransaction.props(store.newWriteOnlyTransaction(), shard, - testSchemaContext, datastoreContext,SHARD_IDENTIFIER.toString()); + testSchemaContext, datastoreContext, shardStats); final TestActorRef subject = TestActorRef .create(getSystem(), props, @@ -191,10 +194,9 @@ public class ShardTransactionFailureTest extends AbstractActorTest { public void testNegativeReadWriteWithTransactionReady() throws Exception { - final ActorRef shard = - getSystem().actorOf(Shard.props(SHARD_IDENTIFIER, Collections.EMPTY_MAP, new DatastoreContext())); + final ActorRef shard = createShard(); final Props props = ShardTransaction.props(store.newReadWriteTransaction(), shard, - testSchemaContext, datastoreContext,SHARD_IDENTIFIER.toString()); + testSchemaContext, datastoreContext, shardStats); final TestActorRef subject = TestActorRef .create(getSystem(), props, @@ -229,10 +231,9 @@ public class ShardTransactionFailureTest extends AbstractActorTest { public void testNegativeMergeTransactionReady() throws Exception { - final ActorRef shard = - getSystem().actorOf(Shard.props(SHARD_IDENTIFIER, Collections.EMPTY_MAP, new DatastoreContext())); + final ActorRef shard = createShard(); final Props props = ShardTransaction.props(store.newReadWriteTransaction(), shard, - testSchemaContext, datastoreContext,SHARD_IDENTIFIER.toString()); + testSchemaContext, datastoreContext, shardStats); final TestActorRef subject = TestActorRef .create(getSystem(), props, "testNegativeMergeTransactionReady"); @@ -262,10 +263,9 @@ public class ShardTransactionFailureTest extends AbstractActorTest { public void testNegativeDeleteDataWhenTransactionReady() throws Exception { - final ActorRef shard = - getSystem().actorOf(Shard.props(SHARD_IDENTIFIER, Collections.EMPTY_MAP, new DatastoreContext())); + final ActorRef shard = createShard(); final Props props = ShardTransaction.props(store.newReadWriteTransaction(), shard, - testSchemaContext, datastoreContext,SHARD_IDENTIFIER.toString()); + testSchemaContext, datastoreContext, shardStats); 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 ff2ee08f94..0beb00b435 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 @@ -13,6 +13,7 @@ import org.junit.BeforeClass; import org.junit.Test; import org.opendaylight.controller.cluster.datastore.exceptions.UnknownMessageException; import org.opendaylight.controller.cluster.datastore.identifiers.ShardIdentifier; +import org.opendaylight.controller.cluster.datastore.jmx.mbeans.shard.ShardStats; import org.opendaylight.controller.cluster.datastore.messages.CloseTransaction; import org.opendaylight.controller.cluster.datastore.messages.CloseTransactionReply; import org.opendaylight.controller.cluster.datastore.messages.DataExists; @@ -62,18 +63,24 @@ public class ShardTransactionTest extends AbstractActorTest { private DatastoreContext datastoreContext = new DatastoreContext(); + private final ShardStats shardStats = new ShardStats(SHARD_IDENTIFIER.toString(), "DataStore"); + @BeforeClass public static void staticSetup() { store.onGlobalContextUpdated(testSchemaContext); } + private ActorRef createShard(){ + return getSystem().actorOf(Shard.props(SHARD_IDENTIFIER, + Collections.EMPTY_MAP, new DatastoreContext(), TestModel.createTestContext())); + } + @Test public void testOnReceiveReadData() throws Exception { new JavaTestKit(getSystem()) {{ - final ActorRef shard = getSystem().actorOf(Shard.props(SHARD_IDENTIFIER, - Collections.EMPTY_MAP, new DatastoreContext())); + final ActorRef shard = createShard(); final Props props = ShardTransaction.props(store.newReadOnlyTransaction(), shard, - testSchemaContext, datastoreContext,SHARD_IDENTIFIER.toString()); + testSchemaContext, datastoreContext, shardStats); final ActorRef subject = getSystem().actorOf(props, "testReadData"); new Within(duration("1 seconds")) { @@ -113,10 +120,9 @@ public class ShardTransactionTest extends AbstractActorTest { @Test public void testOnReceiveReadDataWhenDataNotFound() throws Exception { new JavaTestKit(getSystem()) {{ - final ActorRef shard = getSystem().actorOf(Shard.props(SHARD_IDENTIFIER, - Collections.EMPTY_MAP, new DatastoreContext())); + final ActorRef shard = createShard(); final Props props = ShardTransaction.props( store.newReadOnlyTransaction(), shard, - testSchemaContext, datastoreContext,SHARD_IDENTIFIER.toString()); + testSchemaContext, datastoreContext, shardStats); final ActorRef subject = getSystem().actorOf(props, "testReadDataWhenDataNotFound"); new Within(duration("1 seconds")) { @@ -157,10 +163,9 @@ public class ShardTransactionTest extends AbstractActorTest { @Test public void testOnReceiveDataExistsPositive() throws Exception { new JavaTestKit(getSystem()) {{ - final ActorRef shard = getSystem().actorOf(Shard.props(SHARD_IDENTIFIER, - Collections.EMPTY_MAP, new DatastoreContext())); + final ActorRef shard = createShard(); final Props props = ShardTransaction.props(store.newReadOnlyTransaction(), shard, - testSchemaContext, datastoreContext,SHARD_IDENTIFIER.toString()); + testSchemaContext, datastoreContext, shardStats); final ActorRef subject = getSystem().actorOf(props, "testDataExistsPositive"); new Within(duration("1 seconds")) { @@ -200,10 +205,9 @@ public class ShardTransactionTest extends AbstractActorTest { @Test public void testOnReceiveDataExistsNegative() throws Exception { new JavaTestKit(getSystem()) {{ - final ActorRef shard = getSystem().actorOf(Shard.props(SHARD_IDENTIFIER, - Collections.EMPTY_MAP, new DatastoreContext())); + final ActorRef shard = createShard(); final Props props = ShardTransaction.props(store.newReadOnlyTransaction(), shard, - testSchemaContext, datastoreContext,SHARD_IDENTIFIER.toString()); + testSchemaContext, datastoreContext, shardStats); final ActorRef subject = getSystem().actorOf(props, "testDataExistsNegative"); new Within(duration("1 seconds")) { @@ -278,10 +282,9 @@ public class ShardTransactionTest extends AbstractActorTest { @Test public void testOnReceiveWriteData() throws Exception { new JavaTestKit(getSystem()) {{ - final ActorRef shard = getSystem().actorOf(Shard.props(SHARD_IDENTIFIER, - Collections.EMPTY_MAP, new DatastoreContext())); + final ActorRef shard = createShard(); final Props props = ShardTransaction.props(store.newWriteOnlyTransaction(), shard, - testSchemaContext, datastoreContext,SHARD_IDENTIFIER.toString()); + testSchemaContext, datastoreContext, shardStats); final ActorRef subject = getSystem().actorOf(props, "testWriteData"); @@ -319,10 +322,9 @@ public class ShardTransactionTest extends AbstractActorTest { @Test public void testOnReceiveMergeData() throws Exception { new JavaTestKit(getSystem()) {{ - final ActorRef shard = getSystem().actorOf(Shard.props(SHARD_IDENTIFIER, - Collections.EMPTY_MAP, new DatastoreContext())); + final ActorRef shard = createShard(); final Props props = ShardTransaction.props(store.newReadWriteTransaction(), shard, - testSchemaContext, datastoreContext,SHARD_IDENTIFIER.toString()); + testSchemaContext, datastoreContext, shardStats); final ActorRef subject = getSystem().actorOf(props, "testMergeData"); @@ -361,10 +363,9 @@ public class ShardTransactionTest extends AbstractActorTest { @Test public void testOnReceiveDeleteData() throws Exception { new JavaTestKit(getSystem()) {{ - final ActorRef shard = getSystem().actorOf(Shard.props(SHARD_IDENTIFIER, - Collections.EMPTY_MAP, new DatastoreContext())); + final ActorRef shard = createShard(); final Props props = ShardTransaction.props( store.newWriteOnlyTransaction(), shard, - testSchemaContext, datastoreContext,SHARD_IDENTIFIER.toString()); + testSchemaContext, datastoreContext, shardStats); final ActorRef subject = getSystem().actorOf(props, "testDeleteData"); @@ -401,10 +402,9 @@ public class ShardTransactionTest extends AbstractActorTest { @Test public void testOnReceiveReadyTransaction() throws Exception { new JavaTestKit(getSystem()) {{ - final ActorRef shard = getSystem().actorOf(Shard.props(SHARD_IDENTIFIER, - Collections.EMPTY_MAP, new DatastoreContext())); + final ActorRef shard = createShard(); final Props props = ShardTransaction.props( store.newReadWriteTransaction(), shard, - testSchemaContext, datastoreContext,SHARD_IDENTIFIER.toString()); + testSchemaContext, datastoreContext, shardStats); final ActorRef subject = getSystem().actorOf(props, "testReadyTransaction"); @@ -440,10 +440,9 @@ public class ShardTransactionTest extends AbstractActorTest { @Test public void testOnReceiveCloseTransaction() throws Exception { new JavaTestKit(getSystem()) {{ - final ActorRef shard = getSystem().actorOf(Shard.props(SHARD_IDENTIFIER, - Collections.EMPTY_MAP, new DatastoreContext())); + final ActorRef shard = createShard(); final Props props = ShardTransaction.props(store.newReadWriteTransaction(), shard, - testSchemaContext, datastoreContext,SHARD_IDENTIFIER.toString()); + testSchemaContext, datastoreContext, shardStats); final ActorRef subject = getSystem().actorOf(props, "testCloseTransaction"); @@ -491,10 +490,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 DatastoreContext())); + final ActorRef shard = createShard(); final Props props = ShardTransaction.props(store.newReadOnlyTransaction(), shard, - testSchemaContext, datastoreContext,SHARD_IDENTIFIER.toString()); + testSchemaContext, datastoreContext, shardStats); final TestActorRef subject = TestActorRef.apply(props,getSystem()); subject.receive(new DeleteData(TestModel.TEST_PATH).toSerializable(), ActorRef.noSender()); @@ -503,14 +501,14 @@ public class ShardTransactionTest extends AbstractActorTest { @Test public void testShardTransactionInactivity() { - datastoreContext = new DatastoreContext(InMemoryDOMDataStoreConfigProperties.getDefault(), - Duration.create(500, TimeUnit.MILLISECONDS)); + datastoreContext = new DatastoreContext("Test", + InMemoryDOMDataStoreConfigProperties.getDefault(), + Duration.create(500, TimeUnit.MILLISECONDS), 5); new JavaTestKit(getSystem()) {{ - final ActorRef shard = getSystem().actorOf(Shard.props(SHARD_IDENTIFIER, - Collections.EMPTY_MAP, new DatastoreContext())); + final ActorRef shard = createShard(); final Props props = ShardTransaction.props(store.newReadWriteTransaction(), shard, - testSchemaContext, datastoreContext,SHARD_IDENTIFIER.toString()); + testSchemaContext, datastoreContext, shardStats); 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 e39b9abd65..4e4c34bcbc 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 @@ -23,6 +23,7 @@ import org.junit.BeforeClass; import org.junit.Test; import org.mockito.Mockito; import org.opendaylight.controller.cluster.datastore.identifiers.ShardIdentifier; +import org.opendaylight.controller.cluster.datastore.jmx.mbeans.shard.ShardStats; import org.opendaylight.controller.cluster.datastore.messages.ForwardedCommitTransaction; import org.opendaylight.controller.cluster.datastore.modification.CompositeModification; import org.opendaylight.controller.cluster.datastore.modification.Modification; @@ -66,6 +67,7 @@ public class ThreePhaseCommitCohortFailureTest extends AbstractActorTest { private final DatastoreContext datastoreContext = new DatastoreContext(); + private final ShardStats shardStats = new ShardStats(SHARD_IDENTIFIER.toString(), "DataStore"); @BeforeClass public static void staticSetup() { @@ -74,18 +76,20 @@ public class ThreePhaseCommitCohortFailureTest extends AbstractActorTest { private final FiniteDuration ASK_RESULT_DURATION = Duration.create(5000, TimeUnit.MILLISECONDS); + private ActorRef createShard(){ + return getSystem().actorOf(Shard.props(SHARD_IDENTIFIER, Collections.EMPTY_MAP, datastoreContext, TestModel.createTestContext())); + } @Test(expected = TestException.class) public void testNegativeAbortResultsInException() throws Exception { - final ActorRef shard = getSystem().actorOf(Shard.props(SHARD_IDENTIFIER, - Collections.EMPTY_MAP, datastoreContext)); + final ActorRef shard = createShard(); final DOMStoreThreePhaseCommitCohort mockCohort = Mockito .mock(DOMStoreThreePhaseCommitCohort.class); final CompositeModification mockComposite = Mockito.mock(CompositeModification.class); final Props props = - ThreePhaseCommitCohort.props(mockCohort, shard, mockComposite,SHARD_IDENTIFIER.toString()); + ThreePhaseCommitCohort.props(mockCohort, shard, mockComposite, shardStats); final TestActorRef subject = TestActorRef .create(getSystem(), props, @@ -107,14 +111,13 @@ public class ThreePhaseCommitCohortFailureTest extends AbstractActorTest { @Test(expected = OptimisticLockFailedException.class) public void testNegativeCanCommitResultsInException() throws Exception { - final ActorRef shard = getSystem().actorOf(Shard.props(SHARD_IDENTIFIER, - Collections.EMPTY_MAP, datastoreContext)); + final ActorRef shard = createShard(); final DOMStoreThreePhaseCommitCohort mockCohort = Mockito .mock(DOMStoreThreePhaseCommitCohort.class); final CompositeModification mockComposite = Mockito.mock(CompositeModification.class); final Props props = - ThreePhaseCommitCohort.props(mockCohort, shard, mockComposite,SHARD_IDENTIFIER.toString()); + ThreePhaseCommitCohort.props(mockCohort, shard, mockComposite, shardStats); final TestActorRef subject = TestActorRef .create(getSystem(), props, @@ -139,14 +142,13 @@ public class ThreePhaseCommitCohortFailureTest extends AbstractActorTest { @Test(expected = TestException.class) public void testNegativePreCommitResultsInException() throws Exception { - final ActorRef shard = getSystem().actorOf(Shard.props(SHARD_IDENTIFIER, - Collections.EMPTY_MAP, datastoreContext)); + final ActorRef shard = createShard(); final DOMStoreThreePhaseCommitCohort mockCohort = Mockito .mock(DOMStoreThreePhaseCommitCohort.class); final CompositeModification mockComposite = Mockito.mock(CompositeModification.class); final Props props = - ThreePhaseCommitCohort.props(mockCohort, shard, mockComposite,SHARD_IDENTIFIER.toString()); + ThreePhaseCommitCohort.props(mockCohort, shard, mockComposite, shardStats); final TestActorRef subject = TestActorRef .create(getSystem(), props, @@ -170,12 +172,12 @@ public class ThreePhaseCommitCohortFailureTest extends AbstractActorTest { public void testNegativeCommitResultsInException() throws Exception { final TestActorRef subject = TestActorRef.create(getSystem(), - Shard.props(SHARD_IDENTIFIER, Collections.EMPTY_MAP, datastoreContext), + Shard.props(SHARD_IDENTIFIER, Collections.EMPTY_MAP, datastoreContext, TestModel.createTestContext()), "testNegativeCommitResultsInException"); final ActorRef shardTransaction = getSystem().actorOf(ShardTransaction.props(store.newReadWriteTransaction(), subject, - testSchemaContext, datastoreContext,SHARD_IDENTIFIER.toString())); + testSchemaContext, datastoreContext, shardStats)); ShardTransactionMessages.WriteData writeData = ShardTransactionMessages.WriteData.newBuilder() 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 c4d0b85fb5..e9df3ecd49 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 @@ -11,10 +11,12 @@ import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; -import org.opendaylight.controller.cluster.datastore.jmx.mbeans.AbstractBaseMBean; +import org.opendaylight.controller.md.sal.common.util.jmx.AbstractMXBean; import javax.management.MBeanServer; import javax.management.ObjectName; + +import java.lang.management.ManagementFactory; import java.text.SimpleDateFormat; import java.util.Date; @@ -26,11 +28,11 @@ public class ShardStatsTest { @Before public void setUp() throws Exception { - shardStats = new ShardStats("shard-1"); + shardStats = new ShardStats("shard-1", "DataStore"); shardStats.registerMBean(); - mbeanServer = shardStats.getMBeanServer(); + mbeanServer = ManagementFactory.getPlatformMBeanServer(); String objectName = - AbstractBaseMBean.BASE_JMX_PREFIX + "type=" + shardStats + AbstractMXBean.BASE_JMX_PREFIX + "type=" + shardStats .getMBeanType() + ",Category=" + shardStats.getMBeanCategory() + ",name=" + shardStats.getMBeanName(); @@ -46,7 +48,7 @@ public class ShardStatsTest { public void testGetShardName() throws Exception { Object attribute = mbeanServer.getAttribute(testMBeanName, "ShardName"); - Assert.assertEquals((String) attribute, "shard-1"); + Assert.assertEquals(attribute, "shard-1"); } @@ -60,7 +62,7 @@ public class ShardStatsTest { //now let us get from MBeanServer what is the transaction count. Object attribute = mbeanServer.getAttribute(testMBeanName, "CommittedTransactionsCount"); - Assert.assertEquals((Long) attribute, (Long) 3L); + Assert.assertEquals(attribute, 3L); } @@ -71,13 +73,13 @@ public class ShardStatsTest { Assert.assertEquals(shardStats.getLastCommittedTransactionTime(), sdf.format(new Date(0L))); long millis = System.currentTimeMillis(); - shardStats.setLastCommittedTransactionTime(new Date(millis)); + shardStats.setLastCommittedTransactionTime(millis); //now let us get from MBeanServer what is the transaction count. Object attribute = mbeanServer.getAttribute(testMBeanName, "LastCommittedTransactionTime"); - Assert.assertEquals((String) attribute, sdf.format(new Date(millis))); - Assert.assertNotEquals((String) attribute, + Assert.assertEquals(attribute, sdf.format(new Date(millis))); + Assert.assertNotEquals(attribute, sdf.format(new Date(millis - 1))); } @@ -92,7 +94,7 @@ public class ShardStatsTest { //now let us get from MBeanServer what is the transaction count. Object attribute = mbeanServer.getAttribute(testMBeanName, "FailedTransactionsCount"); - Assert.assertEquals((Long) attribute, (Long) 2L); + Assert.assertEquals(attribute, 2L); } @Test @@ -105,7 +107,7 @@ public class ShardStatsTest { //now let us get from MBeanServer what is the transaction count. Object attribute = mbeanServer.getAttribute(testMBeanName, "AbortTransactionsCount"); - Assert.assertEquals((Long) attribute, (Long) 2L); + Assert.assertEquals(attribute, 2L); } @Test @@ -118,7 +120,7 @@ public class ShardStatsTest { //now let us get from MBeanServer what is the transaction count. Object attribute = mbeanServer.getAttribute(testMBeanName, "FailedReadTransactionsCount"); - Assert.assertEquals((Long) attribute, (Long) 2L); + Assert.assertEquals(attribute, 2L); } @Test @@ -132,7 +134,7 @@ public class ShardStatsTest { //now let us get from MBeanServer what is the transaction count. Object attribute = mbeanServer.getAttribute(testMBeanName, "CommittedTransactionsCount"); - Assert.assertEquals((Long) attribute, (Long) 3L); + Assert.assertEquals(attribute, 3L); //let us increment FailedReadTransactions count and then check shardStats.incrementFailedReadTransactionsCount(); @@ -142,7 +144,7 @@ public class ShardStatsTest { //now let us get from MBeanServer what is the transaction count. attribute = mbeanServer.getAttribute(testMBeanName, "FailedReadTransactionsCount"); - Assert.assertEquals((Long) attribute, (Long) 2L); + Assert.assertEquals(attribute, 2L); //here we will reset the counters and check the above ones are 0 after reset @@ -151,11 +153,11 @@ public class ShardStatsTest { //now let us get from MBeanServer what is the transaction count. attribute = mbeanServer.getAttribute(testMBeanName, "CommittedTransactionsCount"); - Assert.assertEquals((Long) attribute, (Long) 0L); + Assert.assertEquals(attribute, 0L); attribute = mbeanServer.getAttribute(testMBeanName, "FailedReadTransactionsCount"); - Assert.assertEquals((Long) attribute, (Long) 0L); + Assert.assertEquals(attribute, 0L); } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/modification/MutableCompositeModificationTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/modification/MutableCompositeModificationTest.java index 7a21c8cdc5..03aaace0e3 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/modification/MutableCompositeModificationTest.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/modification/MutableCompositeModificationTest.java @@ -1,28 +1,42 @@ package org.opendaylight.controller.cluster.datastore.modification; import com.google.common.base.Optional; -import junit.framework.Assert; import org.junit.Test; import org.opendaylight.controller.md.cluster.datastore.model.TestModel; import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadWriteTransaction; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; + public class MutableCompositeModificationTest extends AbstractModificationTest { - @Test - public void testApply() throws Exception { + @Test + public void testApply() throws Exception { + + MutableCompositeModification compositeModification = new MutableCompositeModification(); + compositeModification.addModification(new WriteModification(TestModel.TEST_PATH, + ImmutableNodes.containerNode(TestModel.TEST_QNAME), TestModel.createTestContext())); + + DOMStoreReadWriteTransaction transaction = store.newReadWriteTransaction(); + compositeModification.apply(transaction); + commitTransaction(transaction); + + Optional> data = readData(TestModel.TEST_PATH); - MutableCompositeModification compositeModification = new MutableCompositeModification(); - compositeModification.addModification(new WriteModification(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME), TestModel.createTestContext())); + assertNotNull(data.get()); + assertEquals(TestModel.TEST_QNAME, data.get().getNodeType()); + } - DOMStoreReadWriteTransaction transaction = store.newReadWriteTransaction(); - compositeModification.apply(transaction); - commitTransaction(transaction); + @Test + public void testEverySerializedCompositeModificationObjectMustBeDifferent(){ + MutableCompositeModification compositeModification = new MutableCompositeModification(); + compositeModification.addModification(new WriteModification(TestModel.TEST_PATH, + ImmutableNodes.containerNode(TestModel.TEST_QNAME), TestModel.createTestContext())); - Optional> data = readData(TestModel.TEST_PATH); + assertNotEquals(compositeModification.toSerializable(), compositeModification.toSerializable()); - Assert.assertNotNull(data.get()); - Assert.assertEquals(TestModel.TEST_QNAME, data.get().getNodeType()); - } + } } 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 b98844ba64..1ab12ff26f 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 @@ -22,9 +22,8 @@ public class InMemoryConfigDataStoreProviderModule extends org.opendaylight.cont @Override public java.lang.AutoCloseable createInstance() { - - InMemoryDOMDataStore dataStore = InMemoryDOMDataStoreFactory.create( - "DOM-CFG", getSchemaServiceDependency(), + InMemoryDOMDataStore dataStore = InMemoryDOMDataStoreFactory.create("DOM-CFG", getSchemaServiceDependency(), + getDebugTransactions(), InMemoryDOMDataStoreConfigProperties.create(getMaxDataChangeExecutorPoolSize(), getMaxDataChangeExecutorQueueSize(), getMaxDataChangeListenerQueueSize(), getMaxDataStoreExecutorQueueSize())); 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 4532452c65..9358552579 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 @@ -23,7 +23,7 @@ public class InMemoryOperationalDataStoreProviderModule extends org.opendaylight @Override public java.lang.AutoCloseable createInstance() { InMemoryDOMDataStore dataStore = InMemoryDOMDataStoreFactory.create("DOM-OPER", getSchemaServiceDependency(), - InMemoryDOMDataStoreConfigProperties.create(getMaxDataChangeExecutorPoolSize(), + getDebugTransactions(), InMemoryDOMDataStoreConfigProperties.create(getMaxDataChangeExecutorPoolSize(), getMaxDataChangeExecutorQueueSize(), getMaxDataChangeListenerQueueSize(), getMaxDataStoreExecutorQueueSize())); diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/AbstractDOMStoreTransaction.java b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/AbstractDOMStoreTransaction.java index 8a190c115f..6cc5939047 100644 --- a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/AbstractDOMStoreTransaction.java +++ b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/AbstractDOMStoreTransaction.java @@ -7,25 +7,26 @@ */ package org.opendaylight.controller.md.sal.dom.store.impl; -import org.opendaylight.controller.sal.core.spi.data.DOMStoreTransaction; - import com.google.common.base.Objects; import com.google.common.base.Objects.ToStringHelper; import com.google.common.base.Preconditions; +import org.opendaylight.controller.sal.core.spi.data.DOMStoreTransaction; +import org.slf4j.Logger; + /** * Abstract DOM Store Transaction * * Convenience super implementation of DOM Store transaction which provides * common implementation of {@link #toString()} and {@link #getIdentifier()}. - * - * */ abstract class AbstractDOMStoreTransaction implements DOMStoreTransaction { + private final Throwable debugContext; private final Object identifier; - protected AbstractDOMStoreTransaction(final Object identifier) { - this.identifier = Preconditions.checkNotNull(identifier,"Identifier must not be null."); + protected AbstractDOMStoreTransaction(final Object identifier, final boolean debug) { + this.identifier = Preconditions.checkNotNull(identifier, "Identifier must not be null."); + this.debugContext = debug ? new Throwable().fillInStackTrace() : null; } @Override @@ -33,6 +34,12 @@ abstract class AbstractDOMStoreTransaction implements DOMStoreTransaction { return identifier; } + protected final void warnDebugContext(final Logger logger) { + if (debugContext != null) { + logger.warn("Transaction {} has been allocated in the following context", identifier, debugContext); + } + } + @Override public final String toString() { return addToStringAttributes(Objects.toStringHelper(this)).toString(); 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 476356a19e..3e74861816 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 @@ -16,14 +16,11 @@ 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.AsyncDataChangeListener; import org.opendaylight.controller.md.sal.common.api.data.OptimisticLockFailedException; @@ -91,7 +88,7 @@ public class InMemoryDOMDataStore implements DOMStore, Identifiable, Sch private final ExecutorService dataChangeListenerExecutor; private final ExecutorService domStoreExecutor; - + private final boolean debugTransactions; private final String name; private volatile AutoCloseable closeable; @@ -99,15 +96,17 @@ public class InMemoryDOMDataStore implements DOMStore, Identifiable, Sch public InMemoryDOMDataStore(final String name, final ExecutorService domStoreExecutor, final ExecutorService dataChangeListenerExecutor) { this(name, domStoreExecutor, dataChangeListenerExecutor, - InMemoryDOMDataStoreConfigProperties.DEFAULT_MAX_DATA_CHANGE_LISTENER_QUEUE_SIZE); + InMemoryDOMDataStoreConfigProperties.DEFAULT_MAX_DATA_CHANGE_LISTENER_QUEUE_SIZE, false); } public InMemoryDOMDataStore(final String name, final ExecutorService domStoreExecutor, - final ExecutorService dataChangeListenerExecutor, final int maxDataChangeListenerQueueSize) { + final ExecutorService dataChangeListenerExecutor, final int maxDataChangeListenerQueueSize, + final boolean debugTransactions) { this.name = Preconditions.checkNotNull(name); this.domStoreExecutor = Preconditions.checkNotNull(domStoreExecutor); this.listeningExecutor = MoreExecutors.listeningDecorator(this.domStoreExecutor); this.dataChangeListenerExecutor = Preconditions.checkNotNull(dataChangeListenerExecutor); + this.debugTransactions = debugTransactions; dataChangeListenerNotificationManager = new QueuedNotificationManager<>(this.dataChangeListenerExecutor, @@ -134,17 +133,17 @@ public class InMemoryDOMDataStore implements DOMStore, Identifiable, Sch @Override public DOMStoreReadTransaction newReadOnlyTransaction() { - return new SnapshotBackedReadTransaction(nextIdentifier(), dataTree.takeSnapshot()); + return new SnapshotBackedReadTransaction(nextIdentifier(), debugTransactions, dataTree.takeSnapshot()); } @Override public DOMStoreReadWriteTransaction newReadWriteTransaction() { - return new SnapshotBackedReadWriteTransaction(nextIdentifier(), dataTree.takeSnapshot(), this); + return new SnapshotBackedReadWriteTransaction(nextIdentifier(), debugTransactions, dataTree.takeSnapshot(), this); } @Override public DOMStoreWriteTransaction newWriteOnlyTransaction() { - return new SnapshotBackedWriteTransaction(nextIdentifier(), dataTree.takeSnapshot(), this); + return new SnapshotBackedWriteTransaction(nextIdentifier(), debugTransactions, dataTree.takeSnapshot(), this); } @Override @@ -171,6 +170,10 @@ public class InMemoryDOMDataStore implements DOMStore, Identifiable, Sch } } + boolean getDebugTransactions() { + return debugTransactions; + } + @Override public >> ListenerRegistration registerChangeListener( final YangInstanceIdentifier path, final L listener, final DataChangeScope scope) { @@ -242,7 +245,7 @@ public class InMemoryDOMDataStore implements DOMStore, Identifiable, Sch } else { snapshot = dataTree.takeSnapshot(); } - return new SnapshotBackedReadTransaction(nextIdentifier(), snapshot); + return new SnapshotBackedReadTransaction(nextIdentifier(), getDebugTransactions(), snapshot); } @Override @@ -256,7 +259,7 @@ public class InMemoryDOMDataStore implements DOMStore, Identifiable, Sch snapshot = dataTree.takeSnapshot(); } final SnapshotBackedReadWriteTransaction ret = new SnapshotBackedReadWriteTransaction(nextIdentifier(), - snapshot, this); + getDebugTransactions(), snapshot, this); latestOutstandingTx = ret; return ret; } @@ -271,8 +274,8 @@ public class InMemoryDOMDataStore implements DOMStore, Identifiable, Sch } else { snapshot = dataTree.takeSnapshot(); } - final SnapshotBackedWriteTransaction ret = new SnapshotBackedWriteTransaction(nextIdentifier(), snapshot, - this); + final SnapshotBackedWriteTransaction ret = new SnapshotBackedWriteTransaction(nextIdentifier(), + getDebugTransactions(), snapshot, this); latestOutstandingTx = ret; return ret; } @@ -384,10 +387,12 @@ public class InMemoryDOMDataStore implements DOMStore, Identifiable, Sch } catch (ConflictingModificationAppliedException e) { LOG.warn("Store Tx: {} Conflicting modification for {}.", transaction.getIdentifier(), e.getPath()); + transaction.warnDebugContext(LOG); throw new OptimisticLockFailedException("Optimistic lock failed.",e); } catch (DataValidationFailedException e) { LOG.warn("Store Tx: {} Data Precondition failed for {}.", transaction.getIdentifier(), e.getPath(), e); + transaction.warnDebugContext(LOG); throw new TransactionCommitFailedException("Data did not pass validation.",e); } } 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 052fb2b89b..dc1482c6ab 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 @@ -5,13 +5,10 @@ * 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 java.util.concurrent.ExecutorService; - import javax.annotation.Nullable; - import org.opendaylight.controller.sal.core.api.model.SchemaService; import org.opendaylight.yangtools.util.concurrent.SpecialExecutors; @@ -42,6 +39,22 @@ public final class InMemoryDOMDataStoreFactory { public static InMemoryDOMDataStore create(final String name, @Nullable final SchemaService schemaService, @Nullable final InMemoryDOMDataStoreConfigProperties properties) { + return create(name, schemaService, false, properties); + } + + /** + * Creates an InMemoryDOMDataStore instance. + * + * @param name the name of the data store + * @param schemaService the SchemaService to which to register the data store. + * @param debugTransactions enable transaction debugging + * @param properties configuration properties for the InMemoryDOMDataStore instance. If null, + * default property values are used. + * @return an InMemoryDOMDataStore instance + */ + public static InMemoryDOMDataStore create(final String name, + @Nullable final SchemaService schemaService, final boolean debugTransactions, + @Nullable final InMemoryDOMDataStoreConfigProperties properties) { InMemoryDOMDataStoreConfigProperties actualProperties = properties; if(actualProperties == null) { @@ -64,7 +77,7 @@ public final class InMemoryDOMDataStoreFactory { InMemoryDOMDataStore dataStore = new InMemoryDOMDataStore(name, domStoreExecutor, dataChangeListenerExecutor, - actualProperties.getMaxDataChangeListenerQueueSize()); + actualProperties.getMaxDataChangeListenerQueueSize(), debugTransactions); 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/SnapshotBackedReadTransaction.java b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/SnapshotBackedReadTransaction.java index 2caa76d49d..ed95796499 100644 --- a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/SnapshotBackedReadTransaction.java +++ b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/SnapshotBackedReadTransaction.java @@ -7,10 +7,13 @@ */ package org.opendaylight.controller.md.sal.dom.store.impl; +import static com.google.common.base.Preconditions.checkNotNull; + import com.google.common.base.Optional; import com.google.common.base.Preconditions; import com.google.common.util.concurrent.CheckedFuture; import com.google.common.util.concurrent.Futures; + import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException; import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadTransaction; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; @@ -19,8 +22,6 @@ import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import static com.google.common.base.Preconditions.checkNotNull; - /** * * Implementation of read-only transaction backed by {@link DataTreeSnapshot} @@ -35,8 +36,8 @@ final class SnapshotBackedReadTransaction extends AbstractDOMStoreTransaction private static final Logger LOG = LoggerFactory.getLogger(SnapshotBackedReadTransaction.class); private volatile DataTreeSnapshot stableSnapshot; - public SnapshotBackedReadTransaction(final Object identifier, final DataTreeSnapshot snapshot) { - super(identifier); + public SnapshotBackedReadTransaction(final Object identifier, final boolean debug, final DataTreeSnapshot snapshot) { + super(identifier, debug); this.stableSnapshot = Preconditions.checkNotNull(snapshot); LOG.debug("ReadOnly Tx: {} allocated with snapshot {}", identifier, snapshot); } @@ -66,7 +67,7 @@ final class SnapshotBackedReadTransaction extends AbstractDOMStoreTransaction } @Override - public CheckedFuture exists(YangInstanceIdentifier path) { + public CheckedFuture exists(final YangInstanceIdentifier path) { LOG.debug("Tx: {} Exists: {}", getIdentifier(), path); checkNotNull(path, "Path must not be null."); diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/SnapshotBackedReadWriteTransaction.java b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/SnapshotBackedReadWriteTransaction.java index ce7043fd47..2ae7425bbb 100644 --- a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/SnapshotBackedReadWriteTransaction.java +++ b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/SnapshotBackedReadWriteTransaction.java @@ -9,19 +9,19 @@ package org.opendaylight.controller.md.sal.dom.store.impl; import static com.google.common.base.Preconditions.checkNotNull; -import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification; -import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot; +import com.google.common.base.Optional; +import com.google.common.util.concurrent.CheckedFuture; +import com.google.common.util.concurrent.Futures; + import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException; import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadWriteTransaction; 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.DataTreeModification; +import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.common.base.Optional; -import com.google.common.util.concurrent.CheckedFuture; -import com.google.common.util.concurrent.Futures; - /** * Implementation of Read-Write transaction which is backed by {@link DataTreeSnapshot} * and executed according to {@link TransactionReadyPrototype}. @@ -39,9 +39,9 @@ class SnapshotBackedReadWriteTransaction extends SnapshotBackedWriteTransaction * @param snapshot Snapshot which will be modified. * @param readyImpl Implementation of ready method. */ - protected SnapshotBackedReadWriteTransaction(final Object identifier, final DataTreeSnapshot snapshot, - final TransactionReadyPrototype store) { - super(identifier, snapshot, store); + protected SnapshotBackedReadWriteTransaction(final Object identifier, final boolean debug, + final DataTreeSnapshot snapshot, final TransactionReadyPrototype store) { + super(identifier, debug, snapshot, store); } @Override @@ -62,8 +62,8 @@ class SnapshotBackedReadWriteTransaction extends SnapshotBackedWriteTransaction } } - @Override public CheckedFuture exists( - YangInstanceIdentifier path) { + @Override + public CheckedFuture exists(final YangInstanceIdentifier path) { try { return Futures.immediateCheckedFuture( read(path).checkedGet().isPresent()); diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/SnapshotBackedWriteTransaction.java b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/SnapshotBackedWriteTransaction.java index 34532ab98f..6129df7478 100644 --- a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/SnapshotBackedWriteTransaction.java +++ b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/SnapshotBackedWriteTransaction.java @@ -9,20 +9,19 @@ package org.opendaylight.controller.md.sal.dom.store.impl; import static com.google.common.base.Preconditions.checkState; +import com.google.common.base.Objects.ToStringHelper; +import com.google.common.base.Preconditions; +import com.google.common.base.Throwables; import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort; import org.opendaylight.controller.sal.core.spi.data.DOMStoreWriteTransaction; 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.DataTreeModification; +import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.common.base.Objects.ToStringHelper; -import com.google.common.base.Preconditions; -import com.google.common.base.Throwables; -import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot; -import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification; - /** * Implementation of Write transaction which is backed by * {@link DataTreeSnapshot} and executed according to @@ -46,9 +45,9 @@ class SnapshotBackedWriteTransaction extends AbstractDOMStoreTransaction impleme * @param readyImpl * Implementation of ready method. */ - public SnapshotBackedWriteTransaction(final Object identifier, final DataTreeSnapshot snapshot, - final TransactionReadyPrototype readyImpl) { - super(identifier); + public SnapshotBackedWriteTransaction(final Object identifier, final boolean debug, + final DataTreeSnapshot snapshot, final TransactionReadyPrototype readyImpl) { + super(identifier, debug); mutableTree = snapshot.newModification(); this.readyImpl = Preconditions.checkNotNull(readyImpl, "readyImpl must not be null."); LOG.debug("Write Tx: {} allocated with snapshot {}", identifier, snapshot); 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 7d19a64446..5ffe4d60ca 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 @@ -1,4 +1,3 @@ - module opendaylight-inmemory-datastore-provider { yang-version 1; @@ -7,8 +6,8 @@ module opendaylight-inmemory-datastore-provider { import config { prefix config; revision-date 2013-04-05; } import rpc-context { prefix rpcx; revision-date 2013-06-17; } - import opendaylight-config-dom-datastore {prefix config-dom-store-spi;} - import opendaylight-operational-dom-datastore {prefix operational-dom-store-spi;} + import opendaylight-config-dom-datastore {prefix config-dom-store-spi;} + import opendaylight-operational-dom-datastore {prefix operational-dom-store-spi;} import opendaylight-md-sal-dom {prefix sal;} description @@ -28,11 +27,11 @@ module opendaylight-inmemory-datastore-provider { // This is the definition of the service implementation as a module identity. - identity inmemory-operational-datastore-provider { - base config:module-type; - config:provided-service operational-dom-store-spi:operational-dom-datastore; - config:java-name-prefix InMemoryOperationalDataStoreProvider; - } + identity inmemory-operational-datastore-provider { + base config:module-type; + config:provided-service operational-dom-store-spi:operational-dom-datastore; + config:java-name-prefix InMemoryOperationalDataStoreProvider; + } grouping datastore-configuration { leaf max-data-change-executor-queue-size { @@ -52,12 +51,16 @@ 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."; } + leaf debug-transactions { + type boolean; + default false; + description "Enable transaction lifecycle debugging."; + } } // Augments the 'configuration' choice node under modules/module. diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDataStoreTest.java b/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDataStoreTest.java index c609e13e79..04e19493db 100644 --- a/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDataStoreTest.java +++ b/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDataStoreTest.java @@ -7,10 +7,18 @@ */ package org.opendaylight.controller.md.sal.dom.store.impl; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + import com.google.common.base.Optional; import com.google.common.util.concurrent.CheckedFuture; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; + +import java.util.concurrent.ExecutionException; + import org.junit.Before; import org.junit.Ignore; import org.junit.Test; @@ -32,13 +40,6 @@ import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes; import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder; import org.opendaylight.yangtools.yang.model.api.SchemaContext; -import java.util.concurrent.ExecutionException; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - public class InMemoryDataStoreTest { @@ -271,7 +272,7 @@ public class InMemoryDataStoreTest { Mockito.doThrow( new RuntimeException( "mock ex" ) ).when( mockSnapshot ) .readNode( Mockito.any( YangInstanceIdentifier.class ) ); - DOMStoreReadTransaction readTx = new SnapshotBackedReadTransaction( "1", mockSnapshot ); + DOMStoreReadTransaction readTx = new SnapshotBackedReadTransaction("1", true, mockSnapshot); doReadAndThrowEx( readTx ); } @@ -296,12 +297,12 @@ public class InMemoryDataStoreTest { .readNode( Mockito.any( YangInstanceIdentifier.class ) ); Mockito.doReturn( mockModification ).when( mockSnapshot ).newModification(); TransactionReadyPrototype mockReady = Mockito.mock( TransactionReadyPrototype.class ); - DOMStoreReadTransaction readTx = new SnapshotBackedReadWriteTransaction( "1", mockSnapshot, mockReady ); + DOMStoreReadTransaction readTx = new SnapshotBackedReadWriteTransaction("1", false, mockSnapshot, mockReady); doReadAndThrowEx( readTx ); } - private void doReadAndThrowEx( DOMStoreReadTransaction readTx ) throws Throwable { + private void doReadAndThrowEx( final DOMStoreReadTransaction readTx ) throws Throwable { try { readTx.read(TestModel.TEST_PATH).get(); diff --git a/opendaylight/md-sal/sal-remoterpc-connector/pom.xml b/opendaylight/md-sal/sal-remoterpc-connector/pom.xml index 41cdd59d6b..8d454c4bd6 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/pom.xml +++ b/opendaylight/md-sal/sal-remoterpc-connector/pom.xml @@ -189,6 +189,7 @@ !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/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/utils/ActorUtil.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/ActorUtil.java index ca14fecb4c..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,44 +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); public static final FiniteDuration GOSSIP_TICK_INTERVAL = Duration.create(500, TimeUnit.MILLISECONDS); public static final String MAILBOX = "bounded-mailbox"; - - - /** - * 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); - } - - } 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/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/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/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/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 821290eca2..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 @@ -5,6 +5,8 @@ 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); } @@ -27,8 +29,12 @@ public class RestConnectorModule extends org.opendaylight.controller.config.yang // Register it with the Broker getDomBrokerDependency().registerProvider(instance); + if(runtimeRegistration != null){ + runtimeRegistration.close(); + } - getRootRuntimeBeanRegistratorWrapper().register(instance); + runtimeRegistration = + getRootRuntimeBeanRegistratorWrapper().register(instance); return instance; } diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfProviderImpl.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfProviderImpl.java index c7c9cc0dc5..abadbf6cb8 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfProviderImpl.java +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfProviderImpl.java @@ -7,9 +7,6 @@ */ package org.opendaylight.controller.sal.restconf.impl; -import java.math.BigInteger; -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; @@ -28,19 +25,21 @@ import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types. import org.opendaylight.yangtools.concepts.ListenerRegistration; import org.opendaylight.yangtools.yang.model.api.SchemaContextListener; -public class RestconfProviderImpl implements Provider, AutoCloseable, RestConnector, RestConnectorRuntimeMXBean { +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); @@ -65,9 +64,12 @@ public class RestconfProviderImpl implements Provider, AutoCloseable, RestConnec @Override public void close() { + if (listenerRegistration != null) { listenerRegistration.close(); } + + WebSocketServer.destroyInstance(); webSocketServerThread.interrupt(); } 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; } }