diff --git a/src/main/java/net/floodlightcontroller/core/web/ControllerSwitchesResource.java b/src/main/java/net/floodlightcontroller/core/web/ControllerSwitchesResource.java index fd20d387e3dd4fb68bbf2e1bd406201d807c5f01..b8d8e0bf9e34c73113c0aa955edaa5443aa18eef 100644 --- a/src/main/java/net/floodlightcontroller/core/web/ControllerSwitchesResource.java +++ b/src/main/java/net/floodlightcontroller/core/web/ControllerSwitchesResource.java @@ -62,16 +62,24 @@ public class ControllerSwitchesResource extends ServerResource { public Map<String,String> getDescription() { Map<String,String> rv = new HashMap<String, String>(); - rv.put("manufacturer", - sw.getDescriptionStatistics().getManufacturerDescription()); - rv.put("hardware", - sw.getDescriptionStatistics().getHardwareDescription()); - rv.put("software", - sw.getDescriptionStatistics().getSoftwareDescription()); - rv.put("serialNum", - sw.getDescriptionStatistics().getSerialNumber()); - rv.put("datapath", - sw.getDescriptionStatistics().getDatapathDescription()); + if (sw.getDescriptionStatistics() == null) { + rv.put("manufacturer", ""); + rv.put("hardware", ""); + rv.put("software", ""); + rv.put("serialNum", ""); + rv.put("datapath", ""); + } else { + rv.put("manufacturer", + sw.getDescriptionStatistics().getManufacturerDescription()); + rv.put("hardware", + sw.getDescriptionStatistics().getHardwareDescription()); + rv.put("software", + sw.getDescriptionStatistics().getSoftwareDescription()); + rv.put("serialNum", + sw.getDescriptionStatistics().getSerialNumber()); + rv.put("datapath", + sw.getDescriptionStatistics().getDatapathDescription()); + } return rv; } @@ -92,6 +100,8 @@ public class ControllerSwitchesResource extends ServerResource { } public String getHarole() { + if (sw.getHARole() == null) + return "null"; return sw.getHARole().toString(); } diff --git a/src/main/java/net/floodlightcontroller/debugevent/Event.java b/src/main/java/net/floodlightcontroller/debugevent/Event.java index ebbeb650ff771b8040074c589c4ffcd438afae42..28c242a07a5273e0db658f5be1ab1b8e3df5ed34 100644 --- a/src/main/java/net/floodlightcontroller/debugevent/Event.java +++ b/src/main/java/net/floodlightcontroller/debugevent/Event.java @@ -1,12 +1,15 @@ package net.floodlightcontroller.debugevent; +import java.lang.ref.SoftReference; import java.lang.reflect.Field; import java.text.SimpleDateFormat; import java.util.HashMap; +import java.util.List; import java.util.Map; import net.floodlightcontroller.debugevent.IDebugEventService.EventColumn; import net.floodlightcontroller.debugevent.IDebugEventService.EventFieldType; +import net.floodlightcontroller.devicemanager.SwitchPort; import net.floodlightcontroller.packet.IPv4; import org.openflow.util.HexString; @@ -129,14 +132,95 @@ public class Event { f.setAccessible(true); try { Object obj = f.get(eventData); - if (ec.description() == EventFieldType.DPID) { - retMap.put(ec.name(), HexString.toHexString((Long) obj)); - } else if (ec.description() == EventFieldType.MAC) { - retMap.put(ec.name(), HexString.toHexString((Long) obj, 6)); - } else if (ec.description() == EventFieldType.IPv4) { - retMap.put(ec.name(), IPv4.fromIPv4Address((Integer) obj)); - } else { - retMap.put(ec.name(), obj.toString()); + + switch(ec.description()) { + case DPID: + retMap.put(ec.name(), HexString.toHexString((Long) obj)); + break; + case MAC: + retMap.put(ec.name(), HexString.toHexString((Long) obj, 6)); + break; + case IPv4: + retMap.put(ec.name(), IPv4.fromIPv4Address((Integer) obj)); + break; + case LIST_IPV4: + @SuppressWarnings("unchecked") + List<Integer> ipv4Addresses = (List<Integer>)obj; + StringBuilder ipv4AddressesStr = new StringBuilder(); + if (ipv4Addresses.size() == 0) { + ipv4AddressesStr.append("--"); + } else { + for (Integer ipv4Addr : ipv4Addresses) { + ipv4AddressesStr.append(IPv4.fromIPv4Address(ipv4Addr.intValue())); + ipv4AddressesStr.append(" "); + } + } + retMap.put(ec.name(), ipv4AddressesStr.toString()); + break; + case LIST_ATTACHMENT_POINT: + @SuppressWarnings("unchecked") + List<SwitchPort> aps = (List<SwitchPort>)obj; + StringBuilder apsStr = new StringBuilder(); + if (aps.size() == 0) { + apsStr.append("--"); + } else { + for (SwitchPort ap : aps) { + apsStr.append(HexString.toHexString(ap.getSwitchDPID())); + apsStr.append("/"); + apsStr.append(ap.getPort()); + apsStr.append(" "); + } + } + retMap.put(ec.name(), apsStr.toString()); + break; + case LIST_OBJECT: + @SuppressWarnings("unchecked") + List<Object> obl = (List<Object>)obj; + StringBuilder sbldr = new StringBuilder(); + if (obl.size() == 0) { + sbldr.append("--"); + } else { + for (Object o : obl) { + sbldr.append(o.toString()); + sbldr.append(" "); + } + } + retMap.put(ec.name(), sbldr.toString()); + break; + case SREF_LIST_OBJECT: + @SuppressWarnings("unchecked") + SoftReference<List<Object>> srefListObj = + (SoftReference<List<Object>>)obj; + List<Object> ol = srefListObj.get(); + if (ol != null) { + StringBuilder sb = new StringBuilder(); + if (ol.size() == 0) { + sb.append("--"); + } else { + for (Object o : ol) { + sb.append(o.toString()); + sb.append(" "); + } + } + retMap.put(ec.name(), sb.toString()); + } else { + retMap.put(ec.name(), "-- reference not available --"); + } + break; + case SREF_OBJECT: + @SuppressWarnings("unchecked") + SoftReference<Object> srefObj = (SoftReference<Object>)obj; + Object o = srefObj.get(); + if (o != null) { + retMap.put(ec.name(), o.toString()); + } else { + retMap.put(ec.name(), "-- reference not available --"); + } + case STRING: + case OBJECT: + case PRIMITIVE: + default: + retMap.put(ec.name(), obj.toString()); } } catch (ClassCastException e) { retMap.put("Error", e.getMessage()); diff --git a/src/main/java/net/floodlightcontroller/debugevent/IDebugEventService.java b/src/main/java/net/floodlightcontroller/debugevent/IDebugEventService.java index 7146c8ffe236e23dd330df2b5c739871f8ac7c5d..300f8fa096b9fe98bb45801fd3fb7517f7791757 100644 --- a/src/main/java/net/floodlightcontroller/debugevent/IDebugEventService.java +++ b/src/main/java/net/floodlightcontroller/debugevent/IDebugEventService.java @@ -25,7 +25,8 @@ public interface IDebugEventService extends IFloodlightService { * Describes the type of field obtained from reflection */ enum EventFieldType { - DPID, IPv4, MAC, STRING, OBJECT, PRIMITIVE + DPID, IPv4, MAC, STRING, OBJECT, PRIMITIVE, LIST_IPV4, + LIST_ATTACHMENT_POINT, LIST_OBJECT, SREF_LIST_OBJECT, SREF_OBJECT, } /** @@ -89,7 +90,7 @@ public interface IDebugEventService extends IFloodlightService { * be explicitly enabled using other methods in this API * @param eventClass A user defined class that annotates the fields * with @EventColumn. This class specifies the - * fields/columns of this event. + * fields/columns for this event. * @param bufferCapacity Number of events to store for this event in a circular * buffer. Older events will be discarded once the * buffer is full. diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/Device.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/Device.java index 457bb564405d1e6bedbd7eda6fe682438163cc98..ef0ba346809496090c83e273be35c5576699369c 100755 --- a/src/main/java/net/floodlightcontroller/devicemanager/internal/Device.java +++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/Device.java @@ -464,14 +464,11 @@ public class Device implements IDevice { } else if (oldAPFlag) { // retain oldAP as is. Put the newAP in oldAPs for flagging // possible duplicates. - oldAPList = new ArrayList<AttachmentPoint>(); - if (oldAPs != null) oldAPList.addAll(oldAPs); - // Add to oldAPList only if it was picked up from the oldAPList - oldAPList.add(newAP); - this.oldAPs = oldAPList; - if (!topology.isInSameBroadcastDomain(oldAP.getSw(), oldAP.getPort(), - newAP.getSw(), newAP.getPort())) - return true; // attachment point changed. + oldAPList = new ArrayList<AttachmentPoint>(); + if (oldAPs != null) oldAPList.addAll(oldAPs); + // Add ot oldAPList only if it was picked up from the oldAPList + oldAPList.add(newAP); + this.oldAPs = oldAPList; } return false; } diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImpl.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImpl.java index 5223fb6ed1ad6a47749510bb1f7715dde9dfd2e0..276e6629f9ac9f539179631503a7e02d48e45701 100755 --- a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImpl.java +++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImpl.java @@ -18,6 +18,7 @@ package net.floodlightcontroller.devicemanager.internal; import java.util.ArrayList; +import java.util.Arrays; import java.util.Calendar; import java.util.Collection; import java.util.Collections; @@ -57,6 +58,13 @@ import net.floodlightcontroller.debugcounter.IDebugCounterService; import net.floodlightcontroller.debugcounter.IDebugCounterService.CounterException; import net.floodlightcontroller.debugcounter.NullDebugCounter; import net.floodlightcontroller.debugcounter.IDebugCounterService.CounterType; +import net.floodlightcontroller.debugevent.IDebugEventService; +import net.floodlightcontroller.debugevent.IDebugEventService.EventColumn; +import net.floodlightcontroller.debugevent.IDebugEventService.EventFieldType; +import net.floodlightcontroller.debugevent.IDebugEventService.EventType; +import net.floodlightcontroller.debugevent.IDebugEventService.MaxEventsRegistered; +import net.floodlightcontroller.debugevent.IEventUpdater; +import net.floodlightcontroller.debugevent.NullDebugEvent; import net.floodlightcontroller.devicemanager.IDevice; import net.floodlightcontroller.devicemanager.IDeviceService; import net.floodlightcontroller.devicemanager.IEntityClass; @@ -162,6 +170,12 @@ IFlowReconcileListener, IInfoProvider { public IDebugCounter cntConsolidateStoreDevicesRemoved; public IDebugCounter cntTransitionToMaster; + /** + * Debug Events + */ + private IDebugEventService debugEvents; + private IEventUpdater<DeviceEvent> evDevice; + private boolean isMaster = false; static final String DEVICE_SYNC_STORE_NAME = @@ -659,6 +673,69 @@ IFlowReconcileListener, IInfoProvider { } } + // *************** + // IDeviceListener + // *************** + private class DeviceDebugEventLogger implements IDeviceListener { + @Override + public String getName() { + return "deviceDebugEventLogger"; + } + + @Override + public boolean isCallbackOrderingPrereq(String type, String name) { + return false; + } + + @Override + public boolean isCallbackOrderingPostreq(String type, String name) { + return false; + } + + @Override + public void deviceAdded(IDevice device) { + generateDeviceEvent(device, "host-added"); + } + + @Override + public void deviceRemoved(IDevice device) { + generateDeviceEvent(device, "host-removed"); + } + + @Override + public void deviceMoved(IDevice device) { + generateDeviceEvent(device, "host-moved"); + } + + @Override + public void deviceIPV4AddrChanged(IDevice device) { + generateDeviceEvent(device, "host-ipv4-addr-changed"); + } + + @Override + public void deviceVlanChanged(IDevice device) { + generateDeviceEvent(device, "host-vlan-changed"); + } + + private void generateDeviceEvent(IDevice device, String reason) { + List<Integer> ipv4Addresses = + new ArrayList<Integer>(Arrays.asList(device.getIPv4Addresses())); + List<SwitchPort> oldAps = + new ArrayList<SwitchPort>(Arrays.asList(device.getOldAP())); + List<SwitchPort> currentAps = + new ArrayList<SwitchPort>(Arrays.asList(device.getAttachmentPoints())); + List<Short> vlanIds = + new ArrayList<Short>(Arrays.asList(device.getVlanId())); + + evDevice.updateEventNoFlush( + new DeviceEvent(device.getMACAddress(), + ipv4Addresses, + oldAps, + currentAps, + vlanIds, reason)); + } + } + // ************* // IInfoProvider // ************* @@ -832,10 +909,27 @@ IFlowReconcileListener, IInfoProvider { this.flowReconcileEngine = fmc.getServiceImpl(IFlowReconcileEngineService.class); this.entityClassifier = fmc.getServiceImpl(IEntityClassifierService.class); this.debugCounters = fmc.getServiceImpl(IDebugCounterService.class); + this.debugEvents = fmc.getServiceImpl(IDebugEventService.class); this.syncService = fmc.getServiceImpl(ISyncService.class); this.deviceSyncManager = new DeviceSyncManager(); this.haListenerDelegate = new HAListenerDelegate(); registerDeviceManagerDebugCounters(); + registerDeviceManagerDebugEvents(); + this.addListener(new DeviceDebugEventLogger()); + } + + private void registerDeviceManagerDebugEvents() throws FloodlightModuleException { + if (debugEvents == null) { + debugEvents = new NullDebugEvent(); + } + try { + evDevice = + debugEvents.registerEvent(PACKAGE, "hostevent", + "Host added, removed, updated, or moved", + EventType.ALWAYS_LOG, DeviceEvent.class, 500); + } catch (MaxEventsRegistered e) { + throw new FloodlightModuleException("Max events registered", e); + } } @Override @@ -2493,4 +2587,37 @@ IFlowReconcileListener, IInfoProvider { IHAListener getHAListener() { return this.haListenerDelegate; } + + /** + * Device Event Class used to log Device related events + */ + private class DeviceEvent { + @EventColumn(name = "MAC", description = EventFieldType.MAC) + private final long macAddress; + @EventColumn(name = "IPs", description = EventFieldType.LIST_IPV4) + private final List<Integer> ipv4Addresses; + @EventColumn(name = "Old Attachment Points", + description = EventFieldType.LIST_ATTACHMENT_POINT) + private final List<SwitchPort> oldAttachmentPoints; + @EventColumn(name = "Current Attachment Points", + description = EventFieldType.LIST_ATTACHMENT_POINT) + private final List<SwitchPort> currentAttachmentPoints; + @EventColumn(name = "VLAN IDs", description = EventFieldType.LIST_OBJECT) + private final List<Short> vlanIds; + @EventColumn(name = "Reason", description = EventFieldType.STRING) + private final String reason; + + public DeviceEvent(long macAddress, List<Integer> ipv4Addresses, + List<SwitchPort> oldAttachmentPoints, + List<SwitchPort> currentAttachmentPoints, + List<Short> vlanIds, String reason) { + super(); + this.macAddress = macAddress; + this.ipv4Addresses = ipv4Addresses; + this.oldAttachmentPoints = oldAttachmentPoints; + this.currentAttachmentPoints = currentAttachmentPoints; + this.vlanIds = vlanIds; + this.reason = reason; + } + } } diff --git a/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java b/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java index 9b3cd7b871af7151a77644f6ff2be6ceab0a61ac..6231f837b043c68f9ea604f64139a008eaf56ee8 100644 --- a/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java +++ b/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java @@ -18,7 +18,6 @@ package net.floodlightcontroller.linkdiscovery.internal; import java.io.IOException; -import java.net.InetAddress; import java.net.NetworkInterface; import java.net.SocketException; import java.nio.ByteBuffer; @@ -84,7 +83,6 @@ import net.floodlightcontroller.notification.INotificationManager; import net.floodlightcontroller.notification.NotificationManagerFactory; import net.floodlightcontroller.packet.BSN; import net.floodlightcontroller.packet.Ethernet; -import net.floodlightcontroller.packet.IPv4; import net.floodlightcontroller.packet.LLDP; import net.floodlightcontroller.packet.LLDPTLV; import net.floodlightcontroller.restserver.IRestApiService; diff --git a/src/main/java/net/floodlightcontroller/topology/TopologyManager.java b/src/main/java/net/floodlightcontroller/topology/TopologyManager.java index c92e4b710fd051c562ebb8f27aeb8b16d62fbade..961d6259cb49c1cb29cfc1e6f4da73db0983d860 100644 --- a/src/main/java/net/floodlightcontroller/topology/TopologyManager.java +++ b/src/main/java/net/floodlightcontroller/topology/TopologyManager.java @@ -51,6 +51,13 @@ import net.floodlightcontroller.debugcounter.IDebugCounterService; import net.floodlightcontroller.debugcounter.IDebugCounterService.CounterException; import net.floodlightcontroller.debugcounter.IDebugCounterService.CounterType; import net.floodlightcontroller.debugcounter.NullDebugCounter; +import net.floodlightcontroller.debugevent.IDebugEventService; +import net.floodlightcontroller.debugevent.IEventUpdater; +import net.floodlightcontroller.debugevent.NullDebugEvent; +import net.floodlightcontroller.debugevent.IDebugEventService.EventColumn; +import net.floodlightcontroller.debugevent.IDebugEventService.EventFieldType; +import net.floodlightcontroller.debugevent.IDebugEventService.EventType; +import net.floodlightcontroller.debugevent.IDebugEventService.MaxEventsRegistered; import net.floodlightcontroller.linkdiscovery.ILinkDiscoveryListener; import net.floodlightcontroller.linkdiscovery.ILinkDiscoveryService; import net.floodlightcontroller.packet.BSN; @@ -164,6 +171,65 @@ public class TopologyManager implements protected static final String PACKAGE = TopologyManager.class.getPackage().getName(); protected IDebugCounter ctrIncoming; + /** + * Debug Events + */ + protected IDebugEventService debugEvents; + + /* + * Topology Event Updater + */ + protected IEventUpdater<TopologyEvent> evTopology; + + /** + * Topology Information exposed for a Topology related event - used inside + * the BigTopologyEvent class + */ + protected class TopologyEventInfo { + private final int numOpenflowClusters; + private final int numExternalClusters; + private final int numExternalPorts; + private final int numTunnelPorts; + public TopologyEventInfo(int numOpenflowClusters, + int numExternalClusters, int numExternalPorts, + int numTunnelPorts) { + super(); + this.numOpenflowClusters = numOpenflowClusters; + this.numExternalClusters = numExternalClusters; + this.numExternalPorts = numExternalPorts; + this.numTunnelPorts = numTunnelPorts; + } + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("# Openflow Clusters: "); + builder.append(numOpenflowClusters); + builder.append(", # External Clusters: "); + builder.append(numExternalClusters); + builder.append(", # External Ports: "); + builder.append(numExternalPorts); + builder.append(", # Tunnel Ports: "); + builder.append(numTunnelPorts); + return builder.toString(); + } + } + + /** + * Topology Event class to track topology related events + */ + protected class TopologyEvent { + @EventColumn(name = "Reason", description = EventFieldType.STRING) + private final String reason; + @EventColumn(name = "Topology Summary") + private final TopologyEventInfo topologyInfo; + public TopologyEvent(String reason, + TopologyEventInfo topologyInfo) { + super(); + this.reason = reason; + this.topologyInfo = topologyInfo; + } + } + // Getter/Setter methods /** * Get the time interval for the period topology updates, if any. @@ -221,7 +287,7 @@ public class TopologyManager implements dtLinksUpdated = false; tunnelPortsUpdated = false; List<LDUpdate> appliedUpdates = applyUpdates(); - newInstanceFlag = createNewInstance(); + newInstanceFlag = createNewInstance("link-discovery-updates"); lastUpdateTime = new Date(); informListeners(appliedUpdates); return newInstanceFlag; @@ -762,6 +828,7 @@ public class TopologyManager implements context.getServiceImpl(IFloodlightProviderService.class); restApi = context.getServiceImpl(IRestApiService.class); debugCounters = context.getServiceImpl(IDebugCounterService.class); + debugEvents = context.getServiceImpl(IDebugEventService.class); switchPorts = new HashMap<Long,Set<Short>>(); switchPortLinks = new HashMap<NodePortTuple, Set<Link>>(); @@ -772,6 +839,22 @@ public class TopologyManager implements ldUpdates = new LinkedBlockingQueue<LDUpdate>(); haListener = new HAListenerDelegate(); registerTopologyDebugCounters(); + registerTopologyDebugEvents(); + } + + protected void registerTopologyDebugEvents() throws FloodlightModuleException { + if (debugEvents == null) { + debugEvents = new NullDebugEvent(); + } + try { + evTopology = + debugEvents.registerEvent(PACKAGE, "topologyevent", + "Topology Computation", + EventType.ALWAYS_LOG, + TopologyEvent.class, 100); + } catch (MaxEventsRegistered e) { + throw new FloodlightModuleException("Max events registered", e); + } } @Override @@ -1084,13 +1167,17 @@ public class TopologyManager implements tunnelPortsUpdated = true; } + public boolean createNewInstance() { + return createNewInstance("internal"); + } + /** * This function computes a new topology instance. * It ignores links connected to all broadcast domain ports * and tunnel ports. The method returns if a new instance of * topology was created or not. */ - protected boolean createNewInstance() { + protected boolean createNewInstance(String reason) { Set<NodePortTuple> blockedPorts = new HashSet<NodePortTuple>(); if (!linksUpdated) return false; @@ -1140,6 +1227,11 @@ public class TopologyManager implements // If needed, we may compute them differently. currentInstance = nt; currentInstanceWithoutTunnels = nt; + + TopologyEventInfo topologyInfo = + new TopologyEventInfo(nt.getClusters().size(), 0, 0, 0); + evTopology.updateEventWithFlush(new TopologyEvent(reason, + topologyInfo)); return true; } @@ -1388,7 +1480,7 @@ public class TopologyManager implements linksUpdated = true; dtLinksUpdated = true; tunnelPortsUpdated = true; - createNewInstance(); + createNewInstance("startup"); lastUpdateTime = new Date(); } diff --git a/src/test/java/net/floodlightcontroller/debugevent/DebugEventTest.java b/src/test/java/net/floodlightcontroller/debugevent/DebugEventTest.java index 5fbaba9d47ab0031a5792c822d6721f2314d0efe..e48a816db38a68ed339c327867831410e484d63a 100644 --- a/src/test/java/net/floodlightcontroller/debugevent/DebugEventTest.java +++ b/src/test/java/net/floodlightcontroller/debugevent/DebugEventTest.java @@ -9,7 +9,6 @@ import net.floodlightcontroller.debugevent.IDebugEventService.DebugEventInfo; import net.floodlightcontroller.debugevent.IDebugEventService.EventColumn; import net.floodlightcontroller.debugevent.IDebugEventService.EventFieldType; import net.floodlightcontroller.debugevent.IDebugEventService.EventType; -import net.floodlightcontroller.debugevent.IDebugEventService.MaxEventsRegistered; import net.floodlightcontroller.test.FloodlightTestCase; public class DebugEventTest extends FloodlightTestCase { diff --git a/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImplTest.java b/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImplTest.java index 36a844d1c251f0d10b963460a432303ec077a0da..ecd145f969176aafb8e7a0ede3dcbc87bd5fb731 100644 --- a/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImplTest.java +++ b/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImplTest.java @@ -661,6 +661,106 @@ public class DeviceManagerImplTest extends FloodlightTestCase { verify(mockListener); } + /** + * In this test, a device is moved from attachment point (1,1) to (5,1) + * and then moved back to (1,1) within 30 seconds. Both the moves should + * generate device moved notification. + * @throws Exception + */ + @Test + public void testAttachmentPointMovingBack() throws Exception { + IDeviceListener mockListener = + createMock(IDeviceListener.class); + expect(mockListener.getName()).andReturn("mockListener").atLeastOnce(); + expect(mockListener.isCallbackOrderingPostreq((String)anyObject(), (String)anyObject())) + .andReturn(false).atLeastOnce(); + expect(mockListener.isCallbackOrderingPrereq((String)anyObject(), (String)anyObject())) + .andReturn(false).atLeastOnce(); + + replay(mockListener); + deviceManager.addListener(mockListener); + verify(mockListener); + reset(mockListener); + + ITopologyService mockTopology = createMock(ITopologyService.class); + expect(mockTopology.getL2DomainId(1L)). + andReturn(1L).anyTimes(); + expect(mockTopology.getL2DomainId(5L)). + andReturn(1L).anyTimes(); + expect(mockTopology.isBroadcastDomainPort(anyLong(), anyShort())). + andReturn(false).anyTimes(); + expect(mockTopology.isInSameBroadcastDomain(anyLong(), anyShort(), + anyLong(), anyShort())) + .andReturn(false).anyTimes(); + + expect(mockTopology.isAttachmentPointPort(anyLong(), + anyShort())).andReturn(true).anyTimes(); + expect(mockTopology.isConsistent(1L, (short)1, 5L, (short)1)). + andReturn(false).anyTimes(); + + Date topologyUpdateTime = new Date(); + expect(mockTopology.getLastUpdateTime()).andReturn(topologyUpdateTime). + anyTimes(); + + replay(mockTopology); + + deviceManager.topology = mockTopology; + + Calendar c = Calendar.getInstance(); + Entity entity1 = new Entity(1L, null, null, 1L, 1, c.getTime()); + c.add(Calendar.SECOND, 1); + Entity entity2 = new Entity(1L, null, null, 5L, 1, c.getTime()); + c.add(Calendar.SECOND, 1); + Entity entity3 = new Entity(1L, null, null, 1L, 1, c.getTime()); + c.add(Calendar.SECOND, 1); + Entity entity4 = new Entity(1L, null, null, 5L, 1, c.getTime()); + + IDevice d; + SwitchPort[] aps; + + mockListener.deviceAdded(isA(IDevice.class)); + replay(mockListener); + + d = deviceManager.learnDeviceByEntity(entity1); + assertEquals(1, deviceManager.getAllDevices().size()); + aps = d.getAttachmentPoints(); + assertArrayEquals(new SwitchPort[] { new SwitchPort(1L, 1) }, aps); + verify(mockListener); + + reset(mockListener); + mockListener.deviceMoved((isA(IDevice.class))); + replay(mockListener); + + d = deviceManager.learnDeviceByEntity(entity2); + assertEquals(1, deviceManager.getAllDevices().size()); + aps = d.getAttachmentPoints(); + + assertArrayEquals(new SwitchPort[] { new SwitchPort(5L, 1) }, aps); + verify(mockListener); + + reset(mockListener); + mockListener.deviceMoved((isA(IDevice.class))); + replay(mockListener); + + d = deviceManager.learnDeviceByEntity(entity3); + assertEquals(1, deviceManager.getAllDevices().size()); + assertArrayEquals(new SwitchPort[] { new SwitchPort(1L, 1), + new SwitchPort(5L, 1, ErrorStatus.DUPLICATE_DEVICE)}, + d.getAttachmentPoints(true)); + verify(mockListener); + + // Generate a packet-in again from 5,1 and ensure that it doesn't + // create a device moved event. + reset(mockListener); + replay(mockListener); + d = deviceManager.learnDeviceByEntity(entity4); + assertEquals(1, deviceManager.getAllDevices().size()); + assertArrayEquals(new SwitchPort[] { new SwitchPort(1L, 1), + new SwitchPort(5L, 1, ErrorStatus.DUPLICATE_DEVICE)}, + d.getAttachmentPoints(true)); + verify(mockListener); + } + private void verifyEntityArray(Entity[] expected, Device d) { Arrays.sort(expected); assertArrayEquals(expected, d.entities);