tablet-leader command to check current Raft leader

Change-Id: Id360db21988a50c3e2895c5194d59b0ba4cb49e4
diff --git a/cli/src/main/java/org/onlab/onos/cli/TabletLeaderCommand.java b/cli/src/main/java/org/onlab/onos/cli/TabletLeaderCommand.java
new file mode 100644
index 0000000..b0aa835
--- /dev/null
+++ b/cli/src/main/java/org/onlab/onos/cli/TabletLeaderCommand.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onlab.onos.cli;
+
+import org.apache.karaf.shell.commands.Command;
+import org.onlab.onos.cluster.ControllerNode;
+import org.onlab.onos.store.service.DatabaseAdminService;
+
+import java.util.Optional;
+
+/**
+ * Lists mastership roles of nodes for each device.
+ */
+@Command(scope = "onos", name = "tablet-leader",
+         description = "Prints the current leader of a tablet.")
+public class TabletLeaderCommand extends AbstractShellCommand {
+
+    @Override
+    protected void execute() {
+        final DatabaseAdminService dbAdminService = get(DatabaseAdminService.class);
+
+        Optional<ControllerNode> leader = dbAdminService.leader();
+        if (leader.isPresent()) {
+            print("Leader: %s", leader.get());
+        } else {
+            print("No Leader");
+        }
+    }
+}
diff --git a/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml
index 71ce11a..b725bcb 100644
--- a/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml
+++ b/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml
@@ -27,6 +27,11 @@
         <command>
             <action class="org.onlab.onos.cli.TabletMemberCommand"/>
         </command>
+
+        <command>
+            <action class="org.onlab.onos.cli.TabletLeaderCommand"/>
+        </command>
+
         <command>
             <action class="org.onlab.onos.cli.TabletAddCommand"/>
             <completers>
diff --git a/core/api/src/main/java/org/onlab/onos/store/service/DatabaseAdminService.java b/core/api/src/main/java/org/onlab/onos/store/service/DatabaseAdminService.java
index 0324dfb..ef7ea67 100644
--- a/core/api/src/main/java/org/onlab/onos/store/service/DatabaseAdminService.java
+++ b/core/api/src/main/java/org/onlab/onos/store/service/DatabaseAdminService.java
@@ -1,6 +1,7 @@
 package org.onlab.onos.store.service;
 
 import java.util.Collection;
+import java.util.Optional;
 import java.util.Set;
 
 import org.onlab.onos.cluster.ControllerNode;
@@ -67,4 +68,11 @@
      * @return Copied collection of members forming default Tablet.
      */
     public Collection<ControllerNode> listMembers();
+
+    /**
+     * Returns the current Leader of the default Tablet.
+     *
+     * @return leader node
+     */
+    public Optional<ControllerNode> leader();
 }
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/service/impl/DatabaseClient.java b/core/store/dist/src/main/java/org/onlab/onos/store/service/impl/DatabaseClient.java
index 17f356b..2e06696 100644
--- a/core/store/dist/src/main/java/org/onlab/onos/store/service/impl/DatabaseClient.java
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/service/impl/DatabaseClient.java
@@ -151,4 +151,8 @@
     public Map<String, VersionedValue> getAll(String tableName) {
         return submit("getAll", tableName);
     }
+
+    Member getCurrentLeader() {
+        return currentLeader;
+    }
 }
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/service/impl/DatabaseManager.java b/core/store/dist/src/main/java/org/onlab/onos/store/service/impl/DatabaseManager.java
index 31f4bf0..192e084 100644
--- a/core/store/dist/src/main/java/org/onlab/onos/store/service/impl/DatabaseManager.java
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/service/impl/DatabaseManager.java
@@ -10,6 +10,7 @@
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.Map;
+import java.util.Optional;
 import java.util.Set;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.ExecutionException;
@@ -38,7 +39,6 @@
 import org.onlab.onos.cluster.ClusterService;
 import org.onlab.onos.cluster.ControllerNode;
 import org.onlab.onos.cluster.DefaultControllerNode;
-import org.onlab.onos.cluster.NodeId;
 import org.onlab.onos.store.cluster.messaging.ClusterCommunicationService;
 import org.onlab.onos.store.cluster.messaging.ClusterMessage;
 import org.onlab.onos.store.cluster.messaging.MessageSubject;
@@ -433,6 +433,18 @@
         }
     }
 
+    @Override
+    public Optional<ControllerNode> leader() {
+        if (copycat != null) {
+            if (copycat.isLeader()) {
+                return Optional.of(clusterService.getLocalNode());
+            }
+            Member leader = copycat.cluster().remoteMember(copycat.leader());
+            return Optional.ofNullable(getNodeIdFromMember(leader));
+        }
+        return Optional.ofNullable(getNodeIdFromMember(client.getCurrentLeader()));
+    }
+
     private final class LeaderAdvertiser implements Runnable {
 
         @Override
@@ -549,28 +561,28 @@
         }
         Set<ControllerNode> members = new HashSet<>();
         for (Member member : copycat.cluster().members()) {
-            if (member instanceof TcpMember) {
-                final TcpMember tcpMember = (TcpMember) member;
-                // TODO assuming tcpMember#host to be IP address,
-                // but if not lookup DNS, etc. first
-                IpAddress ip = IpAddress.valueOf(tcpMember.host());
-                int tcpPort = tcpMember.port();
-                NodeId id = getNodeIdFromIp(ip, tcpPort);
-                if (id == null) {
-                    log.info("No NodeId found for {}:{}", ip, tcpPort);
-                    continue;
-                }
-                members.add(new DefaultControllerNode(id, ip, tcpPort));
+            ControllerNode node = getNodeIdFromMember(member);
+            if (node == null) {
+                log.info("No Node found for {}", member);
+                continue;
             }
+            members.add(node);
         }
         return members;
     }
 
-    private NodeId getNodeIdFromIp(IpAddress ip, int tcpPort) {
-        for (ControllerNode node : clusterService.getNodes()) {
-            if (node.ip().equals(ip) &&
-                node.tcpPort() == tcpPort) {
-                return node.id();
+    private ControllerNode getNodeIdFromMember(Member member) {
+        if (member instanceof TcpMember) {
+            final TcpMember tcpMember = (TcpMember) member;
+            // TODO assuming tcpMember#host to be IP address,
+            // but if not lookup DNS, etc. first
+            IpAddress ip = IpAddress.valueOf(tcpMember.host());
+            int tcpPort = tcpMember.port();
+            for (ControllerNode node : clusterService.getNodes()) {
+                if (node.ip().equals(ip) &&
+                    node.tcpPort() == tcpPort) {
+                    return node;
+                }
             }
         }
         return null;