From b34452ce75563e360ae1d02a9f2aa6223d6208c3 Mon Sep 17 00:00:00 2001 From: kalaiselvik Date: Thu, 22 Oct 2015 15:49:47 +0530 Subject: [PATCH] BUG 2187 - JMX API for create/delete shard replica Change-Id: I48a4dcb7983f5f231e9ddc04e851950abf7c2d8a Signed-off-by: kalaiselvik --- .../cluster/datastore/ShardManager.java | 48 ++++++++++++++++ .../datastore/config/Configuration.java | 7 ++- .../datastore/config/ConfigurationImpl.java | 6 ++ .../mbeans/shardmanager/ShardManagerInfo.java | 40 +++++++++++++ .../shardmanager/ShardManagerInfoMBean.java | 15 +++++ .../datastore/messages/AddShardReplica.java | 42 ++++++++++++++ .../messages/RemoveShardReplica.java | 41 ++++++++++++++ .../cluster/datastore/ShardManagerTest.java | 56 +++++++++++++++++++ .../datastore/utils/MockConfiguration.java | 13 ++++- 9 files changed, 266 insertions(+), 2 deletions(-) create mode 100644 opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/AddShardReplica.java create mode 100644 opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/RemoveShardReplica.java 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 b48215d361..eb39a34dc0 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 @@ -62,6 +62,8 @@ import org.opendaylight.controller.cluster.datastore.messages.RemotePrimaryShard import org.opendaylight.controller.cluster.datastore.messages.ShardLeaderStateChanged; import org.opendaylight.controller.cluster.datastore.messages.SwitchShardBehavior; import org.opendaylight.controller.cluster.datastore.messages.UpdateSchemaContext; +import org.opendaylight.controller.cluster.datastore.messages.AddShardReplica; +import org.opendaylight.controller.cluster.datastore.messages.RemoveShardReplica; import org.opendaylight.controller.cluster.datastore.utils.Dispatchers; import org.opendaylight.controller.cluster.datastore.utils.PrimaryShardInfoFutureCache; import org.opendaylight.controller.cluster.notifications.RegisterRoleChangeListener; @@ -198,6 +200,10 @@ public class ShardManager extends AbstractUntypedPersistentActorWithMetering { onSwitchShardBehavior((SwitchShardBehavior) message); } else if(message instanceof CreateShard) { onCreateShard((CreateShard)message); + } else if(message instanceof AddShardReplica){ + onAddShardReplica((AddShardReplica)message); + } else if(message instanceof RemoveShardReplica){ + onRemoveShardReplica((RemoveShardReplica)message); } else { unknownMessage(message); } @@ -740,6 +746,48 @@ public class ShardManager extends AbstractUntypedPersistentActorWithMetering { return mBean; } + private void onAddShardReplica (AddShardReplica shardReplicaMsg) { + String shardName = shardReplicaMsg.getShardName(); + + // verify the local shard replica is already available in the controller node + if (localShards.containsKey(shardName)) { + LOG.debug ("Local shard {} already available in the controller node", shardName); + getSender().tell(new akka.actor.Status.Failure( + new IllegalArgumentException(String.format("Local shard %s already exists", + shardName))), getSelf()); + return; + } + // verify the shard with the specified name is present in the cluster configuration + if (!(this.configuration.isShardConfigured(shardName))) { + LOG.debug ("No module configuration exists for shard {}", shardName); + getSender().tell(new akka.actor.Status.Failure(new IllegalArgumentException( + String.format("No module configuration exists for shard %s", + shardName))), getSelf()); + return; + } + + // Create the localShard + getSender().tell(new akka.actor.Status.Success(true), getSelf()); + return; + } + + private void onRemoveShardReplica (RemoveShardReplica shardReplicaMsg) { + String shardName = shardReplicaMsg.getShardName(); + boolean deleteStatus = false; + + // verify the local shard replica is available in the controller node + if (!localShards.containsKey(shardName)) { + LOG.debug ("Local shard replica {} is not available in the controller node", shardName); + getSender().tell(new akka.actor.Status.Failure( + new IllegalArgumentException(String.format("Local shard %s not available", + shardName))), getSelf()); + return; + } + // call RemoveShard for the shardName + getSender().tell(new akka.actor.Status.Success(true), getSelf()); + return; + } + @VisibleForTesting protected static class ShardInformation { private final ShardIdentifier shardId; diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/config/Configuration.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/config/Configuration.java index 21801c0102..dea77f5e34 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/config/Configuration.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/config/Configuration.java @@ -22,7 +22,7 @@ public interface Configuration { @Nonnull Collection getMemberShardNames(@Nonnull String memberName); /** - * Returns the namespace for the given module name or null if not found. + * Returns the module name for the given namespace name or null if not found. */ @Nullable String getModuleNameFromNameSpace(@Nonnull String nameSpace); @@ -55,4 +55,9 @@ public interface Configuration { * Returns a unique set of all member names configured for all shards. */ Collection getUniqueMemberNamesForAllShards(); + + /* + * Verifies if the given module shard in available in the cluster + */ + boolean isShardConfigured(String shardName); } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/config/ConfigurationImpl.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/config/ConfigurationImpl.java index 2f1e88910e..d553c66dbe 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/config/ConfigurationImpl.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/config/ConfigurationImpl.java @@ -151,4 +151,10 @@ public class ConfigurationImpl implements Configuration { private ShardStrategy createShardStrategy(String moduleName, String shardStrategyName) { return ShardStrategyFactory.newShardStrategyInstance(moduleName, shardStrategyName, this); } + + @Override + public boolean isShardConfigured(String shardName) { + Preconditions.checkNotNull(shardName, "shardName should not be null"); + return allShardNames.contains(shardName); + } } 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 79beae72a3..94bf67557b 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 @@ -18,6 +18,12 @@ import org.opendaylight.controller.cluster.raft.RaftState; import org.opendaylight.controller.md.sal.common.util.jmx.AbstractMXBean; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.opendaylight.controller.cluster.datastore.messages.AddShardReplica; +import org.opendaylight.controller.cluster.datastore.messages.RemoveShardReplica; +import akka.pattern.Patterns; +import akka.util.Timeout; +import java.util.concurrent.TimeUnit; +import scala.concurrent.Await; public class ShardManagerInfo extends AbstractMXBean implements ShardManagerInfoMBean { @@ -96,4 +102,38 @@ public class ShardManagerInfo extends AbstractMXBean implements ShardManagerInfo public void setShardManager(ShardManager shardManager){ this.shardManager = shardManager; } + + @Override + public void setAddShardReplica (String shardName) { + LOG.info ("addShardReplica initiated for shard {}", shardName); + + // TODO addTimeout to be made configurable + Timeout addTimeOut = new Timeout(1, TimeUnit.MINUTES); + try { + Await.result(Patterns.ask(shardManager.getSelf(), + new AddShardReplica(shardName), addTimeOut), + addTimeOut.duration()); + } catch (Exception ex) { + LOG.debug ("Obtained an exception during addShardReplica", ex); + throw (new RuntimeException(ex.getMessage())); + } + return; + } + + @Override + public void setRemoveShardReplica (String shardName) { + LOG.info ("removeShardReplica initiated for shard {}", shardName); + + // TODO remTimeOut to be made configurable + Timeout remTimeOut = new Timeout(30, TimeUnit.SECONDS); + try { + Await.result(Patterns.ask(shardManager.getSelf(), + new RemoveShardReplica(shardName), remTimeOut), + remTimeOut.duration()); + } catch (Exception ex) { + LOG.debug ("Obtained an exception during removeShardReplica", ex); + throw (new RuntimeException(ex.getMessage())); + } + return; + } } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/jmx/mbeans/shardmanager/ShardManagerInfoMBean.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/jmx/mbeans/shardmanager/ShardManagerInfoMBean.java index 92a43d28f0..8660314b79 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/jmx/mbeans/shardmanager/ShardManagerInfoMBean.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/jmx/mbeans/shardmanager/ShardManagerInfoMBean.java @@ -52,4 +52,19 @@ public interface ShardManagerInfoMBean { * the term provided when switching to a new Leader should always be higher than the previous term. */ void switchShardState(String shardName, String newBehavior, long term); + + /** + * Add a new Shard replica for an existing Shard in this controller node + * + * @param shardName the shard that is to be created and replicated in this controller instance + */ + void setAddShardReplica (String shardName); + + /** + * Remove a Shard replica available in this controller node + * + * @param shardName the shard that is to be removed from this controller instance + */ + void setRemoveShardReplica (String shardName); + } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/AddShardReplica.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/AddShardReplica.java new file mode 100644 index 0000000000..fd20d20c0b --- /dev/null +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/AddShardReplica.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2015 Dell 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.messages; + +import javax.annotation.Nonnull; +import com.google.common.base.Preconditions; + +/** + * A message sent to the ShardManager to dynamically add a new local shard + * that is a replica for an existing shard that is already available in the + * cluster. + */ + +public class AddShardReplica { + + private final String shardName; + + /** + * Constructor. + * + * @param shardName name of the shard that is to be locally replicated. + */ + + public AddShardReplica (@Nonnull String shardName){ + this.shardName = Preconditions.checkNotNull(shardName, "ShardName should not be null"); + } + + public String getShardName(){ + return this.shardName; + } + + @Override + public String toString(){ + return "AddShardReplica[ShardName=" + shardName + "]"; + } +} diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/RemoveShardReplica.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/RemoveShardReplica.java new file mode 100644 index 0000000000..86c70234f2 --- /dev/null +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/RemoveShardReplica.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2015 Dell 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.messages; + +import javax.annotation.Nonnull; +import com.google.common.base.Preconditions; + +/** + * A message sent to the ShardManager to dynamically remove a local shard + * replica available in this node. + */ + +public class RemoveShardReplica { + + private final String shardName; + + /** + * Constructor. + * + * @param shardName name of the local shard that is to be dynamically removed. + */ + + public RemoveShardReplica (@Nonnull String shardName){ + this.shardName = Preconditions.checkNotNull(shardName, "ShardName should not be null"); + } + + public String getShardName(){ + return this.shardName; + } + + @Override + public String toString(){ + return "RemoveShardReplica[ShardName=" + shardName + "]"; + } +} 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 e49221b06a..77250896d9 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 @@ -21,6 +21,7 @@ import akka.actor.ActorRef; import akka.actor.ActorSystem; import akka.actor.AddressFromURIString; import akka.actor.Props; +import akka.actor.Status; import akka.cluster.Cluster; import akka.cluster.ClusterEvent; import akka.dispatch.Dispatchers; @@ -57,6 +58,7 @@ import org.opendaylight.controller.cluster.datastore.exceptions.PrimaryNotFoundE import org.opendaylight.controller.cluster.datastore.identifiers.ShardIdentifier; import org.opendaylight.controller.cluster.datastore.identifiers.ShardManagerIdentifier; import org.opendaylight.controller.cluster.datastore.messages.ActorInitialized; +import org.opendaylight.controller.cluster.datastore.messages.AddShardReplica; import org.opendaylight.controller.cluster.datastore.messages.CreateShard; import org.opendaylight.controller.cluster.datastore.messages.CreateShardReply; import org.opendaylight.controller.cluster.datastore.messages.FindLocalShard; @@ -68,6 +70,7 @@ import org.opendaylight.controller.cluster.datastore.messages.PeerDown; import org.opendaylight.controller.cluster.datastore.messages.PeerUp; import org.opendaylight.controller.cluster.datastore.messages.PrimaryShardInfo; import org.opendaylight.controller.cluster.datastore.messages.RemotePrimaryShardFound; +import org.opendaylight.controller.cluster.datastore.messages.RemoveShardReplica; import org.opendaylight.controller.cluster.datastore.messages.ShardLeaderStateChanged; import org.opendaylight.controller.cluster.datastore.messages.SwitchShardBehavior; import org.opendaylight.controller.cluster.datastore.messages.UpdateSchemaContext; @@ -1030,6 +1033,59 @@ public class ShardManagerTest extends AbstractActorTest { }}; } + @Test + public void testAddShardReplicaForNonExistentShard() throws Exception { + new JavaTestKit(getSystem()) {{ + ActorRef shardManager = getSystem().actorOf(newShardMgrProps( + new ConfigurationImpl(new EmptyModuleShardConfigProvider()))); + + shardManager.tell(new AddShardReplica("model-inventory"), getRef()); + Status.Failure resp = expectMsgClass(duration("2 seconds"), Status.Failure.class); + + assertEquals("Failure obtained", true, + (resp.cause() instanceof IllegalArgumentException)); + }}; + } + + @Test + public void testAddShardReplicaForAlreadyCreatedShard() throws Exception { + new JavaTestKit(getSystem()) {{ + ActorRef shardManager = getSystem().actorOf(newShardMgrProps()); + shardManager.tell(new AddShardReplica("default"), getRef()); + Status.Failure resp = expectMsgClass(duration("2 seconds"), Status.Failure.class); + assertEquals("Failure obtained", true, + (resp.cause() instanceof IllegalArgumentException)); + }}; + } + + @Test + public void testAddShardReplica() throws Exception { + new JavaTestKit(getSystem()) {{ + MockConfiguration mockConfig = new MockConfiguration(ImmutableMap.>builder(). + put("default", Arrays.asList("member-1", "member-2")). + put("astronauts", Arrays.asList("member-2")).build()); + + ActorRef shardManager = getSystem().actorOf(newShardMgrProps(mockConfig)); + + shardManager.tell(new AddShardReplica("astronauts"), getRef()); + expectMsgClass(duration("2 seconds"), Status.Success.class); + }}; + } + + @Test + public void testRemoveShardReplicaForNonExistentShard() throws Exception { + new JavaTestKit(getSystem()) {{ + ActorRef shardManager = getSystem().actorOf(newShardMgrProps( + new ConfigurationImpl(new EmptyModuleShardConfigProvider()))); + + shardManager.tell(new RemoveShardReplica("model-inventory"), getRef()); + Status.Failure resp = expectMsgClass(duration("2 seconds"), Status.Failure.class); + assertEquals("Failure obtained", true, + (resp.cause() instanceof IllegalArgumentException)); + }}; + + } + private static class TestShardPropsCreator implements ShardPropsCreator { ShardIdentifier shardId; Map peerAddresses; 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 index 554e67b1f1..caae615ae6 100644 --- 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 @@ -35,7 +35,13 @@ public class MockConfiguration implements Configuration{ @Override public Collection getMemberShardNames(final String memberName) { - return new ArrayList<>(shardMembers.keySet()); + ArrayList shardNames = new ArrayList(); + for(Map.Entry> shard : shardMembers.entrySet()) { + if (shard.getValue().contains(memberName)) { + shardNames.add(shard.getKey()); + } + } + return shardNames; } @Override @@ -82,4 +88,9 @@ public class MockConfiguration implements Configuration{ @Override public void addModuleShardConfiguration(ModuleShardConfiguration config) { } + + @Override + public boolean isShardConfigured(String shardName) { + return (shardMembers.containsKey(shardName)); + } } -- 2.36.6