Initial support for multiple-shards 82/8882/6
authorMoiz Raja <moraja@cisco.com>
Wed, 9 Jul 2014 22:11:23 +0000 (15:11 -0700)
committerMoiz Raja <moraja@cisco.com>
Mon, 28 Jul 2014 20:56:33 +0000 (13:56 -0700)
Change-Id: Ic58d2dc9198b88c689143a9d85a612016b6bfa22
Signed-off-by: Moiz Raja <moraja@cisco.com>
27 files changed:
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ClusterWrapper.java [new file with mode: 0644]
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ClusterWrapperImpl.java [new file with mode: 0644]
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/Configuration.java [new file with mode: 0644]
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ConfigurationImpl.java [new file with mode: 0644]
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DistributedDataStore.java
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DistributedDataStoreFactory.java
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardManager.java
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/utils/ActorContext.java
opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ConfigurationImplTest.java [new file with mode: 0644]
opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/DataChangeListenerProxyTest.java
opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/DataChangeListenerRegistrationProxyTest.java
opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/DistributedDataStoreIntegrationTest.java
opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardManagerTest.java
opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardTest.java
opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/TransactionProxyTest.java
opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/utils/MockActorContext.java
opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/utils/MockClusterWrapper.java [new file with mode: 0644]
opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/utils/MockConfiguration.java [new file with mode: 0644]
opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/utils/TestUtils.java
opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/md/cluster/datastore/model/CarsModel.java [new file with mode: 0644]
opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/md/cluster/datastore/model/PeopleModel.java [new file with mode: 0644]
opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/md/cluster/datastore/model/SampleModelsTest.java [new file with mode: 0644]
opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/md/cluster/datastore/model/SchemaContextHelper.java [new file with mode: 0644]
opendaylight/md-sal/sal-distributed-datastore/src/test/resources/cars.yang [new file with mode: 0644]
opendaylight/md-sal/sal-distributed-datastore/src/test/resources/module-shards.conf [new file with mode: 0644]
opendaylight/md-sal/sal-distributed-datastore/src/test/resources/modules.conf [new file with mode: 0644]
opendaylight/md-sal/sal-distributed-datastore/src/test/resources/people.yang [new file with mode: 0644]

diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ClusterWrapper.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ClusterWrapper.java
new file mode 100644 (file)
index 0000000..2eac240
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * 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;
+
+import akka.actor.ActorRef;
+
+public interface ClusterWrapper {
+    void subscribeToMemberEvents(ActorRef actorRef);
+    String getCurrentMemberName();
+}
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ClusterWrapperImpl.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ClusterWrapperImpl.java
new file mode 100644 (file)
index 0000000..142aacd
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * 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;
+
+import akka.actor.ActorRef;
+import akka.actor.ActorSystem;
+import akka.cluster.Cluster;
+import akka.cluster.ClusterEvent;
+
+public class ClusterWrapperImpl implements ClusterWrapper {
+    private final Cluster cluster;
+    private final String currentMemberName;
+
+    public ClusterWrapperImpl(ActorSystem actorSystem){
+        cluster = Cluster.get(actorSystem);
+        currentMemberName = (String) cluster.getSelfRoles().toArray()[0];
+
+    }
+
+    public void subscribeToMemberEvents(ActorRef actorRef){
+        cluster.subscribe(actorRef, ClusterEvent.initialStateAsEvents(),
+            ClusterEvent.MemberEvent.class,
+            ClusterEvent.UnreachableMember.class);
+    }
+
+    public String getCurrentMemberName() {
+        return currentMemberName;
+    }
+}
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/Configuration.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/Configuration.java
new file mode 100644 (file)
index 0000000..dff1fd4
--- /dev/null
@@ -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.cluster.datastore;
+
+import java.util.List;
+
+public interface Configuration {
+    List<String> getMemberShardNames(String memberName);
+}
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ConfigurationImpl.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ConfigurationImpl.java
new file mode 100644 (file)
index 0000000..c8c8299
--- /dev/null
@@ -0,0 +1,159 @@
+/*
+ * 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;
+
+import com.typesafe.config.Config;
+import com.typesafe.config.ConfigFactory;
+import com.typesafe.config.ConfigObject;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class ConfigurationImpl implements Configuration {
+    private final List<ModuleShard> moduleShards = new ArrayList<>();
+    private final List<Module> modules = new ArrayList<>();
+
+
+    public ConfigurationImpl(String moduleShardsConfigPath,
+        String modulesConfigPath){
+        Config moduleShardsConfig = ConfigFactory.load(moduleShardsConfigPath);
+        Config modulesConfig = ConfigFactory.load(modulesConfigPath);
+
+        readModuleShards(moduleShardsConfig);
+
+        readModules(modulesConfig);
+    }
+
+    public List<String> getMemberShardNames(String memberName){
+
+        List<String> shards = new ArrayList();
+        for(ModuleShard ms : moduleShards){
+            for(Shard s : ms.getShards()){
+                for(String m : s.getReplicas()){
+                    if(memberName.equals(m)){
+                        shards.add(s.getName());
+                    }
+                }
+            }
+        }
+        return shards;
+
+    }
+
+
+    private void readModules(Config modulesConfig) {
+        List<? extends ConfigObject> modulesConfigObjectList =
+            modulesConfig.getObjectList("modules");
+
+        for(ConfigObject o : modulesConfigObjectList){
+            ConfigObjectWrapper w = new ConfigObjectWrapper(o);
+            modules.add(new Module(w.stringValue("name"), w.stringValue(
+                "namespace"), w.stringValue("sharding-strategy")));
+        }
+    }
+
+    private void readModuleShards(Config moduleShardsConfig) {
+        List<? extends ConfigObject> moduleShardsConfigObjectList =
+            moduleShardsConfig.getObjectList("module-shards");
+
+        for(ConfigObject moduleShardConfigObject : moduleShardsConfigObjectList){
+
+            String moduleName = moduleShardConfigObject.get("name").unwrapped().toString();
+
+            List<? extends ConfigObject> shardsConfigObjectList =
+                moduleShardConfigObject.toConfig().getObjectList("shards");
+
+            List<Shard> shards = new ArrayList<>();
+
+            for(ConfigObject shard : shardsConfigObjectList){
+                String shardName = shard.get("name").unwrapped().toString();
+                List<String> replicas = shard.toConfig().getStringList("replicas");
+                shards.add(new Shard(shardName, replicas));
+            }
+
+            this.moduleShards.add(new ModuleShard(moduleName, shards));
+        }
+    }
+
+
+    public static class ModuleShard {
+        private final String moduleName;
+        private final List<Shard> shards;
+
+        public ModuleShard(String moduleName, List<Shard> shards) {
+            this.moduleName = moduleName;
+            this.shards = shards;
+        }
+
+        public String getModuleName() {
+            return moduleName;
+        }
+
+        public List<Shard> getShards() {
+            return shards;
+        }
+    }
+
+    public static class Shard {
+        private final String name;
+        private final List<String> replicas;
+
+        Shard(String name, List<String> replicas) {
+            this.name = name;
+            this.replicas = replicas;
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public List<String> getReplicas() {
+            return replicas;
+        }
+    }
+
+    public static class Module {
+
+        private final String name;
+        private final String nameSpace;
+        private final String shardingStrategy;
+
+        Module(String name, String nameSpace, String shardingStrategy) {
+            this.name = name;
+            this.nameSpace = nameSpace;
+            this.shardingStrategy = shardingStrategy;
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public String getNameSpace() {
+            return nameSpace;
+        }
+
+        public String getShardingStrategy() {
+            return shardingStrategy;
+        }
+    }
+
+
+    private static class ConfigObjectWrapper{
+
+        private final ConfigObject configObject;
+
+        ConfigObjectWrapper(ConfigObject configObject){
+            this.configObject = configObject;
+        }
+
+        public String stringValue(String name){
+            return configObject.get(name).unwrapped().toString();
+        }
+    }
+}
index 3c12fa6..c7f811b 100644 (file)
@@ -59,8 +59,8 @@ public class DistributedDataStore implements DOMStore, SchemaContextListener, Au
     private final ExecutorService executor =
         Executors.newFixedThreadPool(10);
 
-    public DistributedDataStore(ActorSystem actorSystem, String type) {
-        this(new ActorContext(actorSystem, actorSystem.actorOf(ShardManager.props(type), "shardmanager-" + type)), type);
+    public DistributedDataStore(ActorSystem actorSystem, String type, ClusterWrapper cluster, Configuration configuration) {
+        this(new ActorContext(actorSystem, actorSystem.actorOf(ShardManager.props(type, cluster, configuration), "shardmanager-" + type), configuration), type);
     }
 
     public DistributedDataStore(ActorContext actorContext, String type) {
index f19cb92..907b856 100644 (file)
@@ -8,12 +8,14 @@
 
 package org.opendaylight.controller.cluster.datastore;
 
+import akka.actor.ActorSystem;
 import org.opendaylight.controller.sal.core.api.model.SchemaService;
 
 public class DistributedDataStoreFactory {
     public static DistributedDataStore createInstance(String name, SchemaService schemaService){
+        ActorSystem actorSystem = ActorSystemFactory.getInstance();
         final DistributedDataStore dataStore =
-            new DistributedDataStore(ActorSystemFactory.getInstance(), name);
+            new DistributedDataStore(actorSystem, name, new ClusterWrapperImpl(actorSystem), new ConfigurationImpl("module-shards.conf", "modules.conf"));
         schemaService
             .registerSchemaServiceListener(dataStore);
         return dataStore;
index f55774f..8123652 100644 (file)
@@ -49,54 +49,82 @@ import java.util.Map;
  */
 public class ShardManager extends AbstractUntypedActor {
 
-  // Stores a mapping between a shard name and the address of the current primary
-  private final Map<String, Address> shardNameToPrimaryAddress = new HashMap<>();
-
-  // Stores a mapping between a member name and the address of the member
-  private final Map<String, Address> memberNameToAddress = new HashMap<>();
-
-  // Stores a mapping between the shard name and all the members on which a replica of that shard are available
-  private final Map<String, List<String>> shardNameToMembers = new HashMap<>();
-
-  private final LoggingAdapter log = Logging.getLogger(getContext().system(), this);
-
-  private final ActorPath defaultShardPath;
-
-  /**
-   *
-   * @param type defines the kind of data that goes into shards created by this shard manager. Examples of type would be
-   *             configuration or operational
-   */
-  private ShardManager(String type){
-    ActorRef actor = getContext().actorOf(Shard.props("shard-" + Shard.DEFAULT_NAME + "-" + type), "shard-" + Shard.DEFAULT_NAME + "-" + type);
-    defaultShardPath = actor.path();
-  }
-
-  public static Props props(final String type){
-    return Props.create(new Creator<ShardManager>(){
-
-      @Override
-      public ShardManager create() throws Exception {
-        return new ShardManager(type);
-      }
-    });
-  }
-
-  @Override
-  public void handleReceive(Object message) throws Exception {
-    if (message instanceof FindPrimary) {
-      FindPrimary msg = ((FindPrimary) message);
-      String shardName = msg.getShardName();
-      if(Shard.DEFAULT_NAME.equals(shardName)){
-        getSender().tell(new PrimaryFound(defaultShardPath.toString()), getSelf());
-      } else {
-        getSender().tell(new PrimaryNotFound(shardName), getSelf());
-      }
-    } else if(message instanceof UpdateSchemaContext){
-        // FIXME : Notify all local shards of a schemaContext change
-        getContext().system().actorSelection(defaultShardPath).forward(message, getContext());
+    // Stores a mapping between a shard name and the address of the current primary
+    private final Map<String, Address> shardNameToPrimaryAddress =
+        new HashMap<>();
+
+    // Stores a mapping between a member name and the address of the member
+    private final Map<String, Address> memberNameToAddress = new HashMap<>();
+
+    // Stores a mapping between the shard name and all the members on which a replica of that shard are available
+    private final Map<String, List<String>> shardNameToMembers =
+        new HashMap<>();
+
+    private final LoggingAdapter log =
+        Logging.getLogger(getContext().system(), this);
+
+
+    private final Map<String, ActorPath> localShards = new HashMap<>();
+
+
+    private final ClusterWrapper cluster;
+
+    /**
+     * @param type defines the kind of data that goes into shards created by this shard manager. Examples of type would be
+     *             configuration or operational
+     */
+    private ShardManager(String type, ClusterWrapper cluster, Configuration configuration) {
+        this.cluster = cluster;
+        String memberName = cluster.getCurrentMemberName();
+        List<String> memberShardNames =
+            configuration.getMemberShardNames(memberName);
+
+        for(String shardName : memberShardNames){
+            ActorRef actor = getContext()
+                .actorOf(Shard.props(memberName + "-shard-" + shardName + "-" + type),
+                    memberName + "-shard-" + shardName + "-" + type);
+            ActorPath path = actor.path();
+            localShards.put(shardName, path);
+        }
+    }
+
+    public static Props props(final String type,
+        final ClusterWrapper cluster,
+        final Configuration configuration) {
+        return Props.create(new Creator<ShardManager>() {
+
+            @Override
+            public ShardManager create() throws Exception {
+                return new ShardManager(type, cluster, configuration);
+            }
+        });
+    }
+
+    @Override
+    public void handleReceive(Object message) throws Exception {
+        if (message instanceof FindPrimary) {
+            FindPrimary msg = ((FindPrimary) message);
+            String shardName = msg.getShardName();
+
+
+            if (Shard.DEFAULT_NAME.equals(shardName)) {
+                ActorPath defaultShardPath = localShards.get(shardName);
+                if(defaultShardPath == null){
+                    throw new IllegalStateException("local default shard not found");
+                }
+                getSender().tell(new PrimaryFound(defaultShardPath.toString()),
+                    getSelf());
+            } else {
+                getSender().tell(new PrimaryNotFound(shardName), getSelf());
+            }
+        } else if (message instanceof UpdateSchemaContext) {
+            for(ActorPath path : localShards.values()){
+                getContext().system().actorSelection(path)
+                    .forward(message,
+                        getContext());
+            }
+        }
     }
-  }
 
 
 }
index 3dacec7..2f1949e 100644 (file)
@@ -14,6 +14,7 @@ import akka.actor.ActorSelection;
 import akka.actor.ActorSystem;
 import akka.actor.PoisonPill;
 import akka.util.Timeout;
+import org.opendaylight.controller.cluster.datastore.Configuration;
 import org.opendaylight.controller.cluster.datastore.exceptions.PrimaryNotFoundException;
 import org.opendaylight.controller.cluster.datastore.exceptions.TimeoutException;
 import org.opendaylight.controller.cluster.datastore.messages.FindPrimary;
@@ -45,12 +46,14 @@ public class ActorContext {
 
     private final ActorSystem actorSystem;
     private final ActorRef shardManager;
+    private final Configuration configuration;
 
     private SchemaContext schemaContext = null;
 
-    public ActorContext(ActorSystem actorSystem, ActorRef shardManager){
+    public ActorContext(ActorSystem actorSystem, ActorRef shardManager, Configuration configuration){
         this.actorSystem = actorSystem;
         this.shardManager = shardManager;
+        this.configuration = configuration;
     }
 
     public ActorSystem getActorSystem() {
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ConfigurationImplTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ConfigurationImplTest.java
new file mode 100644 (file)
index 0000000..85877ce
--- /dev/null
@@ -0,0 +1,33 @@
+package org.opendaylight.controller.cluster.datastore;
+
+import junit.framework.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.util.List;
+
+import static org.junit.Assert.assertTrue;
+
+public class ConfigurationImplTest {
+
+    private static ConfigurationImpl configuration;
+
+    @BeforeClass
+    public static void staticSetup(){
+        configuration = new ConfigurationImpl("module-shards.conf", "modules.conf");
+    }
+
+    @Test
+    public void testConstructor(){
+        Assert.assertNotNull(configuration);
+    }
+
+    @Test
+    public void testGetMemberShardNames(){
+        List<String> memberShardNames =
+            configuration.getMemberShardNames("member-1");
+
+        assertTrue(memberShardNames.contains("people-1"));
+        assertTrue(memberShardNames.contains("cars-1"));
+    }
+}
index bf42e3d..d5625d2 100644 (file)
@@ -9,6 +9,7 @@ import org.opendaylight.controller.cluster.datastore.messages.DataChanged;
 import org.opendaylight.controller.cluster.datastore.utils.ActorContext;
 import org.opendaylight.controller.cluster.datastore.utils.DoNothingActor;
 import org.opendaylight.controller.cluster.datastore.utils.MessageCollectorActor;
+import org.opendaylight.controller.cluster.datastore.utils.MockConfiguration;
 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
@@ -63,7 +64,7 @@ public class DataChangeListenerProxyTest extends AbstractActorTest {
 
         //Check if it was received by the remote actor
         ActorContext
-            testContext = new ActorContext(getSystem(), getSystem().actorOf(Props.create(DoNothingActor.class)));
+            testContext = new ActorContext(getSystem(), getSystem().actorOf(Props.create(DoNothingActor.class)), new MockConfiguration());
         Object messages = testContext
             .executeLocalOperation(actorRef, "messages",
                 ActorContext.ASK_DURATION);
index 33b5d95..57609a9 100644 (file)
@@ -8,6 +8,7 @@ import org.opendaylight.controller.cluster.datastore.messages.CloseDataChangeLis
 import org.opendaylight.controller.cluster.datastore.utils.ActorContext;
 import org.opendaylight.controller.cluster.datastore.utils.DoNothingActor;
 import org.opendaylight.controller.cluster.datastore.utils.MessageCollectorActor;
+import org.opendaylight.controller.cluster.datastore.utils.MockConfiguration;
 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener;
 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
@@ -58,7 +59,7 @@ public class DataChangeListenerRegistrationProxyTest extends AbstractActorTest{
 
         //Check if it was received by the remote actor
         ActorContext
-            testContext = new ActorContext(getSystem(), getSystem().actorOf(Props.create(DoNothingActor.class)));
+            testContext = new ActorContext(getSystem(), getSystem().actorOf(Props.create(DoNothingActor.class)), new MockConfiguration());
         Object messages = testContext
             .executeLocalOperation(actorRef, "messages",
                 ActorContext.ASK_DURATION);
index f400e74..636c835 100644 (file)
@@ -3,6 +3,8 @@ package org.opendaylight.controller.cluster.datastore;
 import com.google.common.base.Optional;
 import com.google.common.util.concurrent.ListenableFuture;
 import org.junit.Test;
+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 org.opendaylight.controller.sal.core.spi.data.DOMStoreReadWriteTransaction;
 import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
@@ -17,7 +19,7 @@ public class DistributedDataStoreIntegrationTest extends AbstractActorTest {
     @Test
     public void integrationTest() throws Exception {
         DistributedDataStore distributedDataStore =
-            new DistributedDataStore(getSystem(), "config");
+            new DistributedDataStore(getSystem(), "config", new MockClusterWrapper(), new MockConfiguration());
 
         distributedDataStore.onGlobalContextUpdated(TestModel.createTestContext());
 
index fa436c1..a2f19d8 100644 (file)
@@ -10,6 +10,8 @@ import org.junit.Test;
 import org.opendaylight.controller.cluster.datastore.messages.FindPrimary;
 import org.opendaylight.controller.cluster.datastore.messages.PrimaryFound;
 import org.opendaylight.controller.cluster.datastore.messages.PrimaryNotFound;
+import org.opendaylight.controller.cluster.datastore.utils.MockClusterWrapper;
+import org.opendaylight.controller.cluster.datastore.utils.MockConfiguration;
 import scala.concurrent.duration.Duration;
 
 public class ShardManagerTest {
@@ -30,7 +32,7 @@ public class ShardManagerTest {
     public void testOnReceiveFindPrimaryForNonExistentShard() throws Exception {
 
         new JavaTestKit(system) {{
-            final Props props = ShardManager.props("config");
+            final Props props = ShardManager.props("config", new MockClusterWrapper(), new MockConfiguration());
             final TestActorRef<ShardManager> subject = TestActorRef.create(system, props);
 
             new Within(duration("1 seconds")) {
@@ -51,7 +53,7 @@ public class ShardManagerTest {
   public void testOnReceiveFindPrimaryForExistentShard() throws Exception {
 
     new JavaTestKit(system) {{
-      final Props props = ShardManager.props("config");
+      final Props props = ShardManager.props("config", new MockClusterWrapper(), new MockConfiguration());
       final TestActorRef<ShardManager> subject = TestActorRef.create(system, props);
 
       // the run() method needs to finish within 3 seconds
@@ -67,4 +69,4 @@ public class ShardManagerTest {
       };
     }};
   }
-}
\ No newline at end of file
+}
index d1eabb0..2568b0f 100644 (file)
@@ -7,6 +7,7 @@ import org.junit.Test;
 import org.opendaylight.controller.cluster.datastore.messages.CreateTransaction;
 import org.opendaylight.controller.cluster.datastore.messages.CreateTransactionChain;
 import org.opendaylight.controller.cluster.datastore.messages.CreateTransactionChainReply;
+import org.opendaylight.controller.md.cluster.datastore.model.SchemaContextHelper;
 import org.opendaylight.controller.protobuff.messages.transaction.ShardTransactionMessages.CreateTransactionReply;
 import org.opendaylight.controller.cluster.datastore.messages.RegisterChangeListener;
 import org.opendaylight.controller.cluster.datastore.messages.RegisterChangeListenerReply;
@@ -71,7 +72,7 @@ public class ShardTest extends AbstractActorTest {
                 protected void run() {
 
                     subject.tell(
-                        new UpdateSchemaContext(TestModel.createTestContext()),
+                        new UpdateSchemaContext(SchemaContextHelper.full()),
                         getRef());
 
                     subject.tell(new RegisterChangeListener(TestModel.TEST_PATH,
index 1e9fc90..89cf7e7 100644 (file)
@@ -16,6 +16,7 @@ import org.opendaylight.controller.cluster.datastore.utils.ActorContext;
 import org.opendaylight.controller.cluster.datastore.utils.DoNothingActor;
 import org.opendaylight.controller.cluster.datastore.utils.MessageCollectorActor;
 import org.opendaylight.controller.cluster.datastore.utils.MockActorContext;
+import org.opendaylight.controller.cluster.datastore.utils.MockConfiguration;
 import org.opendaylight.controller.md.cluster.datastore.model.TestModel;
 import org.opendaylight.controller.protobuff.messages.transaction.ShardTransactionMessages.CreateTransactionReply;
 import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
@@ -28,6 +29,9 @@ import java.util.concurrent.Executors;
 
 public class TransactionProxyTest extends AbstractActorTest {
 
+    private final ActorContext testContext =
+        new ActorContext(getSystem(), getSystem().actorOf(Props.create(DoNothingActor.class)), new MockConfiguration());
+
     private ExecutorService transactionExecutor =
         Executors.newSingleThreadExecutor();
 
@@ -110,7 +114,6 @@ public class TransactionProxyTest extends AbstractActorTest {
         transactionProxy.write(TestModel.TEST_PATH,
             ImmutableNodes.containerNode(TestModel.NAME_QNAME));
 
-        ActorContext testContext = new ActorContext(getSystem(), getSystem().actorOf(Props.create(DoNothingActor.class)));
         Object messages = testContext
             .executeLocalOperation(actorRef, "messages",
                 ActorContext.ASK_DURATION);
@@ -142,7 +145,6 @@ public class TransactionProxyTest extends AbstractActorTest {
         transactionProxy.merge(TestModel.TEST_PATH,
             ImmutableNodes.containerNode(TestModel.NAME_QNAME));
 
-        ActorContext testContext = new ActorContext(getSystem(), getSystem().actorOf(Props.create(DoNothingActor.class)));
         Object messages = testContext
             .executeLocalOperation(actorRef, "messages",
                 ActorContext.ASK_DURATION);
@@ -173,7 +175,6 @@ public class TransactionProxyTest extends AbstractActorTest {
 
         transactionProxy.delete(TestModel.TEST_PATH);
 
-        ActorContext testContext = new ActorContext(getSystem(), getSystem().actorOf(Props.create(DoNothingActor.class)));
         Object messages = testContext
             .executeLocalOperation(actorRef, "messages",
                 ActorContext.ASK_DURATION);
@@ -243,7 +244,6 @@ public class TransactionProxyTest extends AbstractActorTest {
 
         transactionProxy.close();
 
-        ActorContext testContext = new ActorContext(getSystem(), getSystem().actorOf(Props.create(DoNothingActor.class)));
         Object messages = testContext
             .executeLocalOperation(actorRef, "messages",
                 ActorContext.ASK_DURATION);
index f5bb978..12f80fb 100644 (file)
@@ -21,11 +21,11 @@ public class MockActorContext extends ActorContext {
     private Object executeLocalOperationResponse;
 
     public MockActorContext(ActorSystem actorSystem) {
-        super(actorSystem, null);
+        super(actorSystem, null, new MockConfiguration());
     }
 
     public MockActorContext(ActorSystem actorSystem, ActorRef shardManager) {
-        super(actorSystem, shardManager);
+        super(actorSystem, shardManager, new MockConfiguration());
     }
 
 
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/utils/MockClusterWrapper.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/utils/MockClusterWrapper.java
new file mode 100644 (file)
index 0000000..7749eaa
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * 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.utils;
+
+import akka.actor.ActorRef;
+import org.opendaylight.controller.cluster.datastore.ClusterWrapper;
+
+public class MockClusterWrapper implements ClusterWrapper{
+
+    @Override public void subscribeToMemberEvents(ActorRef actorRef) {
+        throw new UnsupportedOperationException("subscribeToMemberEvents");
+    }
+
+    @Override public String getCurrentMemberName() {
+        return "member-1";
+    }
+}
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/utils/MockConfiguration.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/utils/MockConfiguration.java
new file mode 100644 (file)
index 0000000..a8877c7
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * 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.utils;
+
+import org.opendaylight.controller.cluster.datastore.Configuration;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class MockConfiguration implements Configuration{
+    @Override public List<String> getMemberShardNames(String memberName) {
+        List<String> shardNames = new ArrayList<>();
+        shardNames.add("default");
+        return shardNames;
+    }
+}
index bb881d5..5f361b2 100644 (file)
@@ -19,7 +19,7 @@ public class TestUtils {
 
     public static void assertFirstSentMessage(ActorSystem actorSystem, ActorRef actorRef, Class clazz){
         ActorContext testContext = new ActorContext(actorSystem, actorSystem.actorOf(
-            Props.create(DoNothingActor.class)));
+            Props.create(DoNothingActor.class)), new MockConfiguration());
         Object messages = testContext
             .executeLocalOperation(actorRef, "messages",
                 ActorContext.ASK_DURATION);
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/md/cluster/datastore/model/CarsModel.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/md/cluster/datastore/model/CarsModel.java
new file mode 100644 (file)
index 0000000..e01730d
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.cluster.datastore.model;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableMapNodeBuilder;
+
+public class CarsModel {
+    public static final QName BASE_QNAME = QName.create("urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:store:test:cars", "2014-03-13",
+        "cars");
+
+    public static final QName CARS_QNAME = QName.create(BASE_QNAME, "cars");
+    public static final QName CAR_QNAME = QName.create(CARS_QNAME, "car");
+    public static final QName CAR_NAME_QNAME = QName.create(CAR_QNAME, "name");
+    public static final QName CAR_PRICE_QNAME = QName.create(CAR_QNAME, "price");
+
+
+    public static NormalizedNode create(){
+
+        // Create a list builder
+        CollectionNodeBuilder<MapEntryNode, MapNode> cars =
+            ImmutableMapNodeBuilder.create().withNodeIdentifier(
+                new InstanceIdentifier.NodeIdentifier(
+                    QName.create(CARS_QNAME, "car")));
+
+        // Create an entry for the car altima
+        MapEntryNode altima =
+            ImmutableNodes.mapEntryBuilder(CARS_QNAME, CAR_NAME_QNAME, "altima")
+                .withChild(ImmutableNodes.leafNode(CAR_NAME_QNAME, "altima"))
+                .withChild(ImmutableNodes.leafNode(CAR_PRICE_QNAME, 1000))
+                .build();
+
+        // Create an entry for the car accord
+        MapEntryNode honda =
+            ImmutableNodes.mapEntryBuilder(CARS_QNAME, CAR_NAME_QNAME, "accord")
+                .withChild(ImmutableNodes.leafNode(CAR_NAME_QNAME, "accord"))
+                .withChild(ImmutableNodes.leafNode(CAR_PRICE_QNAME, 2000))
+                .build();
+
+        cars.withChild(altima);
+        cars.withChild(honda);
+
+        return ImmutableContainerNodeBuilder.create()
+            .withNodeIdentifier(new InstanceIdentifier.NodeIdentifier(BASE_QNAME))
+            .withChild(cars.build())
+            .build();
+
+    }
+
+    public static NormalizedNode emptyContainer(){
+        return ImmutableContainerNodeBuilder.create()
+            .withNodeIdentifier(new InstanceIdentifier.NodeIdentifier(BASE_QNAME))
+            .build();
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/md/cluster/datastore/model/PeopleModel.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/md/cluster/datastore/model/PeopleModel.java
new file mode 100644 (file)
index 0000000..9ccb054
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.cluster.datastore.model;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableMapNodeBuilder;
+
+public class PeopleModel {
+    public static final QName BASE_QNAME = QName.create("urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:store:test:people", "2014-03-13",
+        "people");
+
+    public static final QName PEOPLE_QNAME = QName.create(BASE_QNAME, "people");
+    public static final QName PERSON_QNAME = QName.create(PEOPLE_QNAME, "person");
+    public static final QName PERSON_NAME_QNAME = QName.create(PERSON_QNAME, "name");
+    public static final QName PERSON_AGE_QNAME = QName.create(PERSON_QNAME, "age");
+
+
+    public static NormalizedNode create(){
+
+        // Create a list builder
+        CollectionNodeBuilder<MapEntryNode, MapNode> cars =
+            ImmutableMapNodeBuilder.create().withNodeIdentifier(
+                new InstanceIdentifier.NodeIdentifier(
+                    QName.create(PEOPLE_QNAME, "person")));
+
+        // Create an entry for the person jack
+        MapEntryNode jack =
+            ImmutableNodes.mapEntryBuilder(PEOPLE_QNAME, PERSON_NAME_QNAME, "jack")
+                .withChild(ImmutableNodes.leafNode(PERSON_NAME_QNAME, "jack"))
+                .withChild(ImmutableNodes.leafNode(PERSON_AGE_QNAME, 100))
+                .build();
+
+        // Create an entry for the person jill
+        MapEntryNode jill =
+            ImmutableNodes.mapEntryBuilder(PEOPLE_QNAME, PERSON_NAME_QNAME, "jill")
+                .withChild(ImmutableNodes.leafNode(PERSON_NAME_QNAME, "jill"))
+                .withChild(ImmutableNodes.leafNode(PERSON_AGE_QNAME, 200))
+                .build();
+
+        cars.withChild(jack);
+        cars.withChild(jill);
+
+        return ImmutableContainerNodeBuilder.create()
+            .withNodeIdentifier(new InstanceIdentifier.NodeIdentifier(BASE_QNAME))
+            .withChild(cars.build())
+            .build();
+
+    }
+
+    public static NormalizedNode emptyContainer(){
+        return ImmutableContainerNodeBuilder.create()
+            .withNodeIdentifier(new InstanceIdentifier.NodeIdentifier(BASE_QNAME))
+            .build();
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/md/cluster/datastore/model/SampleModelsTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/md/cluster/datastore/model/SampleModelsTest.java
new file mode 100644 (file)
index 0000000..6824591
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.cluster.datastore.model;
+
+import junit.framework.Assert;
+import org.junit.Test;
+import org.opendaylight.controller.cluster.datastore.node.NormalizedNodeToNodeCodec;
+import org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+public class SampleModelsTest {
+    @Test
+    public void testPeopleModel(){
+        NormalizedNode<?, ?> expected = PeopleModel.emptyContainer();
+
+
+        NormalizedNodeMessages.Container node =
+            new NormalizedNodeToNodeCodec(SchemaContextHelper.full())
+                .encode(InstanceIdentifier.of(PeopleModel.BASE_QNAME),
+                    expected);
+
+        NormalizedNodeMessages.Node normalizedNode =
+            node.getNormalizedNode();
+
+        NormalizedNode<?,?> actual = new NormalizedNodeToNodeCodec(SchemaContextHelper.full()).decode(InstanceIdentifier.of(PeopleModel.BASE_QNAME),
+            normalizedNode);
+
+
+        Assert.assertEquals(expected, actual);
+
+    }
+
+
+    @Test
+    public void testCarsModel(){
+        NormalizedNode<?, ?> expected = CarsModel.emptyContainer();
+
+
+        NormalizedNodeMessages.Container node =
+            new NormalizedNodeToNodeCodec(SchemaContextHelper.full())
+                .encode(InstanceIdentifier.of(CarsModel.BASE_QNAME),
+                    expected);
+
+        NormalizedNodeMessages.Node normalizedNode =
+            node.getNormalizedNode();
+
+        NormalizedNode<?,?> actual = new NormalizedNodeToNodeCodec(SchemaContextHelper.full()).decode(InstanceIdentifier.of(CarsModel.BASE_QNAME),
+            normalizedNode);
+
+
+        Assert.assertEquals(expected, actual);
+
+    }
+}
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/md/cluster/datastore/model/SchemaContextHelper.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/md/cluster/datastore/model/SchemaContextHelper.java
new file mode 100644 (file)
index 0000000..3395738
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.cluster.datastore.model;
+
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
+
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+public class SchemaContextHelper {
+
+    public static InputStream getInputStream(final String yangFileName) {
+        return TestModel.class.getResourceAsStream(yangFileName);
+    }
+
+    public static SchemaContext full(){
+        YangParserImpl parser = new YangParserImpl();
+        List<InputStream> streams = new ArrayList<>();
+        streams.add(getInputStream("/odl-datastore-test.yang"));
+        streams.add(getInputStream("/people.yang"));
+        streams.add(getInputStream("/cars.yang"));
+
+        Set<Module> modules = parser.parseYangModelsFromStreams(streams);
+        return parser.resolveSchemaContext(modules);
+
+    }
+}
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/resources/cars.yang b/opendaylight/md-sal/sal-distributed-datastore/src/test/resources/cars.yang
new file mode 100644 (file)
index 0000000..d83135e
--- /dev/null
@@ -0,0 +1,22 @@
+module cars {
+    yang-version 1;
+    namespace "urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:store:test:cars";
+    prefix "cars";
+
+    revision "2014-03-13" {
+        description "Initial revision.";
+    }
+
+    container cars {
+        list car {
+            key name;
+            leaf name {
+                type string;
+            }
+
+            leaf price {
+                type uint64;
+            }
+        }
+    }
+}
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/resources/module-shards.conf b/opendaylight/md-sal/sal-distributed-datastore/src/test/resources/module-shards.conf
new file mode 100644 (file)
index 0000000..9f35f2b
--- /dev/null
@@ -0,0 +1,42 @@
+module-shards = [
+    {
+        name = "default"
+        shards = [
+            {
+                name="default",
+                replicas = [
+                    "member-1",
+                    "member-2",
+                    "member-3"
+                ]
+            }
+        ]
+    },
+    {
+        name = "people"
+        shards = [
+            {
+                name="people-1"
+                replicas = [
+                    "member-1",
+                    "member-2",
+                    "member-3"
+                ]
+            }
+        ]
+    },
+    {
+        name = "cars"
+        shards = [
+            {
+                name="cars-1"
+                replicas = [
+                    "member-4",
+                    "member-1",
+                    "member-5"
+                ]
+            }
+        ]
+    }
+
+]
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/resources/modules.conf b/opendaylight/md-sal/sal-distributed-datastore/src/test/resources/modules.conf
new file mode 100644 (file)
index 0000000..5d2e556
--- /dev/null
@@ -0,0 +1,13 @@
+modules = [
+    {
+        name = "people"
+        namespace = "urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:store:test:people"
+        sharding-strategy = "module"
+    },
+    {
+        name = "cars"
+        namespace = "urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:store:test:cars"
+        sharding-strategy = "module"
+    }
+
+]
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/resources/people.yang b/opendaylight/md-sal/sal-distributed-datastore/src/test/resources/people.yang
new file mode 100644 (file)
index 0000000..7ede0e6
--- /dev/null
@@ -0,0 +1,22 @@
+module people {
+    yang-version 1;
+    namespace "urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:store:test:people";
+    prefix "people";
+
+    revision "2014-03-13" {
+        description "Initial revision.";
+    }
+
+    container people {
+        list person {
+            key name;
+            leaf name {
+                type string;
+            }
+
+            leaf age {
+                type uint32;
+            }
+        }
+    }
+}

©2013 OpenDaylight, A Linux Foundation Collaborative Project. All Rights Reserved.
OpenDaylight is a registered trademark of The OpenDaylight Project, Inc.
Linux Foundation and OpenDaylight are registered trademarks of the Linux Foundation.
Linux is a registered trademark of Linus Torvalds.