From 4c2b615fc0d33467d22602ec22c09faf1f9f0534 Mon Sep 17 00:00:00 2001
From: Rob Adams <rob.adams@bigswitch.com>
Date: Fri, 27 Apr 2012 13:48:02 -0700
Subject: [PATCH] Port broadcast domain logic from old device manager

---
 .../devicemanager/internal/Device.java        |   2 +-
 .../internal/DeviceManagerImpl.java           | 104 ++++++++++++------
 .../internal/DeviceManagerImplTest.java       |  52 +++++++++
 3 files changed, 124 insertions(+), 34 deletions(-)

diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/Device.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/Device.java
index f8bbbe578..1a31036aa 100755
--- a/src/main/java/net/floodlightcontroller/devicemanager/internal/Device.java
+++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/Device.java
@@ -237,7 +237,7 @@ public class Device implements IDevice {
         // XXX - TODO suppress flapping
         // XXX - TODO handle suppressing switching to a broadcast domain
         Entity[] clentities = Arrays.<Entity>copyOf(entities, entities.length);
-        Arrays.sort(clentities, deviceManager.clusterIdComparator);
+        Arrays.sort(clentities, deviceManager.apComparator);
         
         ITopologyService topology = deviceManager.topology;
         long prevCluster = 0;
diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImpl.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImpl.java
index b2ac5a5fd..dcc1b4783 100755
--- a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImpl.java
+++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImpl.java
@@ -101,6 +101,14 @@ public class DeviceManagerImpl implements
      */
     protected static final int ENTITY_CLEANUP_INTERVAL = 60*60;
     
+    /**
+     * Attachment points on a broadcast domain will have lower priority
+     * than attachment points in openflow domains.  This is the timeout
+     * for switching from a non-broadcast domain to a broadcast domain 
+     * attachment point.
+     */
+    protected static long NBD_TO_BD_TIMEDIFF_MS = 300000; // 5 minutes
+    
     /**
      * This is the master device map that maps device IDs to {@link Device}
      * objects.
@@ -219,10 +227,71 @@ public class DeviceManagerImpl implements
         }
     }
     
+    /**
+     * Comparator for finding the correct attachment point to use based on
+     * the set of entities
+     */
+    protected class AttachmentPointComparator 
+            implements Comparator<Entity> {
+
+        
+        public AttachmentPointComparator() {
+            super();
+        }
+
+        @Override
+        public int compare(Entity e1, Entity e2) {
+            int r = 0;
+
+            Long swdpid1 = e1.getSwitchDPID();
+            Long swdpid2 = e2.getSwitchDPID();
+            if (swdpid1 == null)
+                r = swdpid2 == null ? 0 : -1;
+            else if (swdpid2 == null)
+                r = 1;
+            else {
+                Long d1ClusterId = 
+                        topology.getSwitchClusterId(swdpid1);
+                Long d2ClusterId = 
+                        topology.getSwitchClusterId(swdpid2);
+                r = d1ClusterId.compareTo(d2ClusterId);
+            }
+            if (r != 0) return r;
+            
+            Date e1t = e1.getLastSeenTimestamp();
+            Date e2t = e2.getLastSeenTimestamp();
+            if (e1t == null)
+                r = e2t == null ? 0 : -1;
+            else if (e2t == null)
+                r = 1;
+            else {
+                long e1ts = e1t.getTime();
+                long e2ts = e2t.getTime();
+                if (topology.
+                        isBroadcastDomainPort(e1.getSwitchDPID(), 
+                                              e1.getSwitchPort().
+                                                  shortValue())) {
+                    e1ts -= NBD_TO_BD_TIMEDIFF_MS;
+                }
+                if (topology.
+                        isBroadcastDomainPort(e2.getSwitchDPID(), 
+                                              e2.getSwitchPort().
+                                                  shortValue())) {
+                    e2ts -= NBD_TO_BD_TIMEDIFF_MS;
+                }
+                
+                r = Long.valueOf(e1ts).compareTo(e2ts);
+            }
+
+            return r;
+        }
+        
+    }
+    
     /**
      * Comparator for sorting by cluster ID
      */
-    public Comparator<Entity> clusterIdComparator;
+    public Comparator<Entity> apComparator;
     
     /**
      * Periodic task to clean up expired entities
@@ -520,38 +589,7 @@ public class DeviceManagerImpl implements
         deviceMap = new ConcurrentHashMap<Long, Device>();
         classStateMap = 
                 new ConcurrentHashMap<IEntityClass, ClassState>();
-        clusterIdComparator = new Comparator<Entity>() {
-            @Override
-            public int compare(Entity e1, Entity e2) {
-                int r = 0;
-
-                Long swdpid1 = e1.getSwitchDPID();
-                Long swdpid2 = e2.getSwitchDPID();
-                if (swdpid1 == null)
-                    r = swdpid2 == null ? 0 : -1;
-                else if (swdpid2 == null)
-                    r = 1;
-                else {
-                    Long d1ClusterId = 
-                            topology.getSwitchClusterId(swdpid1);
-                    Long d2ClusterId = 
-                            topology.getSwitchClusterId(swdpid2);
-                    r = d1ClusterId.compareTo(d2ClusterId);
-                }
-                if (r != 0) return r;
-                
-                Date e1t = e1.getLastSeenTimestamp();
-                Date e2t = e2.getLastSeenTimestamp();
-                if (e1t == null)
-                    r = e2t == null ? 0 : -1;
-                else if (e2t == null)
-                    r = 1;
-                else
-                    r = e1t.compareTo(e2t);
-
-                return r;
-            }
-        };
+        apComparator = new AttachmentPointComparator();
         
         floodlightProvider.addOFMessageListener(OFType.PACKET_IN, this);
         
diff --git a/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImplTest.java b/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImplTest.java
index cfa01fc69..587b76b16 100644
--- a/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImplTest.java
+++ b/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImplTest.java
@@ -248,6 +248,9 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
         ITopologyService mockTopology = createMock(ITopologyService.class);
         expect(mockTopology.getSwitchClusterId(anyLong())).
             andReturn(1L).anyTimes();
+        expect(mockTopology.isBroadcastDomainPort(anyLong(), anyShort())).
+        andReturn(false).anyTimes();
+
         expect(mockTopology.isInternal(anyLong(), 
                                        anyShort())).andReturn(false).anyTimes();
         deviceManager.topology = mockTopology;
@@ -359,6 +362,8 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
         andReturn(10L).anyTimes();
         expect(mockTopology.getSwitchClusterId(50L)).
         andReturn(10L).anyTimes();
+        expect(mockTopology.isBroadcastDomainPort(anyLong(), anyShort())).
+                andReturn(false).anyTimes();
         
         expect(mockTopology.isInternal(anyLong(), 
                                        anyShort())).andReturn(false).anyTimes();
@@ -430,6 +435,53 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
         verify(mockListener);
     }
 
+    @Test
+    public void testBDAttachmentPointLearning() throws Exception {
+        ITopologyService mockTopology = createMock(ITopologyService.class);
+        expect(mockTopology.getSwitchClusterId(anyLong())).
+                andReturn(1L).anyTimes();
+        expect(mockTopology.isInternal(anyLong(), anyShort())).
+                andReturn(false).anyTimes();
+        expect(mockTopology.isBroadcastDomainPort(1L, (short)1)).
+                andReturn(false).anyTimes();
+        expect(mockTopology.isBroadcastDomainPort(1L, (short)2)).
+                andReturn(true).anyTimes();
+        
+        replay(mockTopology);
+        
+        deviceManager.topology = mockTopology;
+        
+        Calendar c = Calendar.getInstance();
+        Entity entity1 = new Entity(1L, null, 1, 1L, 1, c.getTime());
+        c.add(Calendar.MILLISECOND, 
+              (int)DeviceManagerImpl.NBD_TO_BD_TIMEDIFF_MS / 2);
+        Entity entity2 = new Entity(1L, null, null, 1L, 2, c.getTime());
+        c.add(Calendar.MILLISECOND, 
+              (int)DeviceManagerImpl.NBD_TO_BD_TIMEDIFF_MS / 2 + 1);
+        Entity entity3 = new Entity(1L, null, null, 1L, 2, c.getTime());
+        
+        IDevice d;
+        SwitchPort[] aps;
+
+        d = deviceManager.learnDeviceByEntity(entity1);
+        assertEquals(1, deviceManager.getAllDevices().size());
+        aps = d.getAttachmentPoints(); 
+        assertArrayEquals(new SwitchPort[] { new SwitchPort(1L, 1) }, aps);
+
+        // this timestamp is too soon; don't switch
+        d = deviceManager.learnDeviceByEntity(entity2);
+        assertEquals(1, deviceManager.getAllDevices().size());
+        aps = d.getAttachmentPoints(); 
+        assertArrayEquals(new SwitchPort[] { new SwitchPort(1L, 1) }, aps);
+
+        // it should switch when we learn with a timestamp after the 
+        // timeout
+        d = deviceManager.learnDeviceByEntity(entity3);
+        assertEquals(1, deviceManager.getAllDevices().size());
+        aps = d.getAttachmentPoints(); 
+        assertArrayEquals(new SwitchPort[] { new SwitchPort(1L, 2) }, aps);
+    }
+
     @Test
     public void testPacketIn() throws Exception {
         byte[] dataLayerSource = 
-- 
GitLab