Add mastership to Topology

Change-Id: Ibc2914682dac2fe124b69e40922e2d313190f168
diff --git a/src/main/java/net/onrc/onos/core/topology/BaseInternalTopology.java b/src/main/java/net/onrc/onos/core/topology/BaseInternalTopology.java
index 6f9d9bf..cf44c32 100644
--- a/src/main/java/net/onrc/onos/core/topology/BaseInternalTopology.java
+++ b/src/main/java/net/onrc/onos/core/topology/BaseInternalTopology.java
@@ -11,7 +11,7 @@
 /**
  * Interface to reference internal self-contained elements.
  */
-public interface BaseInternalTopology {
+public interface BaseInternalTopology extends BaseMastership {
 
     /**
      * Gets a SwitchEvent.
diff --git a/src/main/java/net/onrc/onos/core/topology/BaseMastership.java b/src/main/java/net/onrc/onos/core/topology/BaseMastership.java
new file mode 100644
index 0000000..fa05e1c
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/topology/BaseMastership.java
@@ -0,0 +1,22 @@
+package net.onrc.onos.core.topology;
+
+import net.onrc.onos.core.util.Dpid;
+import net.onrc.onos.core.util.OnosInstanceId;
+
+// TODO probably "Base" prefix is not required
+/**
+ * Interface to access switch mastership information in the network view.
+ */
+public interface BaseMastership {
+
+    /**
+     * Gets the master instance ID for a switch.
+     *
+     * @param dpid switch dpid
+     * @return master instance ID or null if there is no master
+     */
+    public OnosInstanceId getSwitchMaster(Dpid dpid);
+
+// We may need something like below in the future
+//    public List<ImmutablePair<OnosInstanceId, Role>> getSwitchMasterCandidates(Dpid dpid);
+}
diff --git a/src/main/java/net/onrc/onos/core/topology/BaseTopology.java b/src/main/java/net/onrc/onos/core/topology/BaseTopology.java
index 6b2bef8..69db9fd 100644
--- a/src/main/java/net/onrc/onos/core/topology/BaseTopology.java
+++ b/src/main/java/net/onrc/onos/core/topology/BaseTopology.java
@@ -11,7 +11,7 @@
 /**
  * BaseTopology interface common to both {@link ImmutableTopology} and {@link MutableTopology}.
  */
-public interface BaseTopology {
+public interface BaseTopology extends BaseMastership {
 
     /**
      * Gets the switch for a given switch DPID.
diff --git a/src/main/java/net/onrc/onos/core/topology/BaseTopologyAdaptor.java b/src/main/java/net/onrc/onos/core/topology/BaseTopologyAdaptor.java
index 3c6317e..2a6ac33 100644
--- a/src/main/java/net/onrc/onos/core/topology/BaseTopologyAdaptor.java
+++ b/src/main/java/net/onrc/onos/core/topology/BaseTopologyAdaptor.java
@@ -6,8 +6,10 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
+
 import net.floodlightcontroller.util.MACAddress;
 import net.onrc.onos.core.util.Dpid;
+import net.onrc.onos.core.util.OnosInstanceId;
 import net.onrc.onos.core.util.PortNumber;
 import net.onrc.onos.core.util.SwitchPort;
 
@@ -268,4 +270,10 @@
     public Collection<Host> getHosts(SwitchPort port) {
         return toHostImpls(internal, internal.getHostEvents(port));
     }
+
+
+    @Override
+    public OnosInstanceId getSwitchMaster(Dpid dpid) {
+        return internal.getSwitchMaster(dpid);
+    }
 }
diff --git a/src/main/java/net/onrc/onos/core/topology/TopologyImpl.java b/src/main/java/net/onrc/onos/core/topology/TopologyImpl.java
index d407064..1dc14c6 100644
--- a/src/main/java/net/onrc/onos/core/topology/TopologyImpl.java
+++ b/src/main/java/net/onrc/onos/core/topology/TopologyImpl.java
@@ -1,12 +1,17 @@
 package net.onrc.onos.core.topology;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
+import java.util.SortedSet;
+import java.util.TreeSet;
 import java.util.Map.Entry;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
@@ -16,9 +21,11 @@
 
 import javax.annotation.concurrent.GuardedBy;
 
+import net.floodlightcontroller.core.IFloodlightProviderService.Role;
 import net.floodlightcontroller.util.MACAddress;
 import net.onrc.onos.core.util.Dpid;
 import net.onrc.onos.core.util.LinkTuple;
+import net.onrc.onos.core.util.OnosInstanceId;
 import net.onrc.onos.core.util.PortNumber;
 import net.onrc.onos.core.util.SwitchPort;
 
@@ -30,6 +37,7 @@
 import com.google.common.collect.Multimaps;
 
 
+// TODO add TopologyManager, etc. to populate Mastership information.
 /**
  * Class to represent an instance of Topology Snapshot.
  */
@@ -39,6 +47,10 @@
 
     // TODO Revisit Map types after implementing CoW/lock-free
 
+    // Mastership info
+    // Dpid -> [ (InstanceID, Role) ]
+    private final Map<Dpid, SortedSet<MastershipEvent>> mastership;
+
     // DPID -> Switch
     private final ConcurrentMap<Dpid, SwitchEvent> switches;
     private final ConcurrentMap<Dpid, ConcurrentMap<PortNumber, PortEvent>> ports;
@@ -60,6 +72,7 @@
      * Create an empty Topology.
      */
     public TopologyImpl() {
+        mastership = new HashMap<>();
         // TODO: Does these object need to be stored in Concurrent Collection?
         switches = new ConcurrentHashMap<>();
         ports = new ConcurrentHashMap<>();
@@ -78,6 +91,13 @@
     public TopologyImpl(TopologyImpl original) {
         original.acquireReadLock();
         try {
+            // shallow copy Set in Map
+            this.mastership = new HashMap<>(original.mastership.size());
+            for (Entry<Dpid, SortedSet<MastershipEvent>> e
+                        : original.mastership.entrySet()) {
+                this.mastership.put(e.getKey(), new TreeSet<>(e.getValue()));
+            }
+
             this.switches = new ConcurrentHashMap<>(original.switches);
 
             // shallow copy Map in Map
@@ -481,6 +501,20 @@
         return Collections.unmodifiableCollection(mac2Host.values());
     }
 
+    @Override
+    public OnosInstanceId getSwitchMaster(Dpid dpid) {
+        final SortedSet<MastershipEvent> candidates = mastership.get(dpid);
+        if (candidates == null) {
+            return null;
+        }
+        for (MastershipEvent candidate : candidates) {
+            if (candidate.getRole() == Role.MASTER) {
+                return candidate.getOnosInstanceId();
+            }
+        }
+        return null;
+    }
+
     /**
      * Puts a SwitchEvent.
      *
@@ -670,6 +704,49 @@
         }
     }
 
+    /**
+     * Puts a mastership change event.
+     *
+     * @param master MastershipEvent
+     */
+    @GuardedBy("writeLock")
+    protected void putSwitchMastershipEvent(MastershipEvent master) {
+        checkNotNull(master);
+
+        SortedSet<MastershipEvent> candidates
+            = mastership.get(master.getDpid());
+        if (candidates == null) {
+            // SortedSet, customized so that MASTER MastershipEvent appear
+            // earlier during iteration.
+            candidates = new TreeSet<>(new MastershipEvent.MasterFirstComparator());
+        }
+
+        // always replace
+        candidates.remove(master);
+        candidates.add(master);
+    }
+
+    /**
+     * Removes a mastership change event.
+     * <p>
+     * Note: Only Dpid and OnosInstanceId will be used to identify the
+     * {@link MastershipEvent} to remove.
+     *
+     * @param master {@link MastershipEvent} to remove. (Role is ignored)
+     */
+    @GuardedBy("writeLock")
+    protected void removeSwitchMastershipEvent(MastershipEvent master) {
+        checkNotNull(master);
+
+        SortedSet<MastershipEvent> candidates
+            = mastership.get(master.getDpid());
+        if (candidates == null) {
+            // nothing to do
+            return;
+        }
+        candidates.remove(master);
+    }
+
 
     @Override
     public void acquireReadLock() {