Cherry-pick from https://gerrit.onos.onlab.us/#/c/342/
Cherry-pick from https://gerrit.onos.onlab.us/#/c/322/
Suppress device update for the same device.
Add function to clean devices.
Conflicts:
src/main/java/net/onrc/onos/ofcontroller/devicemanager/OnosDeviceManager.java
NOTE: The above conflict has been resolved by hand.
The conflict was in the number of parameters when
calling deviceStorage.init() inside file OnosDeviceManager.java:
deviceStorage.init("") -> deviceStorage.init("","")
Conflicts:
src/main/java/net/onrc/onos/datagrid/HazelcastDatagrid.java
src/main/java/net/onrc/onos/datagrid/IDatagridService.java
NOTE: The above conflict has been resolved by hand.
Change-Id: I831e391efce9a795370be99238834b6bc7db2bee
diff --git a/src/main/java/net/onrc/onos/datagrid/HazelcastDatagrid.java b/src/main/java/net/onrc/onos/datagrid/HazelcastDatagrid.java
index fae09ec..33fa54d 100755
--- a/src/main/java/net/onrc/onos/datagrid/HazelcastDatagrid.java
+++ b/src/main/java/net/onrc/onos/datagrid/HazelcastDatagrid.java
@@ -17,6 +17,8 @@
import net.floodlightcontroller.core.module.IFloodlightService;
import net.floodlightcontroller.restserver.IRestApiService;
import net.onrc.onos.datagrid.web.DatagridWebRoutable;
+import net.onrc.onos.ofcontroller.devicemanager.IDeviceEventHandler;
+import net.onrc.onos.ofcontroller.devicemanager.OnosDevice;
import net.onrc.onos.ofcontroller.flowmanager.IFlowEventHandlerService;
import net.onrc.onos.ofcontroller.proxyarp.ArpReplyNotification;
import net.onrc.onos.ofcontroller.proxyarp.IArpReplyEventHandler;
@@ -124,6 +126,11 @@
}
+ // State related to the Network Device map
+ protected static final String mapDeviceName = "mapDevice";
+ private IMap<Long, OnosDevice> mapDevice = null;
+ private List<IDeviceEventHandler> deviceEventHandlers = new ArrayList<IDeviceEventHandler>();
+
/**
* Class for receiving notifications for Flow state.
*
@@ -522,6 +529,35 @@
// NOTE: We don't use eviction for this map
}
}
+
+ class MapDeviceListener implements EntryListener<Long, OnosDevice> {
+
+ @Override
+ public void entryAdded(EntryEvent<Long, OnosDevice> event) {
+ for (IDeviceEventHandler deviceEventHandler : deviceEventHandlers) {
+ deviceEventHandler.addDeviceEvent(event.getKey(), event.getValue());
+ }
+ }
+
+ @Override
+ public void entryRemoved(EntryEvent<Long, OnosDevice> event) {
+ for (IDeviceEventHandler deviceEventHandler : deviceEventHandlers) {
+ deviceEventHandler.deleteDeviceEvent(event.getKey(), event.getValue());
+ }
+ }
+
+ @Override
+ public void entryUpdated(EntryEvent<Long, OnosDevice> event) {
+ for (IDeviceEventHandler deviceEventHandler : deviceEventHandlers) {
+ deviceEventHandler.updateDeviceEvent(event.getKey(), event.getValue());
+ }
+ }
+
+ @Override
+ public void entryEvicted(EntryEvent<Long, OnosDevice> arg0) {
+ //Not used.
+ }
+ }
/**
* Class for receiving notifications for sending packet-outs.
@@ -732,7 +768,8 @@
arpReplyMap.addEntryListener(new ArpReplyMapListener(), true);
intentList = hazelcastInstance.getList(intentListName);
-
+ mapDevice = hazelcastInstance.getMap(mapDeviceName);
+ mapDevice.addEntryListener(new MapDeviceListener(), true);
}
/**
@@ -943,6 +980,18 @@
public void deregisterArpReplyEventHandler(IArpReplyEventHandler arpReplyEventHandler) {
arpReplyEventHandlers.remove(arpReplyEventHandler);
}
+
+ @Override
+ public void registerMapDeviceEventHandler(IDeviceEventHandler deviceEventHandler) {
+ if (deviceEventHandler != null) {
+ deviceEventHandlers.add(deviceEventHandler);
+ }
+ }
+
+ @Override
+ public void deregisterMapDeviceEventHandler(IDeviceEventHandler deviceEventHandler) {
+ deviceEventHandlers.remove(deviceEventHandler);
+ }
/**
* Get all Flows that are currently in the datagrid.
@@ -1442,6 +1491,7 @@
// - Value : Serialized TopologyElement (byte[])
//
mapTopology.putAsync(topologyElement.elementId(), valueBytes);
+
}
/**
@@ -1496,4 +1546,19 @@
public void sendArpReplyNotification(ArpReplyNotification arpReply) {
arpReplyMap.putAsync(arpReply, dummyByte, 1L, TimeUnit.MILLISECONDS);
}
+
+ @Override
+ public void sendNotificationDeviceAdded(Long mac, OnosDevice dev) {
+ log.debug("DeviceAdded in datagrid. mac {}", dev.getMacAddress());
+ mapDevice.putAsync(mac, dev);
+ }
+
+ @Override
+ public void sendNotificationDeviceDeleted(OnosDevice dev) {
+ long mac = dev.getMacAddress().toLong();
+ if(mapDevice.containsKey(mac)){
+ log.debug("DeviceDeleted in datagrid. mac {}", dev.getMacAddress());
+ mapDevice.removeAsync(mac);
+ }
+ }
}
diff --git a/src/main/java/net/onrc/onos/datagrid/IDatagridService.java b/src/main/java/net/onrc/onos/datagrid/IDatagridService.java
index 118cbfa..0c79b81 100755
--- a/src/main/java/net/onrc/onos/datagrid/IDatagridService.java
+++ b/src/main/java/net/onrc/onos/datagrid/IDatagridService.java
@@ -4,6 +4,8 @@
import net.floodlightcontroller.core.module.IFloodlightService;
import net.onrc.onos.intent.Intent;
+import net.onrc.onos.ofcontroller.devicemanager.IDeviceEventHandler;
+import net.onrc.onos.ofcontroller.devicemanager.OnosDevice;
import net.onrc.onos.ofcontroller.flowmanager.IFlowEventHandlerService;
import net.onrc.onos.ofcontroller.proxyarp.ArpReplyNotification;
import net.onrc.onos.ofcontroller.proxyarp.IArpReplyEventHandler;
@@ -317,4 +319,13 @@
* @param arpReply The notification of the ARP reply
*/
public void sendArpReplyNotification(ArpReplyNotification arpReply);
+
+ void sendNotificationDeviceAdded(Long mac, OnosDevice dev);
+
+ void sendNotificationDeviceDeleted(OnosDevice dev);
+
+ void registerMapDeviceEventHandler(IDeviceEventHandler deviceEventHandler);
+
+ void deregisterMapDeviceEventHandler(IDeviceEventHandler deviceEventHandler);
+
}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/core/IDeviceStorage.java b/src/main/java/net/onrc/onos/ofcontroller/core/IDeviceStorage.java
index 624137c..53f515c 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/core/IDeviceStorage.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/core/IDeviceStorage.java
@@ -18,4 +18,5 @@
public void commit();
public void addOnosDevice(OnosDevice onosDevice);
+ public void deleteOnosDevice(OnosDevice onosDevice);
}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/core/internal/DeviceStorageImpl.java b/src/main/java/net/onrc/onos/ofcontroller/core/internal/DeviceStorageImpl.java
index 5a67c12..f794a2b 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/core/internal/DeviceStorageImpl.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/core/internal/DeviceStorageImpl.java
@@ -122,6 +122,7 @@
*/
@Override
public void removeDevice(IDevice device) {
+ //Not used
IDeviceObject dev;
if ((dev = ope.searchDevice(device.getMACAddressString())) != null) {
@@ -428,5 +429,21 @@
ope.rollback();
}
}
+
+ @Override
+ public void deleteOnosDevice(OnosDevice onosDevice){
+ String macAddress = HexString.toHexString(onosDevice.getMacAddress().toBytes());
+ try {
+ IDeviceObject device = ope.searchDevice(macAddress);
+ if(device != null){
+ ope.removeDevice(device);
+ }
+ ope.commit();
+ }
+ catch (TitanException e){
+ log.error("deleteOnosDevice {} failed:", macAddress, e);
+ ope.rollback();
+ }
+ }
}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/devicemanager/IDeviceEventHandler.java b/src/main/java/net/onrc/onos/ofcontroller/devicemanager/IDeviceEventHandler.java
new file mode 100644
index 0000000..81bc09f
--- /dev/null
+++ b/src/main/java/net/onrc/onos/ofcontroller/devicemanager/IDeviceEventHandler.java
@@ -0,0 +1,7 @@
+package net.onrc.onos.ofcontroller.devicemanager;
+
+public interface IDeviceEventHandler {
+ public void addDeviceEvent(Long key, OnosDevice value);
+ public void deleteDeviceEvent(Long key, OnosDevice value);
+ public void updateDeviceEvent(Long key, OnosDevice value);
+}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/devicemanager/IOnosDeviceService.java b/src/main/java/net/onrc/onos/ofcontroller/devicemanager/IOnosDeviceService.java
index 1e40aa5..9125985 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/devicemanager/IOnosDeviceService.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/devicemanager/IOnosDeviceService.java
@@ -11,4 +11,8 @@
*/
public interface IOnosDeviceService extends IFloodlightService {
+ public void deleteOnosDevice(OnosDevice dev);
+
+ public void addOnosDevice(Long mac, OnosDevice dev);
+
}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/devicemanager/OnosDevice.java b/src/main/java/net/onrc/onos/ofcontroller/devicemanager/OnosDevice.java
index 34d2c38..4b47501 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/devicemanager/OnosDevice.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/devicemanager/OnosDevice.java
@@ -17,6 +17,7 @@
package net.onrc.onos.ofcontroller.devicemanager;
+import java.io.Serializable;
import java.util.Date;
import net.floodlightcontroller.devicemanager.internal.Entity;
@@ -37,7 +38,7 @@
* @author readams
*
*/
-public class OnosDevice { //implements Comparable<OnosDevice> {
+public class OnosDevice implements Serializable { //implements Comparable<OnosDevice> {
/**
* Timeout for computing {@link Entity#activeSince}.
* @see {@link Entity#activeSince}
@@ -126,6 +127,10 @@
public Integer getIpv4Address() {
return ipv4Address;
}
+
+ public void setIpv4Address(Integer ipv4Address) {
+ this.ipv4Address = ipv4Address;
+ }
public Short getVlan() {
return vlan;
@@ -134,10 +139,18 @@
public Long getSwitchDPID() {
return switchDPID;
}
+
+ public void setSwitchDPID(long dpid) {
+ this.switchDPID = dpid;
+ }
public short getSwitchPort() {
return switchPort;
}
+
+ public void setSwitchPort(short port) {
+ this.switchPort = port;
+ }
public Date getLastSeenTimestamp() {
return lastSeenTimestamp;
@@ -165,7 +178,6 @@
this.activeSince = activeSince;
}
- /*
@Override
public int hashCode() {
if (hashCode != 0) return hashCode;
@@ -173,10 +185,9 @@
hashCode = 1;
hashCode = prime * hashCode
+ ((ipv4Address == null) ? 0 : ipv4Address.hashCode());
- //hashCode = prime * hashCode + (int) (macAddress ^ (macAddress >>> 32));
- hashCode = prime * macAddress.hashCode();
- hashCode = prime * hashCode + (int) (switchDPID ^ (switchDPID >>> 32));
- hashCode = prime * hashCode + switchPort;
+ hashCode = prime * hashCode + (int) (macAddress.toLong() ^ (macAddress.toLong() >>> 32));
+ hashCode = prime * hashCode + (int)switchDPID;
+ hashCode = prime * hashCode + (int)switchPort;
hashCode = prime * hashCode + ((vlan == null) ? 0 : vlan.hashCode());
return hashCode;
}
@@ -186,26 +197,21 @@
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
- Entity other = (Entity) obj;
+ OnosDevice other = (OnosDevice) obj;
if (hashCode() != other.hashCode()) return false;
if (ipv4Address == null) {
if (other.ipv4Address != null) return false;
- } else if (!ipv4Address.equals(other.ipv4Address)) return false;
- if (macAddress != other.macAddress) return false;
- if (switchDPID == null) {
- if (other.switchDPID != null) return false;
- } else if (!switchDPID.equals(other.switchDPID)) return false;
- if (switchPort == null) {
- if (other.switchPort != null) return false;
- } else if (!switchPort.equals(other.switchPort)) return false;
+ } else if (!ipv4Address.equals(other.ipv4Address)) return false;
+ if (macAddress == null) {
+ if (other.macAddress != null) return false;
+ } else if (!macAddress.equals(other.macAddress)) return false;
+ if(switchDPID != other.switchDPID) return false;
+ if (switchPort != other.switchPort) return false;
if (vlan == null) {
if (other.vlan != null) return false;
} else if (!vlan.equals(other.vlan)) return false;
return true;
}
- */
-
-
@Override
public String toString() {
diff --git a/src/main/java/net/onrc/onos/ofcontroller/devicemanager/OnosDeviceManager.java b/src/main/java/net/onrc/onos/ofcontroller/devicemanager/OnosDeviceManager.java
index 1b14875..e9ddfa7 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/devicemanager/OnosDeviceManager.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/devicemanager/OnosDeviceManager.java
@@ -4,8 +4,14 @@
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
import net.floodlightcontroller.core.FloodlightContext;
import net.floodlightcontroller.core.IFloodlightProviderService;
@@ -22,29 +28,51 @@
import net.floodlightcontroller.packet.IPv4;
import net.floodlightcontroller.packet.UDP;
import net.floodlightcontroller.util.MACAddress;
+import net.onrc.onos.datagrid.IDatagridService;
import net.onrc.onos.ofcontroller.core.IDeviceStorage;
import net.onrc.onos.ofcontroller.core.internal.DeviceStorageImpl;
+import net.onrc.onos.ofcontroller.topology.TopologyElement;
+import net.onrc.onos.ofcontroller.topology.TopologyElement.Type;
import org.openflow.protocol.OFMessage;
import org.openflow.protocol.OFPacketIn;
import org.openflow.protocol.OFType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
public class OnosDeviceManager implements IFloodlightModule, IOFMessageListener,
IOnosDeviceService {
- private IDeviceStorage deviceStorage;
+ protected final static Logger log = LoggerFactory.getLogger(OnosDeviceManager.class);
+ private static final int CLEANUP_SECOND = 60*60;
+ private static final int AGEING_MILLSEC = 60*60*1000;
+ private IDeviceStorage deviceStorage;
private IFloodlightProviderService floodlightProvider;
-
+ private final static ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
+
+ private IDatagridService datagrid;
+ private Map<Long, OnosDevice> mapDevice = new ConcurrentHashMap<Long, OnosDevice>();
+
+ public enum OnosDeviceUpdateType {
+ ADD, DELETE, UPDATE;
+ }
+
private class OnosDeviceUpdate implements IUpdate {
private OnosDevice device;
+ private OnosDeviceUpdateType type;
- public OnosDeviceUpdate(OnosDevice device) {
+ public OnosDeviceUpdate(OnosDevice device, OnosDeviceUpdateType type) {
this.device = device;
+ this.type = type;
}
@Override
public void dispatch() {
- deviceStorage.addOnosDevice(device);
+ if(type == OnosDeviceUpdateType.ADD) {
+ deviceStorage.addOnosDevice(device);
+ } else if (type == OnosDeviceUpdateType.DELETE){
+ deviceStorage.deleteOnosDevice(device);
+ }
}
}
@@ -81,18 +109,98 @@
}
private Command processPacketIn(IOFSwitch sw, OFPacketIn pi, Ethernet eth) {
-
- // Extract source entity information
+ long dpid =sw.getId();
+ short portId = pi.getInPort();
+ Long mac = eth.getSourceMAC().toLong();
+
OnosDevice srcDevice =
- getSourceDeviceFromPacket(eth, sw.getId(), pi.getInPort());
- if (srcDevice == null)
- return Command.STOP;
+ getSourceDeviceFromPacket(eth, dpid, portId);
+
+ if (srcDevice == null){
+ return Command.STOP;
+ }
- floodlightProvider.publishUpdate(new OnosDeviceUpdate(srcDevice));
+ //We check if it is the same device in datagrid to suppress the device update
+ OnosDevice exDev = null;
+ if((exDev = mapDevice.get(mac)) != null ){
+ if(exDev.equals(srcDevice)) {
+ //There is the same existing device. Update only ActiveSince time.
+ exDev.setLastSeenTimestamp(new Date());
+ if(log.isTraceEnabled()) {
+ log.debug("In the datagrid, there is the same device."
+ + "Only update last seen time. dpid {}, port {}, mac {}, ip {}, lastSeenTime {}",
+ dpid, portId, srcDevice.getMacAddress(), srcDevice.getIpv4Address(), srcDevice.getLastSeenTimestamp().getTime());
+ }
+ return Command.CONTINUE;
+ } else if (srcDevice.getIpv4Address() == null &&
+ exDev.getSwitchDPID() == srcDevice.getSwitchDPID() &&
+ exDev.getSwitchPort() == srcDevice.getSwitchPort() &&
+ exDev.getVlan() == srcDevice.getVlan()) {
+ //Device attachment point and mac address are the same
+ //but the packet does not have an ip address.
+ exDev.setLastSeenTimestamp(new Date());
+ if(log.isTraceEnabled()) {
+ log.debug("In the datagrid, there is the same device with no ip."
+ + "Keep ip and update last seen time. dpid {}, port {}, mac {}, ip {}, lastSeenTime {}",
+ dpid, portId, srcDevice.getMacAddress(), srcDevice.getIpv4Address(), srcDevice.getLastSeenTimestamp().getTime());
+ }
+ return Command.CONTINUE;
+ }
+ }
+ //If the switch port we try to attach a new device already has a link, then stop adding device
+ Collection<TopologyElement> list = datagrid.getAllTopologyElements();
+ for(TopologyElement elem: list) {
+ if(elem.getType() == Type.ELEMENT_LINK) {
+ if((elem.getFromPort() == portId && elem.getFromSwitch() == dpid) ||
+ (elem.getToPort() == portId && elem.getToSwitch() == dpid)) {
+ if(log.isTraceEnabled()) {
+ log.debug("Stop adding OnosDevice {} due to there is a link to: dpid {} port {}",
+ srcDevice.getMacAddress(), dpid, portId);
+ }
+ return Command.CONTINUE;
+ }
+ }
+ }
+
+ addOnosDevice(mac, srcDevice);
+
+ if(log.isTraceEnabled()) {
+ log.debug("Add device info in the set. dpid {}, port {}, mac {}, ip {}, lastSeenTime {}",
+ dpid, portId, srcDevice.getMacAddress(), srcDevice.getIpv4Address(), srcDevice.getLastSeenTimestamp().getTime());
+ }
return Command.CONTINUE;
}
+ //Thread to delete devices periodically.
+ //Remove all devices from the map first and then finally delete devices from the DB.
+ private class CleanDevice implements Runnable {
+ @Override
+ public void run() {
+ log.debug("called CleanDevice");
+ try{
+ Set<OnosDevice> deleteSet = new HashSet<OnosDevice>();
+ for (OnosDevice dev : mapDevice.values() ) {
+ long now = new Date().getTime();
+ if((now - dev.getLastSeenTimestamp().getTime() > AGEING_MILLSEC)) {
+ if(log.isTraceEnabled()) {
+ log.debug("Remove device info in the datagrid. dpid {}, port {}, mac {}, ip {}, lastSeenTime {}, diff {}",
+ dev.getSwitchDPID(), dev.getSwitchPort(), dev.getMacAddress(), dev.getIpv4Address(),
+ dev.getLastSeenTimestamp().getTime(), now - dev.getLastSeenTimestamp().getTime());
+ }
+ deleteSet.add(dev);
+ }
+ }
+
+ for(OnosDevice dev : deleteSet) {
+ deleteOnosDevice(dev);
+ }
+ } catch(Exception e) {
+ log.error("Error:", e);
+ }
+ }
+ }
+
/**
* Get IP address from packet if the packet is either an ARP
* or a DHCP packet
@@ -177,14 +285,50 @@
public void init(FloodlightModuleContext context)
throws FloodlightModuleException {
floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
-
+ executor.scheduleAtFixedRate(new CleanDevice(), 30 ,CLEANUP_SECOND, TimeUnit.SECONDS);
deviceStorage = new DeviceStorageImpl();
deviceStorage.init("","");
+
+ datagrid = context.getServiceImpl(IDatagridService.class);
}
@Override
public void startUp(FloodlightModuleContext context) {
floodlightProvider.addOFMessageListener(OFType.PACKET_IN, this);
+ datagrid.registerMapDeviceEventHandler(new MapDevListener());
}
+ @Override
+ public void deleteOnosDevice(OnosDevice dev) {
+ datagrid.sendNotificationDeviceDeleted(dev);
+ floodlightProvider.publishUpdate(new OnosDeviceUpdate(dev, OnosDeviceUpdateType.DELETE));
+ }
+
+ @Override
+ public void addOnosDevice(Long mac, OnosDevice dev) {
+ datagrid.sendNotificationDeviceAdded(mac, dev);
+ floodlightProvider.publishUpdate(new OnosDeviceUpdate(dev, OnosDeviceUpdateType.ADD));
+ }
+
+ //This is listener for datagrid mapDevice change.
+ class MapDevListener implements IDeviceEventHandler {
+
+ @Override
+ public void addDeviceEvent(Long mac, OnosDevice dev) {
+ mapDevice.put(mac, dev);
+ log.debug("addDeviceMap: device mac {}", mac);
+ }
+
+ @Override
+ public void deleteDeviceEvent(Long mac, OnosDevice dev) {
+ mapDevice.remove(mac);
+ log.debug("deleteDeviceMap: device mac {}", mac);
+ }
+
+ @Override
+ public void updateDeviceEvent(Long mac, OnosDevice dev) {
+ mapDevice.put(mac, dev);
+ log.debug("updateDeviceMap: device mac {}", mac);
+ }
+ }
}