-
Srinivasan Ramasubramanian authored
Add 5 seconds for attachment point change from one broadcast domain to another. When the attachment points flap quickly, the controller should avoid learing the attachment point but should continue processing the packet.
Srinivasan Ramasubramanian authoredAdd 5 seconds for attachment point change from one broadcast domain to another. When the attachment points flap quickly, the controller should avoid learing the attachment point but should continue processing the packet.
DeviceManagerImpl.java 93.23 KiB
/**
* Copyright 2011, Big Switch Networks, Inc.
* Originally created by David Erickson, Stanford University
*
* 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 net.floodlightcontroller.devicemanager.internal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.ConcurrentModificationException;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Collection;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import net.floodlightcontroller.core.FloodlightContext;
import net.floodlightcontroller.core.IFloodlightProviderService;
import net.floodlightcontroller.core.IInfoProvider;
import net.floodlightcontroller.core.IOFMessageListener;
import net.floodlightcontroller.core.IOFSwitch;
import net.floodlightcontroller.core.IOFSwitchListener;
import net.floodlightcontroller.core.module.FloodlightModuleContext;
import net.floodlightcontroller.core.module.FloodlightModuleException;
import net.floodlightcontroller.core.module.IFloodlightModule;
import net.floodlightcontroller.core.module.IFloodlightService;
import net.floodlightcontroller.core.util.SingletonTask;
import net.floodlightcontroller.devicemanager.Device;
import net.floodlightcontroller.devicemanager.DeviceAttachmentPoint;
import net.floodlightcontroller.devicemanager.DeviceNetworkAddress;
import net.floodlightcontroller.devicemanager.IDeviceManagerService;
import net.floodlightcontroller.devicemanager.IDeviceManagerAware;
import net.floodlightcontroller.linkdiscovery.ILinkDiscovery;
import net.floodlightcontroller.linkdiscovery.ILinkDiscoveryListener;
import net.floodlightcontroller.linkdiscovery.ILinkDiscoveryService;
import net.floodlightcontroller.linkdiscovery.SwitchPortTuple;
import net.floodlightcontroller.packet.ARP;
import net.floodlightcontroller.packet.Ethernet;
import net.floodlightcontroller.packet.IPv4;
import net.floodlightcontroller.restserver.IRestApiService;
import net.floodlightcontroller.routing.ForwardingBase;
import net.floodlightcontroller.storage.IResultSet;
import net.floodlightcontroller.storage.IStorageSourceListener;
import net.floodlightcontroller.storage.IStorageSourceService;
import net.floodlightcontroller.storage.OperatorPredicate;
import net.floodlightcontroller.storage.StorageException;
import net.floodlightcontroller.threadpool.IThreadPoolService;
import net.floodlightcontroller.topology.ITopologyListener;
import net.floodlightcontroller.topology.ITopologyService;
import net.floodlightcontroller.util.EventHistory;
import net.floodlightcontroller.util.EventHistory.EvAction;
import org.openflow.protocol.OFMatch;
import org.openflow.protocol.OFMessage;
import org.openflow.protocol.OFPacketIn;
import org.openflow.protocol.OFPhysicalPort.OFPortConfig;
import org.openflow.protocol.OFPhysicalPort.OFPortState;
import org.openflow.protocol.OFPortStatus;
import org.openflow.protocol.OFPortStatus.OFPortReason;
import org.openflow.protocol.OFType;
import org.openflow.util.HexString;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* DeviceManager creates Devices based upon MAC addresses seen in the network.
* It tracks any network addresses mapped to the Device, and its location
* within the network.
*
* @author David Erickson (daviderickson@cs.stanford.edu)
*/
public class DeviceManagerImpl implements IDeviceManagerService, IOFMessageListener,
IOFSwitchListener, ILinkDiscoveryListener, IFloodlightModule, IStorageSourceListener,
ITopologyListener, IInfoProvider {
/**
* Class to maintain all the device manager maps which consists of four
* main maps.
* (a) data layer address (mac) to device object
* (b) ipv4 address to device object
* (c) Switch object to device objects
* (d) Switch port tuple object to device objects
*
* Also maintained is a pending attachment point object which is a
* mapping from switch dpid to pending attachment point object
* @author subrata
*/
public class DevMgrMaps {
private Map<Long, Device> dataLayerAddressDeviceMap;
private Map<Integer, Device> ipv4AddressDeviceMap;
// The Integer below if the hashCode iof the device
private Map<IOFSwitch, Map<Integer, Device>> switchDeviceMap;
// The Integer below is the hashCode of the device
private Map<SwitchPortTuple, Map<Integer, Device>> switchPortDeviceMap;
private Map<Long, List<PendingAttachmentPoint>> switchUnresolvedAPMap;
private Map<String, String> portChannelMap;
public Map<Long, Device> getDataLayerAddressDeviceMap() {
return dataLayerAddressDeviceMap;
}
public Map<Integer, Device> getIpv4AddressDeviceMap() {
return ipv4AddressDeviceMap;
}
public Map<IOFSwitch, Map<Integer, Device>> getSwitchDeviceMap() {
return switchDeviceMap;
}
public Map<SwitchPortTuple, Map<Integer, Device>>
getSwitchPortDeviceMap() {
return switchPortDeviceMap;
}
public Map<Long, List<PendingAttachmentPoint>>
getSwitchUnresolvedAPMap() {
return switchUnresolvedAPMap;
}
// Constructor for the device maps class
public DevMgrMaps() {
dataLayerAddressDeviceMap = new ConcurrentHashMap<Long, Device>();
ipv4AddressDeviceMap = new ConcurrentHashMap<Integer, Device>();
switchDeviceMap =
new ConcurrentHashMap<IOFSwitch, Map<Integer, Device>>();
switchPortDeviceMap =
new ConcurrentHashMap<SwitchPortTuple, Map<Integer, Device>>();
switchUnresolvedAPMap =
new ConcurrentHashMap<Long, List<PendingAttachmentPoint>>();
portChannelMap =
new ConcurrentHashMap<String, String>();
}
// ***********
// Get methods
// ***********
protected Collection<Device> getDevicesOnASwitch(IOFSwitch sw) {
return switchDeviceMap.get(sw).values();
}
protected int getSwitchCount() {
return switchDeviceMap.size();
}
protected Set<IOFSwitch> getSwitches() {
return switchDeviceMap.keySet();
}
protected Set<SwitchPortTuple> getSwitchPorts() {
return switchPortDeviceMap.keySet();
}
protected Collection<Device> getDevices() {
lock.readLock().lock(); // Do we need the lock TODO
try {
return dataLayerAddressDeviceMap.values();
} finally {
lock.readLock().unlock();
}
}
// *****************************************************
// Individual update and delete methods for given device
// *****************************************************
private void updateDataLayerAddressDeviceMap(Device d) {
dataLayerAddressDeviceMap.put(d.getDataLayerAddressAsLong(), d);
}
private void delFromDataLayerAddressDeviceMap(Device d) {
dataLayerAddressDeviceMap.remove(d.getDataLayerAddressAsLong());
}
private void updateIpv4AddressDeviceMap(Device d) {
Collection< DeviceNetworkAddress> dNAColl = d.getNetworkAddresses();
for (DeviceNetworkAddress dna : dNAColl) {
ipv4AddressDeviceMap.put(dna.getNetworkAddress(), d);
}
}
private void delFromIpv4AddressDeviceMap(Device d) {
Collection< DeviceNetworkAddress> dNAColl = d.getNetworkAddresses();
for (DeviceNetworkAddress dna : dNAColl) {
ipv4AddressDeviceMap.remove(dna.getNetworkAddress());
}
}
private void delFromIpv4AddressDeviceMap(Integer ip, Device d) {
ipv4AddressDeviceMap.remove(ip);
}
private void updateSwitchDeviceMap(Device d) {
// Find all the attachment points of this device
// Then find all the switches from the attachment points
// Then update the device in maps for all those switches
Collection<DeviceAttachmentPoint> dapColl = d.getAttachmentPoints();
for (DeviceAttachmentPoint dap : dapColl) {
SwitchPortTuple swPrt = dap.getSwitchPort();
IOFSwitch sw = swPrt.getSw();
Map<Integer, Device> oneSwDevMap = switchDeviceMap.get(sw);
if (oneSwDevMap == null) {
oneSwDevMap = new ConcurrentHashMap<Integer, Device>();
switchDeviceMap.put(sw, oneSwDevMap);
}
oneSwDevMap.put(d.hashCode(), d);
}
}
private void delFromSwitchDeviceMap(Device d) {
// Find all the attachment points of this device
// Then find all the switches from the attachment points
// Then update the device in maps for all those switches
Collection<DeviceAttachmentPoint> dapColl = d.getAttachmentPoints();
for (DeviceAttachmentPoint dap : dapColl) {
SwitchPortTuple swPrt = dap.getSwitchPort();
IOFSwitch sw = swPrt.getSw();
Map<Integer, Device> oneSwDevMap = switchDeviceMap.get(sw);
if (oneSwDevMap != null) {
oneSwDevMap.remove(d.hashCode());
}
}
}
private void delFromSwitchDeviceMap(IOFSwitch sw, Device d) {
Map<Integer, Device> oneSwDevMap = switchDeviceMap.get(sw);
if (oneSwDevMap != null) {
oneSwDevMap.remove(d.hashCode());
}
}
protected void delSwitchfromMaps(IOFSwitch sw) {
// Remove all switch:port mappings of the given switch sw
Set<SwitchPortTuple> swPortSet = devMgrMaps.getSwitchPorts();
for (SwitchPortTuple swPort : swPortSet) {
if (swPort.getSw().equals(sw)) {
devMgrMaps.removeSwPort(swPort);
}
}
// Not removing the switch from the SwitchDevice map, let it age out
}
private void updateSwitchPortDeviceMap(Device d) {
// Find all the attachment points of this device
// Then update the device in maps for all those attachment points
Collection<DeviceAttachmentPoint> dapColl = d.getAttachmentPoints();
for (DeviceAttachmentPoint dap : dapColl) {
SwitchPortTuple swPrt = dap.getSwitchPort();
Map<Integer, Device> oneSwPrtMap =
switchPortDeviceMap.get(swPrt);
if (oneSwPrtMap == null) {
oneSwPrtMap = new ConcurrentHashMap<Integer, Device>();
switchPortDeviceMap.put(swPrt, oneSwPrtMap);
}
oneSwPrtMap.put(d.hashCode(), d);
}
}
private void delFromSwitchPortDeviceMap(Device d) {
// Find all the attachment points of this device
// Then update the device in maps for all those attachment points
Collection<DeviceAttachmentPoint> dapColl = d.getAttachmentPoints();
for (DeviceAttachmentPoint dap : dapColl) {
SwitchPortTuple swPrt = dap.getSwitchPort();
Map<Integer, Device> oneSwPrtMap =
switchPortDeviceMap.get(swPrt);
if (oneSwPrtMap != null) {
oneSwPrtMap.remove(d.hashCode());
}
}
}
private void delFromSwitchPortDeviceMap(
SwitchPortTuple swPort, Device d) {
Map<Integer, Device> oneSwPrtMap = switchPortDeviceMap.get(swPort);
if (oneSwPrtMap != null) {
oneSwPrtMap.remove(d.hashCode());
}
}
/**
* Overall update, delete and clear methods for the set of maps
* @param d the device to update
*/
private synchronized void updateMaps(Device d) {
// Update dataLayerAddressDeviceMap
updateDataLayerAddressDeviceMap(d);
// Update ipv4AddressDeviceMap
updateIpv4AddressDeviceMap(d);
// update switchDeviceMap
updateSwitchDeviceMap(d);
// Update switchPortDeviceMap
updateSwitchPortDeviceMap(d);
}
/**
* Delete a device object from the device maps
* @param d the device to remove
*/
private void delFromMaps(Device d) {
// Update dataLayerAddressDeviceMap
delFromDataLayerAddressDeviceMap(d);
// Update ipv4AddressDeviceMap
delFromIpv4AddressDeviceMap(d);
// update switchDeviceMap
delFromSwitchDeviceMap(d);
// Update switchPortDeviceMap
delFromSwitchPortDeviceMap(d);
}
/**
* Delete a device by a data layer address
* @param dlAddr the address
*/
protected void delFromMaps(long dlAddr) {
Device d = devMgrMaps.getDeviceByDataLayerAddr(dlAddr);
if (d != null) {
delFromMaps(d);
}
}
/**
* Clear all the device maps
*/
private void clearMaps() {
dataLayerAddressDeviceMap.clear();
ipv4AddressDeviceMap.clear();
switchUnresolvedAPMap.clear();
switchDeviceMap.clear();
switchPortDeviceMap.clear();
}
/**
* Remove switch-port and also remove this switch-port as the
* attachment point in the device objects if any
* @param swPrt The {@link SwitchPortTuple} to remove
*/
protected void removeSwPort(SwitchPortTuple swPrt) {
Map<Integer, Device> switchPortDevices =
switchPortDeviceMap.remove(swPrt);
if (switchPortDevices == null) {
return;
}
// Update the individual devices by updating its attachment points
for (Device d : switchPortDevices.values()) {
// Remove the device from the switch->device mapping
delDevAttachmentPoint(d.getDataLayerAddressAsLong(), swPrt);
evHistAttachmtPt(d.getDataLayerAddressAsLong(), swPrt, EvAction.REMOVED,
"SwitchPort removed");
}
}
/**
* Retrieve the device by it's data layer address
* @param mac the mac address to look up
* @return the device object
*/
protected Device getDeviceByDataLayerAddr(long mac) {
return dataLayerAddressDeviceMap.get(mac);
}
/**
* Check whether the given switch exists in the switch maps
* @param sw the switch to check
* @return true if the switch exists
*/
protected boolean isSwitchPresent (IOFSwitch sw) {
return switchDeviceMap.containsKey(sw);
}
/**
* Reinitialize portChannelMap upon config change
*/
protected void clearPortChannelMap() {
portChannelMap.clear();
}
// ***************************************
// Add operations on the device attributes
// ***************************************
/**
* Add a network layer address to the device with the given link
* layer address
* @param dlAddr The link layer address of the device
* @param nwAddr the new network layer address
* @param lastSeen the new last seen timestamp for the network address
*/
protected void addNwAddrByDataLayerAddr(long dlAddr,
int nwAddr,
Date lastSeen) {
Device d = getDeviceByDataLayerAddr(dlAddr);
if (d == null) return;
Device dCopy = new Device(d);
DeviceNetworkAddress na = dCopy.getNetworkAddress(nwAddr);
if (na == null) { // this particular address is not in the device
na = new DeviceNetworkAddress(nwAddr, lastSeen);
dCopy.addNetworkAddress(na);
updateMaps(dCopy);
}
}
/**
* Delete a network address from a device
* @param dlAddr The link layer address of the device
* @param nwAddr the new network layer address
*/
protected void delNwAddrByDataLayerAddr(long dlAddr,
int nwAddr) {
Device d = getDeviceByDataLayerAddr(dlAddr);
if (d == null) return;
Device dCopy = new Device(d);
DeviceNetworkAddress na = dCopy.getNetworkAddress(nwAddr);
if (na != null) {
delFromIpv4AddressDeviceMap(nwAddr, d);
dCopy.removeNetworkAddress(na);
updateMaps(dCopy);
removeNetworkAddressFromStorage(d, na);
}
}
/**
* Add a device attachment point to the device with the given
* link-layer address
* @param mac the MAC address
* @param swPort the {@link SwitchPortTuple} to add
* @param lastSeen the new last seen timestamp
*/
protected void addDevAttachmentPoint(long mac,
SwitchPortTuple swPort,
Date lastSeen) {
addDevAttachmentPoint(mac, swPort.getSw(),
swPort.getPort(), lastSeen);
}
/**
* Add a device attachment point to the device with the given
* link-layer address
* @param mac the MAC address
* @param sw The {@link IOFSwitch} for the new attachment point
* @param port The port for the new attachment point
* @param lastSeen the new last seen timestamp
*/
protected boolean addDevAttachmentPoint(long mac,
IOFSwitch sw,
short port,
Date lastSeen) {
Device d = getDeviceByDataLayerAddr(mac);
// Check if the attachment point is already there
SwitchPortTuple swPort = new SwitchPortTuple(sw, port);
DeviceAttachmentPoint dap = d.getAttachmentPoint(swPort);
if (dap != null) {
// The attachment point is already there
dap.setLastSeen(lastSeen); // TODO
return false;
}
// new device attachment point for this device
dap = new DeviceAttachmentPoint(swPort, lastSeen);
Device dCopy = new Device(d);
dCopy.addAttachmentPoint(dap);
// Now add this updated device to the maps, which will replace
// the old copy
updateMaps(dCopy);
return true;
}
// ******************************************
// Delete operations on the device attributes
// ******************************************
/**
* Delete an attachment point from a device
* @param d the device
* @param swPort the {@link SwitchPortTuple} to remove
*/
protected void delDevAttachmentPoint(long dlAddr, SwitchPortTuple swPort) {
delDevAttachmentPoint(devMgrMaps.getDeviceByDataLayerAddr(dlAddr),
swPort.getSw(), swPort.getPort());
}
/**
* Delete an attachment point from a device
* @param d the device
* @param sw the {@link IOFSwitch} for the attachment point to remove
* @param port the port for the attachment point to remove
*/
protected boolean delDevAttachmentPoint(Device d,
IOFSwitch sw,
short port) {
// Check if the attachment point is there
SwitchPortTuple swPort = new SwitchPortTuple(sw, port);
if (d.getAttachmentPoint(swPort) == null) {
// The attachment point is NOT there
return false;
}
// Make a copy of this device
Device dCopy = new Device(d);
DeviceAttachmentPoint dap = dCopy.removeAttachmentPoint(swPort);
// Remove the original device from the Switch-port map
// This is a no-op when this fn is called from removeSwPort
// as the switch-pot itself would be deleted from the map
delFromSwitchPortDeviceMap(swPort, d);
// Remove the original device from the Switch map
delFromSwitchDeviceMap(sw, d);
// Now add this updated device to the maps, which will replace
// the old copy
updateMaps(dCopy);
if (log.isDebugEnabled()) {
log.debug("Remove AP {} post {} prev {} for Device {}",
new Object[] {dap, dCopy.getAttachmentPoints().size(),
d.getAttachmentPoints().size(), dCopy});
}
removeAttachmentPointFromStorage(d, dap);
d = null;
return true;
}
/**
* Add a new pending attachment point to the unresolved attachment
* point map
* @param dpid the DPID of the switch for the attachment point
* @param mac the MAC address of the device
* @param port the port for the attachment point
* @param lastSeen the last seen timestamp
*/
protected void updateSwitchUnresolvedAPMap(long dpid,
long mac,
short port,
Date lastSeen) {
PendingAttachmentPoint pap = new PendingAttachmentPoint();
pap.mac = mac;
pap.switchDpid = dpid;
pap.switchPort = port;
pap.lastSeen = lastSeen;
List<PendingAttachmentPoint> papl =
devMgrMaps.switchUnresolvedAPMap.get(dpid);
if (papl == null) {
devMgrMaps.switchUnresolvedAPMap.put(dpid,
papl = Collections.synchronizedList(
new ArrayList<PendingAttachmentPoint>()));
}
papl.add(pap);
}
/**
* Add switch-port to port_channel mapping to portChannelMap
* @param switch_id
* @param port_no
* @param port_channel
*/
protected void addPortToPortChannel(String switch_id,
String port_name, String port_channel) {
String swPort = switch_id + port_name;
portChannelMap.put(swPort, port_channel);
}
/**
* Check if two ports belong to the same port channel
* @param swPort1
* @param swPort2
* @return
*/
protected boolean inSamePortChannel(SwitchPortTuple swPort1,
SwitchPortTuple swPort2) {
IOFSwitch sw = swPort1.getSw();
String portName = sw.getPort(swPort1.getPort()).getName();
String key = sw.getStringId() + portName;
String portChannel1 = portChannelMap.get(key);
if (portChannel1 == null)
return false;
sw = swPort2.getSw();
portName = sw.getPort(swPort2.getPort()).getName();
key = sw.getStringId() + portName;
String portChannel2 = portChannelMap.get(key);
if (portChannel2 == null)
return false;
return portChannel1.equals(portChannel2);
}
} // End of DevMgrMap class definition
private DevMgrMaps devMgrMaps;
public DevMgrMaps getDevMgrMaps() {
return devMgrMaps;
}
protected static Logger log =
LoggerFactory.getLogger(DeviceManagerImpl.class);
protected Set<IDeviceManagerAware> deviceManagerAware;
protected LinkedList<Update> updates;
protected ReentrantReadWriteLock lock;
protected volatile boolean shuttingDown = false;
protected volatile boolean portChannelConfigChanged = false;
// Our dependencies
protected IFloodlightProviderService floodlightProvider;
protected ILinkDiscoveryService linkDiscovery;
protected ITopologyService topology;
protected IStorageSourceService storageSource;
protected IThreadPoolService threadPool;
protected IRestApiService restApi;
protected Runnable deviceAgingTimer;
protected SingletonTask deviceUpdateTask;
protected Date previousStorageAudit;
protected static int DEVICE_MAX_AGE = 60 * 60 * 24;
protected static int DEVICE_NA_MAX_AGE = 60 * 60 * 2;
protected static int DEVICE_AP_MAX_AGE = 60 * 60 * 2;
protected static long NBD_TO_BD_TIMEDIFF_MS = 300000; // 5 minutes
protected static long BD_TO_BD_TIMEDIFF_MS = 5000; // 0 seconds
// This the amount of time that we need for a device to move from
// a non-broadcast domain port to a broadcast domain port.
// Constants for accessing storage
// Table names
private static final String DEVICE_TABLE_NAME = "controller_host";
private static final String DEVICE_ATTACHMENT_POINT_TABLE_NAME =
"controller_hostattachmentpoint";
private static final String DEVICE_NETWORK_ADDRESS_TABLE_NAME =
"controller_hostnetworkaddress";
protected static final String PORT_CHANNEL_TABLE_NAME = "controller_portchannelconfig";
// Column names for the host table
private static final String MAC_COLUMN_NAME = "mac";
private static final String VLAN_COLUMN_NAME = "vlan";
// Column names for both the attachment point and network address tables
private static final String ID_COLUMN_NAME = "id";
private static final String DEVICE_COLUMN_NAME = "host_id";
private static final String LAST_SEEN_COLUMN_NAME = "last_seen";
// Column names for the attachment point table
private static final String SWITCH_COLUMN_NAME = "switch_id";
private static final String PORT_COLUMN_NAME = "inport";
private static final String AP_STATUS_COLUMN_NAME = "status";
// Column names for the network address table
private static final String NETWORK_ADDRESS_COLUMN_NAME = "ip";
// Column names for the port channel table
protected static final String PC_ID_COLUMN_NAME = "id";
protected static final String PORT_CHANNEL_COLUMN_NAME = "port_channel_id";
protected static final String PC_SWITCH_COLUMN_NAME = "switch";
protected static final String PC_PORT_COLUMN_NAME = "port";
protected enum UpdateType {
ADDED, REMOVED, MOVED, ADDRESS_ADDED, ADDRESS_REMOVED, VLAN_CHANGED
}
/**
* Used internally to feed update queue for IDeviceManagerAware listeners
*/
protected class Update {
public Device device;
public IOFSwitch oldSw;
public Short oldSwPort;
public IOFSwitch sw;
public Short swPort;
public DeviceNetworkAddress address;
public UpdateType updateType;
public Update(UpdateType type) {
this.updateType = type;
}
}
protected class PendingAttachmentPoint {
long mac;
long switchDpid;
short switchPort;
Date lastSeen;
public long getMac() {
return mac;
}
public long getSwitchDpid() {
return switchDpid;
}
public short getSwitchPort() {
return switchPort;
}
public Date getLastSeen() {
return lastSeen;
}
}
public void shutDown() {
shuttingDown = true;
floodlightProvider.removeOFMessageListener(OFType.PACKET_IN, this);
floodlightProvider.removeOFMessageListener(OFType.PORT_STATUS, this);
floodlightProvider.removeOFSwitchListener(this);
deviceAgingTimer = null;
}
@Override
public String getName() {
return "devicemanager";
}
/**
* Used in processPortStatus to check if the event is delete or shutdown
* of a switch-port
* @param ps
* @return
*/
private boolean isPortStatusDelOrModify(OFPortStatus ps) {
boolean isDelete =
((byte)OFPortReason.OFPPR_DELETE.ordinal() == ps.getReason());
boolean isModify =
((byte)OFPortReason.OFPPR_MODIFY.ordinal() == ps.getReason());
boolean isNotActive =
(((OFPortConfig.OFPPC_PORT_DOWN.getValue() &
ps.getDesc().getConfig()) > 0) ||
((OFPortState.OFPPS_LINK_DOWN.getValue() &
ps.getDesc().getState()) > 0));
return (isDelete || (isModify && isNotActive));
}
public Command processPortStatusMessage(IOFSwitch sw, OFPortStatus ps) {
// if ps is a delete, or a modify where the port is down or
// configured down
if (isPortStatusDelOrModify(ps)) {
SwitchPortTuple id = new SwitchPortTuple(sw,
ps.getDesc().getPortNumber());
lock.writeLock().lock();
try {
devMgrMaps.removeSwPort(id);
} finally {
lock.writeLock().unlock();
}
}
return Command.CONTINUE;
}
private void handleNewDevice(byte[] mac, Date currentDate,
SwitchPortTuple swPort, int nwAddr, Short vlan) {
// Create the new device with attachment point and network address
Device device = new Device(mac, currentDate);
device.addAttachmentPoint(swPort, currentDate);
evHistAttachmtPt(mac, swPort, EvAction.ADDED, "New device");
device.addNetworkAddress(nwAddr, currentDate);
device.setVlanId(vlan);
// Update the maps - insert the device in the maps
devMgrMaps.updateMaps(device);
writeDeviceToStorage(device, currentDate);
updateStatus(device, true);
if (log.isDebugEnabled()) {
log.debug("New device learned {}", device);
}
}
private boolean isGratArp(Ethernet eth) {
if (eth.getPayload() instanceof ARP) {
ARP arp = (ARP) eth.getPayload();
if (arp.isGratuitous()) {
return true;
}
}
return false;
}
private int getSrcNwAddr(Ethernet eth, long dlAddr) {
if (eth.getPayload() instanceof ARP) {
ARP arp = (ARP) eth.getPayload();
if ((arp.getProtocolType() == ARP.PROTO_TYPE_IP)
&& (Ethernet.toLong(arp.getSenderHardwareAddress()) ==dlAddr)) {
return IPv4.toIPv4Address(arp.getSenderProtocolAddress());
}
} else if (eth.getPayload() instanceof IPv4) {
IPv4 ipv4 = (IPv4) eth.getPayload();
return ipv4.getSourceAddress();
}
return 0;
}
/**
* This method is called for every packet-in and should be optimized for
* performance.
* @param sw
* @param pi
* @param cntx
* @return
*/
public Command processPacketInMessage(IOFSwitch sw, OFPacketIn pi,
FloodlightContext cntx) {
Ethernet eth = IFloodlightProviderService.bcStore.get(
cntx, IFloodlightProviderService.CONTEXT_PI_PAYLOAD);
// If the packet in comes from a port that's not allowed by higher
// level topology, it should be dropped. An L2 bridge can result
// in this situation.
short pinPort = pi.getInPort();
long pinSw = sw.getId();
if (topology.isAllowed(pinSw, pinPort) == false) {
if (eth.getEtherType() == Ethernet.TYPE_BDDP ||
eth.isMulticast() == false) {
return Command.CONTINUE;
} else {
if (log.isDebugEnabled()) {
log.debug("deviceManager: Stopping packet as it is coming" +
"in on a port blocked by higher layer on." +
"switch ={}, port={}", new Object[] {sw.getStringId(), pinPort});
}
return Command.STOP;
}
}
Command ret = Command.CONTINUE;
OFMatch match = new OFMatch();
match.loadFromPacket(pi.getPacketData(), pi.getInPort(), sw.getId());
// Add this packet-in to event history
evHistPktIn(match);
if (log.isTraceEnabled())
log.trace("Entering packet_in processing sw {}, port {}. {} --> {}, type {}",
new Object[] { sw.getStringId(), pi.getInPort(),
HexString.toHexString(match.getDataLayerSource()),
HexString.toHexString(match.getDataLayerDestination()),
match.getDataLayerType()
});
// Create attachment point/update network address if required
SwitchPortTuple switchPort = new SwitchPortTuple(sw, pi.getInPort());
// Don't learn from internal port or invalid port
if (topology.isInternal(switchPort.getSw().getId(), switchPort.getPort()) ||
!isValidInputPort(switchPort.getPort())) {
processUpdates();
return Command.CONTINUE;
}
// Packet arrive at a port from where we can learn the device
// if the source is multicast/broadcast ignore it
if ((match.getDataLayerSource()[0] & 0x1) != 0) {
return Command.CONTINUE;
}
Long dlAddr = Ethernet.toLong(match.getDataLayerSource());
Short vlan = match.getDataLayerVirtualLan();
if (vlan < 0) vlan = null;
int nwSrc = getSrcNwAddr(eth, dlAddr);
Device device = devMgrMaps.getDeviceByDataLayerAddr(dlAddr);
if (log.isTraceEnabled()) {
long dstAddr = Ethernet.toLong(match.getDataLayerDestination());
Device dstDev = devMgrMaps.getDeviceByDataLayerAddr(dstAddr);
if (device != null)
log.trace(" Src.AttachmentPts: {}", device.getAttachmentPointsMap().keySet());
if (dstDev != null)
log.trace(" Dst.AttachmentPts: {}", dstDev.getAttachmentPointsMap().keySet());
}
Date currentDate = new Date();
if (device != null) {
// Write lock is expensive, check if we have an update first
boolean newAttachmentPoint = false;
boolean newNetworkAddress = false;
boolean updateAttachmentPointLastSeen = false;
boolean updateNetworkAddressLastSeen = false;
boolean updateNeworkAddressMap = false;
boolean updateDeviceVlan = false;
boolean clearAttachmentPoints = false;
boolean updateDevice = false;
DeviceAttachmentPoint attachmentPoint = null;
DeviceNetworkAddress networkAddress = null;
// Copy-replace of device would be too expensive here
device.setLastSeen(currentDate);
updateDevice = device.shouldWriteLastSeenToStorage();
// If the attachment point moves from a non-broadcast domain
// port to a broadcast domain port too quickly, ignore learning
// the broadcast domain attachment point.
for ( DeviceAttachmentPoint oldDap : device.getAttachmentPoints() ) {
// if the two switches are in the same cluster
// and if currSw,CurrPort is not in broadcast domain
long currSw = oldDap.getSwitchPort().getSw().getId();
short currPort = oldDap.getSwitchPort().getPort();
long newSw = switchPort.getSw().getId();
short newPort = switchPort.getPort();
if ( (topology.getSwitchClusterId(currSw) == topology.getSwitchClusterId(newSw)) &&
(topology.isBroadcastDomainPort(currSw, currPort) == false) &&
(topology.isBroadcastDomainPort(newSw, newPort) == true)) {
long dt = currentDate.getTime() -
oldDap.getLastSeen().getTime() ;
if (dt < NBD_TO_BD_TIMEDIFF_MS) {
// if the packet was seen within the last 5 minutes, we should ignore.
// it should also ignore processing the packet.
if (log.isTraceEnabled()) {
log.trace("Surpressing too quick move of {} from non broadcast domain port {} {}" +
" to broadcast domain port {} {}. Last seen on non-BD {} sec ago",
new Object[] { HexString.toHexString(match.getDataLayerSource()),
oldDap.getSwitchPort().getSw().getStringId(), currPort,
switchPort.getSw().getStringId(), newPort,
dt/1000 }
);
}
return Command.CONTINUE;
}
} else if ( (topology.getSwitchClusterId(currSw) == topology.getSwitchClusterId(newSw)) &&
(topology.isBroadcastDomainPort(currSw, currPort) == true) &&
(topology.isBroadcastDomainPort(newSw, newPort) == true)) {
long dt = currentDate.getTime() -
oldDap.getLastSeen().getTime() ;
if (dt < BD_TO_BD_TIMEDIFF_MS) {
// if the packet was seen within the last 5 seconds, we should ignore.
// it should also ignore processing the packet.
if (log.isTraceEnabled()) {
log.trace("Surpressing too quick move of {} from one broadcast domain port {} {}" +
" to another broadcast domain port {} {}. Last seen on BD {} sec ago",
new Object[] { HexString.toHexString(match.getDataLayerSource()),
oldDap.getSwitchPort().getSw().getStringId(), currPort,
switchPort.getSw().getStringId(), newPort,
dt/1000 }
);
}
return Command.CONTINUE;
}
}
}
if (isGratArp(eth)) {
clearAttachmentPoints = true;
}
attachmentPoint = device.getAttachmentPoint(switchPort);
if (attachmentPoint != null) {
updateAttachmentPointLastSeen = true;
} else {
newAttachmentPoint = true;
}
if (nwSrc != 0) {
networkAddress = device.getNetworkAddress(nwSrc);
if (networkAddress != null) {
updateNetworkAddressLastSeen = true;
} else if (eth != null && (eth.getPayload() instanceof ARP)) {
/** MAC-IP association should be learnt from both ARP request and reply.
* Since a host learns some other host's mac to ip mapping after receiving
* an ARP request from the host.
*
* However, device's MAC-IP mapping could be wrong if a host sends a ARP request with
* an IP other than its own. Unfortunately, there isn't an easy way to allow both learning
* and prevent incorrect learning.
*/
networkAddress = new DeviceNetworkAddress(nwSrc,
currentDate);
newNetworkAddress = true;
}
// Also, if this address is currently mapped to a different
// device, fix it. This should be rare, so it is OK to do update
// the storage from here.
//
// NOTE: the mapping is observed, and the decision is made based
// on that, outside a lock. So the state may change by the time
// we get to do the map update. But that is OK since the mapping
// will eventually get consistent.
Device deviceByNwaddr = this.getDeviceByIPv4Address(nwSrc);
if ((deviceByNwaddr != null) &&
(deviceByNwaddr.getDataLayerAddressAsLong() !=
device.getDataLayerAddressAsLong())) {
updateNeworkAddressMap = true;
Device dCopy = new Device(deviceByNwaddr);
DeviceNetworkAddress naOld = dCopy.getNetworkAddress(nwSrc);
Map<Integer, DeviceNetworkAddress> namap =
dCopy.getNetworkAddressesMap();
if (namap.containsKey(nwSrc)) namap.remove(nwSrc);
dCopy.setNetworkAddresses(namap.values());
this.devMgrMaps.updateMaps(dCopy);
if (naOld !=null)
removeNetworkAddressFromStorage(dCopy, naOld);
}
}
if ((vlan == null && device.getVlanId() != null) ||
(vlan != null && !vlan.equals(device.getVlanId()))) {
updateDeviceVlan = true;
}
if (newAttachmentPoint || newNetworkAddress || updateDeviceVlan ||
updateNeworkAddressMap) {
Device nd = new Device(device);
try {
// Check if we have seen this attachmentPoint recently,
// An exception is thrown if the attachmentPoint is blocked.
if (newAttachmentPoint) {
attachmentPoint = getNewAttachmentPoint(nd, switchPort);
if (attachmentPoint == null) {
newAttachmentPoint = false;
} else {
nd.addAttachmentPoint(attachmentPoint);
evHistAttachmtPt(nd.getDataLayerAddressAsLong(),
attachmentPoint.getSwitchPort(),
EvAction.ADDED,
"New AP from pkt-in");
}
}
if (clearAttachmentPoints) {
nd.clearAttachmentPoints();
evHistAttachmtPt(nd, 0L, (short)(-1),
EvAction.CLEARED, "Grat. ARP from pkt-in");
}
if (newNetworkAddress) {
// add the address
nd.addNetworkAddress(networkAddress);
if (log.isTraceEnabled()) {
log.trace("Device {} added IP {}",
new Object[] {nd, IPv4.fromIPv4Address(nwSrc)});
}
}
if (updateDeviceVlan) {
nd.setVlanId(vlan);
writeDeviceToStorage(nd, currentDate);
}
} catch (APBlockedException e) {
assert(attachmentPoint == null);
ret = Command.STOP;
// install drop flow to avoid overloading the controller
// set hard timeout to 5 seconds to avoid blocking host
// forever
ForwardingBase.blockHost(floodlightProvider, switchPort,
dlAddr, ForwardingBase.FLOWMOD_DEFAULT_HARD_TIMEOUT);
}
// Update the maps
devMgrMaps.updateMaps(nd);
// publish the update after devMgrMaps is updated.
if (newNetworkAddress) {
updateAddress(nd, networkAddress, true);
}
if (updateDeviceVlan) {
updateVlan(nd);
}
device = nd;
}
if (updateAttachmentPointLastSeen) {
attachmentPoint.setLastSeen(currentDate);
if (attachmentPoint.shouldWriteLastSeenToStorage())
writeAttachmentPointToStorage(
device, attachmentPoint, currentDate);
}
if (updateNetworkAddressLastSeen || newNetworkAddress) {
if (updateNetworkAddressLastSeen)
networkAddress.setLastSeen(currentDate);
if (newNetworkAddress ||
networkAddress.shouldWriteLastSeenToStorage())
writeNetworkAddressToStorage(
device, networkAddress, currentDate);
}
if (updateDevice) {
writeDeviceToStorage(device, currentDate);
}
} else { // device is null
if (log.isTraceEnabled())
log.trace(" new device");
handleNewDevice(match.getDataLayerSource(), currentDate,
switchPort, nwSrc, vlan);
}
processUpdates();
return ret;
}
/**
* Check if there exists a state of attachment point flapping.
* If not, we return a new attachment point (or resurrect an old one).
* If swPort is blocked for this device, throw an exception.
*
* This must be called with write lock held.
*/
private DeviceAttachmentPoint getNewAttachmentPoint(Device device,
SwitchPortTuple swPort)
throws APBlockedException {
Date currentDate = new Date();
// First, check if we have an existing attachment point
DeviceAttachmentPoint curAttachmentPoint = null;
IOFSwitch newSwitch = swPort.getSw();
for (DeviceAttachmentPoint existingAttachmentPoint:
device.getAttachmentPoints()) {
IOFSwitch existingSwitch =
existingAttachmentPoint.getSwitchPort().getSw();
if ((newSwitch == existingSwitch) || ((topology != null) &&
topology.inSameCluster(newSwitch.getId(), existingSwitch.getId()))) {
curAttachmentPoint = existingAttachmentPoint;
break;
}
}
if (curAttachmentPoint != null) {
Long curDPID = curAttachmentPoint.getSwitchPort().getSw().getId();
Short curPort = curAttachmentPoint.getSwitchPort().getPort();
boolean sameBD =
topology.isInSameBroadcastDomain(swPort.getSw().getId(),
swPort.getPort(),
curDPID, curPort);
if (sameBD) return null;
}
// Do we have an old attachment point?
DeviceAttachmentPoint attachmentPoint =
device.getOldAttachmentPoint(swPort);
if (attachmentPoint == null) {
attachmentPoint = new DeviceAttachmentPoint(swPort, currentDate);
} else {
attachmentPoint.setLastSeen(currentDate);
if (attachmentPoint.isBlocked()) {
// Attachment point is currently in blocked state
// If curAttachmentPoint exists and active, drop the packet
if (curAttachmentPoint != null &&
currentDate.getTime() -
curAttachmentPoint.getLastSeen().getTime() < 600000) {
throw new APBlockedException("Attachment point is blocked");
}
log.info("Unblocking {} for device {}",
attachmentPoint.getSwitchPort(), device);
attachmentPoint.setBlocked(false);
evHistAttachmtPt(device.getDataLayerAddressAsLong(), swPort,
EvAction.UNBLOCKED, "packet-in after block timer expired");
}
// Remove from old list
device.removeOldAttachmentPoint(attachmentPoint);
}
// Update mappings
devMgrMaps.addDevAttachmentPoint(
device.getDataLayerAddressAsLong(), swPort, currentDate);
evHistAttachmtPt(device.getDataLayerAddressAsLong(), swPort,
EvAction.ADDED, "packet-in GNAP");
// If curAttachmentPoint exists, we mark it a conflict and may block it.
if (curAttachmentPoint != null) {
device.removeAttachmentPoint(curAttachmentPoint);
device.addOldAttachmentPoint(curAttachmentPoint);
// If two ports are in the same port-channel, we don't treat it
// as conflict, but will forward based on the last seen switch-port
if (!devMgrMaps.inSamePortChannel(swPort,
curAttachmentPoint.getSwitchPort())) {
curAttachmentPoint.setConflict(currentDate);
if (curAttachmentPoint.isFlapping()) {
curAttachmentPoint.setBlocked(true);
evHistAttachmtPt(device.getDataLayerAddressAsLong(),
curAttachmentPoint.getSwitchPort(),
EvAction.BLOCKED, "Conflict");
writeAttachmentPointToStorage(device, curAttachmentPoint,
currentDate);
log.warn(
"Device {}: flapping between {} and {}, block the latter",
new Object[] {device, swPort,
curAttachmentPoint.getSwitchPort()});
// Check if flapping is between the same switch port
if (swPort.getSw().getId() ==
curAttachmentPoint.getSwitchPort().getSw().getId()
&& swPort.getPort() ==
curAttachmentPoint.getSwitchPort().getPort()) {
log.warn("Fake flapping on port " + swPort.getPort() +
" between sw " + swPort.getSw() + " and " +
curAttachmentPoint.getSwitchPort().getSw());
device.removeOldAttachmentPoint(curAttachmentPoint);
removeAttachmentPointFromStorage(device,
curAttachmentPoint);
}
} else {
removeAttachmentPointFromStorage(device, curAttachmentPoint);
evHistAttachmtPt(device.getDataLayerAddressAsLong(),
curAttachmentPoint.getSwitchPort(),
EvAction.REMOVED, "Conflict");
}
}
updateMoved(device, curAttachmentPoint.getSwitchPort(),
attachmentPoint);
if (log.isDebugEnabled()) {
log.debug("Device {} moved from {} to {}", new Object[] {
device, curAttachmentPoint.getSwitchPort(), swPort});
}
} else {
updateStatus(device, true);
log.debug("Device {} added {}", device, swPort);
}
writeAttachmentPointToStorage(device, attachmentPoint, currentDate);
return attachmentPoint;
}
private boolean isValidInputPort(Short port) {
// Not a physical port. We should not 'discover' attachment points where
return ((int)port.shortValue() & 0xff00) != 0xff00 ||
port.shortValue() == (short)0xfffe;
}
/**
* Removes the specified device from data layer and network layer maps.
* Does NOT remove the device from switch and switch:port level maps.
* Must be called from within a write lock.
* @param device
*/
protected void delDevice(Device device) {
devMgrMaps.delFromMaps(device);
removeDeviceDiscoveredStateFromStorage(device);
updateStatus(device, false);
if (log.isDebugEnabled()) {
log.debug("Removed device {}", device);
}
processUpdates();
}
@Override
public Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) {
switch (msg.getType()) {
case PACKET_IN:
return this.processPacketInMessage(sw, (OFPacketIn) msg, cntx);
case PORT_STATUS:
return this.processPortStatusMessage(sw, (OFPortStatus) msg);
}
log.error("received an unexpected message {} from switch {}", msg, sw);
return Command.CONTINUE;
}
/**
* @param floodlightProvider the floodlightProvider to set
*/
public void setFloodlightProvider(IFloodlightProviderService floodlightProvider) {
this.floodlightProvider = floodlightProvider;
}
/**
* @param topology the topology to set
*/
public void setTopology(ITopologyService topology) {
this.topology = topology;
}
/**
* @param topology the topology to set
*/
public void setLinkDiscovery(ILinkDiscoveryService linkDiscovery) {
this.linkDiscovery = linkDiscovery;
}
@Override
public Device getDeviceByDataLayerAddress(byte[] address) {
if (address.length != Ethernet.DATALAYER_ADDRESS_LENGTH) return null;
return getDeviceByDataLayerAddress(Ethernet.toLong(address));
}
@Override
public Device getDeviceByDataLayerAddress(long address) {
return this.devMgrMaps.dataLayerAddressDeviceMap.get(address);
}
@Override
public Device getDeviceByIPv4Address(Integer address) {
lock.readLock().lock();
try {
return this.devMgrMaps.ipv4AddressDeviceMap.get(address);
} finally {
lock.readLock().unlock();
}
}
@Override
public void invalidateDeviceAPsByIPv4Address(Integer address) {
lock.readLock().lock();
try {
Device d = this.devMgrMaps.ipv4AddressDeviceMap.get(address);
if (d!= null && d.getAttachmentPoints() != null) {
d.getAttachmentPoints().clear();
}
} finally {
lock.readLock().unlock();
}
}
@Override
public boolean isDeviceKnownToCluster(long deviceId, long switchId) {
Device device = devMgrMaps.getDeviceByDataLayerAddr(deviceId);
if (device == null) {
return false;
}
/**
* Iterate through all APs and check if the switch clusterID matches
* with the given clusterId
*/
for(DeviceAttachmentPoint dap : device.getAttachmentPoints()) {
if (dap == null) continue;
if (topology.getSwitchClusterId(switchId) ==
topology.getSwitchClusterId(dap.getSwitchPort().getSw().getId())) {
return true;
}
}
return false;
}
@Override
public List<Device> getDevices() {
lock.readLock().lock();
try {
return new ArrayList<Device>(
this.devMgrMaps.dataLayerAddressDeviceMap.values());
} finally {
lock.readLock().unlock();
}
}
@Override
public void addedSwitch(IOFSwitch sw) {
// Fix up attachment points related to the switch
lock.writeLock().lock();
try {
Long swDpid = sw.getId();
List<PendingAttachmentPoint> papl =
devMgrMaps.switchUnresolvedAPMap.get(swDpid);
if (papl != null) {
for (PendingAttachmentPoint pap : papl) {
Device d = devMgrMaps.getDeviceByDataLayerAddr(pap.mac);
if (d == null) continue;
// Add attachment point
devMgrMaps.addDevAttachmentPoint(
pap.mac, sw, pap.switchPort, pap.lastSeen);
evHistAttachmtPt(pap.mac,
sw.getId(), pap.switchPort,
EvAction.ADDED, "Switch Added");
}
devMgrMaps.switchUnresolvedAPMap.remove(swDpid);
}
} finally {
lock.writeLock().unlock();
}
}
@Override
public void removedSwitch (IOFSwitch sw) {
// remove all devices attached to this switch
if (!devMgrMaps.isSwitchPresent(sw)) {
// Switch not present
return;
}
lock.writeLock().lock();
try {
devMgrMaps.delSwitchfromMaps(sw);
} finally {
lock.writeLock().unlock();
}
}
public void addedOrUpdatedLink(long srcSw, short srcPort, int srcPortState,
long dstSw, short dstPort, int dstPortState, ILinkDiscovery.LinkType type)
{
if (((srcPortState & OFPortState.OFPPS_STP_MASK.getValue()) !=
OFPortState.OFPPS_STP_BLOCK.getValue()) &&
((dstPortState & OFPortState.OFPPS_STP_MASK.getValue()) !=
OFPortState.OFPPS_STP_BLOCK.getValue())) {
// Remove all devices living on this switch:port now that it is
// internal
SwitchPortTuple switchPort = new SwitchPortTuple(floodlightProvider.getSwitches().get(dstSw), dstPort);
lock.writeLock().lock();
try {
devMgrMaps.removeSwPort(switchPort);
} finally {
lock.writeLock().unlock();
}
}
}
/**
* Process device manager aware updates. Call without any lock held
*/
protected void processUpdates() {
synchronized (updates) {
Update update = null;
while ((update = updates.poll()) != null) {
log.debug ("processUpdates, update: {}", update);
if (deviceManagerAware == null)
continue;
for (IDeviceManagerAware dma : deviceManagerAware) {
switch (update.updateType) {
case ADDED:
dma.deviceAdded(update.device);
break;
case REMOVED:
dma.deviceRemoved(update.device);
break;
case MOVED:
dma.deviceMoved(update.device,
update.oldSw,
update.oldSwPort,
update.sw, update.swPort);
break;
case ADDRESS_ADDED:
dma.deviceNetworkAddressAdded(update.device,
update.address);
break;
case ADDRESS_REMOVED:
dma.deviceNetworkAddressRemoved(update.device,
update.address);
break;
case VLAN_CHANGED:
dma.deviceVlanChanged(update.device);
break;
}
}
}
}
}
/**
* Puts an update in queue for the Device. Must be called from within the
* write lock.
* @param device
* @param added
*/
protected void updateStatus(Device device, boolean added) {
synchronized (updates) {
Update update;
if (added) {
update = new Update(UpdateType.ADDED);
} else {
update = new Update(UpdateType.REMOVED);
}
update.device = device;
this.updates.add(update);
}
}
/**
* Puts an update in queue to indicate the Device moved. Must be called
* from within the write lock.
* @param device The device that has moved.
* @param oldSwPort The old switchport
* @param newDap The new attachment point
*/
protected void updateMoved(Device device, SwitchPortTuple oldSwPort,
DeviceAttachmentPoint newDap) {
// We also clear the attachment points on other islands
device.clearAttachmentPoints();
evHistAttachmtPt(device, 0L, (short)(-1), EvAction.CLEARED, "Moved");
device.addAttachmentPoint(newDap);
evHistAttachmtPt(device.getDataLayerAddressAsLong(),
newDap.getSwitchPort(),
EvAction.ADDED, "Moved");
synchronized (updates) {
Update update = new Update(UpdateType.MOVED);
update.device = device;
update.oldSw = oldSwPort.getSw();
update.oldSwPort = oldSwPort.getPort();
update.sw = newDap.getSwitchPort().getSw();
update.swPort = newDap.getSwitchPort().getPort();
this.updates.add(update);
}
}
protected void updateAddress(Device device, DeviceNetworkAddress address,
boolean added) {
synchronized (updates) {
Update update;
if (added) {
update = new Update(UpdateType.ADDRESS_ADDED);
} else {
update = new Update(UpdateType.ADDRESS_REMOVED);
}
update.device = device;
update.address = address;
this.updates.add(update);
}
}
protected void updateVlan(Device device) {
synchronized (updates) {
Update update = new Update(UpdateType.VLAN_CHANGED);
update.device = device;
this.updates.add(update);
}
}
/**
* Iterates through all devices and cleans up attachment points
*/
@Override
public void topologyChanged() {
deviceUpdateTask.reschedule(10, TimeUnit.MILLISECONDS);
}
private boolean isNewer(DeviceAttachmentPoint dap1,
DeviceAttachmentPoint dap2) {
// dap 1 is newer than dap 2 if
// (1) if dap 1 is a non-broadcast domain attachment point
// (a) if (dap 2 is a non-broadcast domain attachment point
// and dap1.lastseen time is after dap2.last seentime
// OR
// (b) if (dap 2 is a braodcast domain attachment point
// and dap1.lastseen time is after (dap2.lastseen-5minutes)
// (2) if dap 1 is a broadcast domain attachment point
// (a) if dap 2 is a non-broadcast attachment point and
// dap1.lastseen is after (dap2.lastseen + 5 minutes)
// OR
// (b) if dap2 is a broadcastdomain attachment point and
// dap2.lastseen
SwitchPortTuple sp1, sp2;
boolean flag1, flag2;
sp1 = dap1.getSwitchPort();
sp2 = dap2.getSwitchPort();
flag1 = topology.isBroadcastDomainPort(sp1.getSw().getId(),
sp1.getPort());
flag2 = topology.isBroadcastDomainPort(sp2.getSw().getId(),
sp2.getPort());
long ls1 = dap1.getLastSeen().getTime();
long ls2 = dap2.getLastSeen().getTime();
if (flag1 == false) {
if (flag2 == false) {
return (ls1 > ls2);
} else {
return (ls1 > (ls2 - NBD_TO_BD_TIMEDIFF_MS));
}
} else {
if (flag2 == false) {
return (ls1 > (ls2 + NBD_TO_BD_TIMEDIFF_MS));
} else {
return ((ls1 > ls2 + BD_TO_BD_TIMEDIFF_MS) ||
(ls1 < ls2 && ls1 > ls2 - BD_TO_BD_TIMEDIFF_MS));
}
}
}
/**
* Removes any attachment points that are in the same
* {@link net.floodlightcontroller.topology.SwitchCluster SwitchCluster}
* @param d The device to update the attachment points
*/
public void cleanupAttachmentPoints(Device d) {
// The long here is the SwitchCluster ID
Map<Long, DeviceAttachmentPoint> map =
new HashMap<Long, DeviceAttachmentPoint>();
// Get only the latest DAPs into a map
for (DeviceAttachmentPoint dap : d.getAttachmentPoints()) {
if (DeviceAttachmentPoint.isNotNull(dap) &&
!topology.isInternal(dap.getSwitchPort().getSw().getId(), dap.getSwitchPort().getPort())) {
long clusterId = topology.getSwitchClusterId(
dap.getSwitchPort().getSw().getId());
// do not use attachment points if the attachment point is
// not allowed by topology
long swid = dap.getSwitchPort().getSw().getId();
short port = dap.getSwitchPort().getPort();
if (topology.isAllowed(swid, port) == false)
continue;
if (map.containsKey(clusterId)) {
// We compare to see which one is newer, move attachment
// point to "old" list.
// They are removed after deleting from storage.
DeviceAttachmentPoint value = map.get(clusterId);
//if (dap.getLastSeen().after(value.getLastSeen())) {
if (isNewer(dap, map.get(clusterId))) {
map.put(clusterId, dap);
d.addOldAttachmentPoint(value); // on copy of device
}
} else {
map.put(clusterId, dap);
}
}
}
synchronized (d) {
// Since the update below is happening on a copy of the device it
// should not impact packetIn processing time due to lock contention
d.setAttachmentPoints(map.values());
}
}
public void clearAllDeviceStateFromMemory() {
devMgrMaps.clearMaps();
}
private Date ageBoundaryDifference(Date currentDate, long expire) {
if (expire == 0) {
return new Date(0);
}
return new Date(currentDate.getTime() - 1000*expire);
}
// *********************
// Storage Write Methods
// *********************
protected void writeDeviceToStorage(Device device, Date currentDate) {
Map<String, Object> rowValues = new HashMap<String, Object>();
String macString = device.getDlAddrString();
rowValues.put(MAC_COLUMN_NAME, macString);
if (device.getVlanId() != null)
rowValues.put(VLAN_COLUMN_NAME, device.getVlanId());
rowValues.put(LAST_SEEN_COLUMN_NAME, currentDate);
storageSource.updateRowAsync(DEVICE_TABLE_NAME, rowValues);
device.lastSeenWrittenToStorage(currentDate);
for (DeviceAttachmentPoint attachmentPoint:
device.getAttachmentPoints()) {
writeAttachmentPointToStorage(device, attachmentPoint, currentDate);
}
for (DeviceNetworkAddress networkAddress:
device.getNetworkAddresses()) {
writeNetworkAddressToStorage(device, networkAddress, currentDate);
}
}
protected void writeAttachmentPointToStorage(Device device,
DeviceAttachmentPoint attachmentPoint, Date currentDate) {
assert(device != null);
assert(attachmentPoint != null);
String deviceId = device.getDlAddrString();
SwitchPortTuple switchPort = attachmentPoint.getSwitchPort();
assert(switchPort != null);
String switchId = switchPort.getSw().getStringId();
Short port = switchPort.getPort();
String attachmentPointId =
deviceId + "|" + switchId + "|" + port.toString();
Map<String, Object> rowValues = new HashMap<String, Object>();
rowValues.put(ID_COLUMN_NAME, attachmentPointId);
rowValues.put(DEVICE_COLUMN_NAME, deviceId);
rowValues.put(SWITCH_COLUMN_NAME, switchId);
rowValues.put(PORT_COLUMN_NAME, port);
rowValues.put(LAST_SEEN_COLUMN_NAME, attachmentPoint.getLastSeen());
String status = null;
if (attachmentPoint.isBlocked())
status = "blocked: duplicate mac";
rowValues.put(AP_STATUS_COLUMN_NAME, status);
storageSource.updateRowAsync(DEVICE_ATTACHMENT_POINT_TABLE_NAME,
rowValues);
attachmentPoint.lastSeenWrittenToStorage(currentDate);
}
// **********************
// Storage Remove Methods
// **********************
protected void removeAttachmentPointFromStorage(Device device,
DeviceAttachmentPoint attachmentPoint) {
try {
String deviceId = device.getDlAddrString();
SwitchPortTuple switchPort = attachmentPoint.getSwitchPort();
String switchId = switchPort.getSw().getStringId();
Short port = switchPort.getPort();
String attachmentPointId =
deviceId + "|" + switchId + "|" + port.toString();
storageSource.deleteRowAsync(
DEVICE_ATTACHMENT_POINT_TABLE_NAME, attachmentPointId);
} catch (NullPointerException e) {
log.warn("Null ptr exception for device {} attach-point {}",
device, attachmentPoint);
}
}
protected void writeNetworkAddressToStorage(Device device,
DeviceNetworkAddress networkAddress, Date currentDate) {
assert(device != null);
assert(networkAddress != null);
String deviceId = device.getDlAddrString();
String networkAddressString = IPv4.fromIPv4Address(
networkAddress.getNetworkAddress());
String networkAddressId = deviceId + "|" + networkAddressString;
if (networkAddress.getNetworkAddress() == 0) {
log.error("Zero network address for device {}\n {}",
device, Thread.currentThread().getStackTrace());
return;
}
Map<String, Object> rowValues = new HashMap<String, Object>();
rowValues.put(ID_COLUMN_NAME, networkAddressId);
rowValues.put(DEVICE_COLUMN_NAME, deviceId);
rowValues.put(NETWORK_ADDRESS_COLUMN_NAME, networkAddressString);
rowValues.put(LAST_SEEN_COLUMN_NAME, networkAddress.getLastSeen());
storageSource.updateRowAsync(DEVICE_NETWORK_ADDRESS_TABLE_NAME,
rowValues);
networkAddress.lastSeenWrittenToStorage(currentDate);
}
protected void removeNetworkAddressFromStorage(Device device,
DeviceNetworkAddress networkAddress) {
assert(device != null);
assert(networkAddress != null);
String deviceId = device.getDlAddrString();
String networkAddressString = IPv4.fromIPv4Address(
networkAddress.getNetworkAddress());
String networkAddressId = deviceId + "|" + networkAddressString;
storageSource.deleteRowAsync(DEVICE_NETWORK_ADDRESS_TABLE_NAME,
networkAddressId);
}
// ********************
// Storage Read Methods
// ********************
public boolean readPortChannelConfigFromStorage() {
devMgrMaps.clearPortChannelMap();
try {
IResultSet pcResultSet = storageSource.executeQuery(
PORT_CHANNEL_TABLE_NAME, null, null, null);
while (pcResultSet.next()) {
String port_channel = pcResultSet.getString(PORT_CHANNEL_COLUMN_NAME);
String switch_id = pcResultSet.getString(PC_SWITCH_COLUMN_NAME);
String port_name = pcResultSet.getString(PC_PORT_COLUMN_NAME);
devMgrMaps.addPortToPortChannel(switch_id, port_name, port_channel);
}
return true;
} catch (StorageException e) {
log.error("Error reading port-channel data from storage {}", e);
return false;
}
}
public boolean readAllDeviceStateFromStorage() {
Date currentDate = new Date();
try {
// These methods MUST be called in this order
readDevicesFromStorage(currentDate);
readDeviceAttachmentPointsFromStorage(currentDate);
readDeviceNetworkAddressesFromStorage(currentDate);
return true;
}
catch (StorageException e) {
log.error("Error reading device data from storage {}", e);
return false;
}
}
/**
* Exclude any entries which are older than the
* @throws StorageException
*/
private void readDevicesFromStorage(Date currentDate)
throws StorageException {
String [] colNames = new String[] {MAC_COLUMN_NAME,
VLAN_COLUMN_NAME, LAST_SEEN_COLUMN_NAME};
IResultSet deviceResultSet = storageSource.executeQuery(
DEVICE_TABLE_NAME, colNames, null, null);
while (deviceResultSet.next()) {
Date lastSeen = deviceResultSet.getDate(LAST_SEEN_COLUMN_NAME);
String macString = deviceResultSet.getString(MAC_COLUMN_NAME);
assert(macString != null);
if (macString == null) {
log.debug("Ignoring storage entry with no mac address");
continue;
}
byte[] macBytes = HexString.fromHexString(macString);
if (lastSeen == null) lastSeen = new Date();
Device d = new Device(macBytes, lastSeen);
if (deviceResultSet.containsColumn(VLAN_COLUMN_NAME)) {
d.setVlanId(deviceResultSet.getShort(VLAN_COLUMN_NAME));
if (d.getVlanId() < 0 || d.getVlanId() >= 4096) {
log.debug("Ignore storage entry with invalid vlan {}", d);
continue;
}
}
devMgrMaps.updateMaps(d);
}
}
private void readDeviceAttachmentPointsFromStorage(Date currentDate)
throws StorageException {
String [] colNames = new String[] {
DEVICE_COLUMN_NAME, SWITCH_COLUMN_NAME,
PORT_COLUMN_NAME, LAST_SEEN_COLUMN_NAME};
IResultSet dapResultSet = storageSource.executeQuery(
DEVICE_ATTACHMENT_POINT_TABLE_NAME, colNames, null, null);
while (dapResultSet.next()) {
Date lastSeen = dapResultSet.getDate(LAST_SEEN_COLUMN_NAME);
if (lastSeen == null) lastSeen = new Date();
String macString = dapResultSet.getString(DEVICE_COLUMN_NAME);
String dpidString = dapResultSet.getString(SWITCH_COLUMN_NAME);
if (macString == null || dpidString == null)
continue;
long mac = HexString.toLong(macString);
long swDpid = HexString.toLong(dpidString);
IOFSwitch sw = floodlightProvider.getSwitches().get(swDpid);
Integer port = dapResultSet.getIntegerObject(PORT_COLUMN_NAME);
if (port == null || port > Short.MAX_VALUE) continue;
if (sw == null) { // switch has not joined yet
devMgrMaps.updateSwitchUnresolvedAPMap(
swDpid, mac, port.shortValue(), lastSeen);
} else {
devMgrMaps.addDevAttachmentPoint(
mac, sw, port.shortValue(), lastSeen);
evHistAttachmtPt(mac, sw.getId(), port.shortValue(),
EvAction.ADDED, "Read from storage");
}
}
}
private void readDeviceNetworkAddressesFromStorage(Date currentDate)
throws StorageException {
String [] colNames = new String[]{ DEVICE_COLUMN_NAME,
NETWORK_ADDRESS_COLUMN_NAME,
LAST_SEEN_COLUMN_NAME};
IResultSet dnaResultSet = storageSource.executeQuery(
DEVICE_NETWORK_ADDRESS_TABLE_NAME, colNames, null, null);
while (dnaResultSet.next()) {
Date lastSeen = dnaResultSet.getDate(LAST_SEEN_COLUMN_NAME);
if (lastSeen == null) lastSeen = new Date();
String macStr = dnaResultSet.getString(DEVICE_COLUMN_NAME);
String netaddr = dnaResultSet.getString(
NETWORK_ADDRESS_COLUMN_NAME);
if (macStr == null) {
continue;
}
if (netaddr == null) {
// If the key is in the database then ignore this record
continue;
}
devMgrMaps.addNwAddrByDataLayerAddr(HexString.toLong(macStr),
IPv4.toIPv4Address(netaddr),
lastSeen);
}
}
public void removeDeviceDiscoveredStateFromStorage(Device device) {
String deviceId = device.getDlAddrString();
// Remove all of the attachment points
storageSource.deleteMatchingRowsAsync(DEVICE_ATTACHMENT_POINT_TABLE_NAME,
new OperatorPredicate(DEVICE_COLUMN_NAME,
OperatorPredicate.Operator.EQ, deviceId));
storageSource.deleteMatchingRowsAsync(DEVICE_NETWORK_ADDRESS_TABLE_NAME,
new OperatorPredicate(DEVICE_COLUMN_NAME,
OperatorPredicate.Operator.EQ, deviceId));
// Remove the device
storageSource.deleteRow(DEVICE_TABLE_NAME, deviceId);
}
/**
* IStorageSource listeners.
* Need to optimize if we support a large number of port-channel entries.
*/
@Override
public void rowsModified(String tableName, Set<Object> rowKeys) {
portChannelConfigChanged = true;
deviceUpdateTask.reschedule(5, TimeUnit.SECONDS);
}
@Override
public void rowsDeleted(String tableName, Set<Object> rowKeys) {
portChannelConfigChanged = true;
deviceUpdateTask.reschedule(5, TimeUnit.SECONDS);
}
/**
* Remove aged network address from device
*
* @param device
* @param currentDate
* @return the new device object since the device is immutable
*/
private Device removeAgedNetworkAddresses(Device device, Date currentDate) {
Collection<DeviceNetworkAddress> addresses =
device.getNetworkAddresses();
for (DeviceNetworkAddress address : addresses) {
long expire = address.getExpire();
if (expire == 0) {
expire = DEVICE_NA_MAX_AGE;
}
Date agedBoundary = ageBoundaryDifference(currentDate, expire);
if (address.getLastSeen().before(agedBoundary)) {
devMgrMaps.delNwAddrByDataLayerAddr(device.getDataLayerAddressAsLong(),
address.getNetworkAddress().intValue());
}
}
return devMgrMaps.getDeviceByDataLayerAddr(device.getDataLayerAddressAsLong());
}
/**
* Remove aged device attachment point
*
* @param device
* @param currentDate
* @return the new device object since the device is immutable
*/
private Device removeAgedAttachmentPoints(Device device, Date currentDate) {
Collection<DeviceAttachmentPoint> aps = device.getAttachmentPoints();
long dlAddr = device.getDataLayerAddressAsLong();
for (DeviceAttachmentPoint ap : aps) {
int expire = ap.getExpire();
if (expire == 0) {
expire = DEVICE_AP_MAX_AGE;
}
Date agedBoundary = ageBoundaryDifference(currentDate, expire);
if (ap.getLastSeen().before(agedBoundary)) {
devMgrMaps.delDevAttachmentPoint(dlAddr, ap.getSwitchPort());
evHistAttachmtPt(device.getDataLayerAddressAsLong(),
ap.getSwitchPort(), EvAction.REMOVED,
"Aged");
}
}
return devMgrMaps.getDeviceByDataLayerAddr(device.getDataLayerAddressAsLong());
}
/**
* Age device entry based on an expiry associated with the device.
*/
private void removeAgedDevices(Date currentDate) {
Date deviceAgeBoundary = ageBoundaryDifference(currentDate,
DEVICE_MAX_AGE);
Collection<Device> deviceColl = devMgrMaps.getDevices();
for (Device device: deviceColl) {
device = removeAgedNetworkAddresses(device, currentDate);
device = removeAgedAttachmentPoints(device, currentDate);
if ((device.getAttachmentPoints().size() == 0) &&
(device.getNetworkAddresses().size() == 0) &&
(device.getLastSeen().before(deviceAgeBoundary))) {
delDevice(device);
}
}
}
protected static int DEVICE_AGING_TIMER= 60 * 15; // in seconds
protected static final int DEVICE_AGING_TIMER_INTERVAL = 1; // in seconds
/**
* Create the deviceAgingTimer, which calls removeAgedDeviceState()
* periodically.
*/
private void enableDeviceAgingTimer() {
if (deviceAgingTimer != null) {
return;
}
deviceAgingTimer = new Runnable() {
@Override
public void run() {
Date currentDate = new Date();
removeAgedDevices(currentDate);
if (deviceAgingTimer != null) {
ScheduledExecutorService ses =
threadPool.getScheduledExecutor();
ses.schedule(this, DEVICE_AGING_TIMER, TimeUnit.SECONDS);
}
}
};
threadPool.getScheduledExecutor().schedule(
deviceAgingTimer, DEVICE_AGING_TIMER_INTERVAL, TimeUnit.SECONDS);
}
@Override
public boolean isCallbackOrderingPrereq(OFType type, String name) {
return (type == OFType.PACKET_IN && name.equals("topology"));
}
@Override
public boolean isCallbackOrderingPostreq(OFType type, String name) {
return false;
}
protected class DeviceUpdateWorker implements Runnable {
@Override
public void run() {
boolean updatePortChannel = portChannelConfigChanged;
portChannelConfigChanged = false;
if (updatePortChannel) {
readPortChannelConfigFromStorage();
}
try {
log.debug("DeviceUpdateWorker: cleaning up attachment points");
for (IOFSwitch sw : devMgrMaps.getSwitches()) {
// If the set of devices connected to the switches we
// re-iterate a max of 3 times - WHY? TODO
int maxIter = 3;
while (maxIter > 0) {
try {
for (Device d: devMgrMaps.getDevicesOnASwitch(sw)) {
Device dCopy = new Device(d);
cleanupAttachmentPoints(dCopy);
for (DeviceAttachmentPoint dap :
dCopy.getOldAttachmentPoints()) {
// Don't remove conflict attachment points
// with recent activities
if (dap.isInConflict() && !updatePortChannel)
continue;
// Delete from memory after storage,
// otherwise an exception will
// leave stale attachment points on storage.
removeAttachmentPointFromStorage(dCopy, dap);
dCopy.removeOldAttachmentPoint(dap);
}
// Update the maps with the new device copy
devMgrMaps.updateMaps(dCopy);
}
break;
} catch (ConcurrentModificationException e) {
maxIter--;
} catch (NullPointerException e) { }
}
if (maxIter == 0) {
log.warn("Device attachment point clean up " +
"attempted three times and failed.");
}
}
log.debug("DeviceUpdateWorker: finished cleaning up device " +
"attachment points");
} catch (StorageException e) {
log.error("DeviceUpdateWorker had a storage exception, " +
"Floodlight exiting");
System.exit(1);
}
}
}
@Override
public void linkDiscoveryUpdate(LDUpdate update) {
if (update.getOperation() == UpdateOperation.SWITCH_UPDATED) {
updatedSwitch(update.getSrc(), update.getSrcType());
} else if (update.getOperation() == UpdateOperation.ADD_OR_UPDATE) {
this.addedOrUpdatedLink(update.getSrc(), update.getSrcPort(),
update.getSrcPortState(),
update.getDst(), update.getDstPort(),
update.getDstPortState(),
update.getType());
}
}
public void updatedSwitch(long swId, SwitchType stype) {
IOFSwitch sw = floodlightProvider.getSwitches().get(swId);
if (sw.hasAttribute(IOFSwitch.SWITCH_IS_CORE_SWITCH)) {
removedSwitch(sw);
}
}
// **************************************************
// Device Manager's Event History members and methods
// **************************************************
// Attachment-point event history
public EventHistory<EventHistoryAttachmentPoint> evHistDevMgrAttachPt;
public EventHistoryAttachmentPoint evHAP;
private void evHistAttachmtPt(long dlAddr, SwitchPortTuple swPrt,
EvAction action, String reason) {
evHistAttachmtPt(
dlAddr,
swPrt.getSw().getId(),
swPrt.getPort(), action, reason);
}
private void evHistAttachmtPt(byte [] mac, SwitchPortTuple swPrt,
EvAction action, String reason) {
evHistAttachmtPt(Ethernet.toLong(mac), swPrt.getSw().getId(),
swPrt.getPort(), action, reason);
}
private void evHistAttachmtPt(Device d, long dpid, short port,
EvAction op, String reason) {
evHistAttachmtPt(d.getDataLayerAddressAsLong(),dpid, port, op, reason);
}
private void evHistAttachmtPt(long mac, long dpid, short port,
EvAction action, String reason) {
if (evHAP == null) {
evHAP = new EventHistoryAttachmentPoint();
}
evHAP.dpid = dpid;
evHAP.port = port;
evHAP.mac = mac;
evHAP.reason = reason;
evHAP = evHistDevMgrAttachPt.put(evHAP, action);
}
/***
* Packet-In Event history related classes and members
* @author subrata
*
*/
public EventHistory<OFMatch> evHistDevMgrPktIn;
private void evHistPktIn(OFMatch packetIn) {
evHistDevMgrPktIn.put(packetIn, EvAction.PKT_IN);
}
// IFloodlightModule methods
@Override
public Collection<Class<? extends IFloodlightService>> getModuleServices() {
Collection<Class<? extends IFloodlightService>> l =
new ArrayList<Class<? extends IFloodlightService>>();
l.add(IDeviceManagerService.class);
return l;
}
@Override
public Map<Class<? extends IFloodlightService>, IFloodlightService>
getServiceImpls() {
Map<Class<? extends IFloodlightService>,
IFloodlightService> m =
new HashMap<Class<? extends IFloodlightService>,
IFloodlightService>();
// We are the class that implements the service
m.put(IDeviceManagerService.class, this);
return m;
}
@Override
public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
Collection<Class<? extends IFloodlightService>> l =
new ArrayList<Class<? extends IFloodlightService>>();
l.add(IFloodlightProviderService.class);
l.add(ITopologyService.class);
l.add(ILinkDiscoveryService.class);
l.add(IStorageSourceService.class);
l.add(IThreadPoolService.class);
l.add(IRestApiService.class);
return l;
}
@Override
public void init(FloodlightModuleContext context)
throws FloodlightModuleException {
// Wire up all our dependencies
floodlightProvider =
context.getServiceImpl(IFloodlightProviderService.class);
topology =
context.getServiceImpl(ITopologyService.class);
linkDiscovery =
context.getServiceImpl(ILinkDiscoveryService.class);
storageSource =
context.getServiceImpl(IStorageSourceService.class);
threadPool =
context.getServiceImpl(IThreadPoolService.class);
restApi =
context.getServiceImpl(IRestApiService.class);
// We create this here because there is no ordering guarantee
this.deviceManagerAware = new HashSet<IDeviceManagerAware>();
this.updates = new LinkedList<Update>();
this.devMgrMaps = new DevMgrMaps();
this.lock = new ReentrantReadWriteLock();
this.evHistDevMgrAttachPt =
new EventHistory<EventHistoryAttachmentPoint>("Attachment-Point");
this.evHistDevMgrPktIn =
new EventHistory<OFMatch>("Pakcet-In");
}
@Override
public void startUp(FloodlightModuleContext context) {
// This is our 'constructor'
if (linkDiscovery != null) {
// Register to get updates from topology
linkDiscovery.addListener(this);
} else {
log.error("Could not add linkdiscovery listener");
}
if (topology != null) {
topology.addListener(this);
} else {
log.error("Could not add topology listener");
}
// Create our database tables
storageSource.createTable(DEVICE_TABLE_NAME, null);
storageSource.setTablePrimaryKeyName(
DEVICE_TABLE_NAME, MAC_COLUMN_NAME);
storageSource.createTable(DEVICE_ATTACHMENT_POINT_TABLE_NAME, null);
storageSource.setTablePrimaryKeyName(
DEVICE_ATTACHMENT_POINT_TABLE_NAME, ID_COLUMN_NAME);
storageSource.createTable(DEVICE_NETWORK_ADDRESS_TABLE_NAME, null);
storageSource.setTablePrimaryKeyName(
DEVICE_NETWORK_ADDRESS_TABLE_NAME, ID_COLUMN_NAME);
storageSource.createTable(PORT_CHANNEL_TABLE_NAME, null);
storageSource.setTablePrimaryKeyName(
PORT_CHANNEL_TABLE_NAME, PC_ID_COLUMN_NAME);
storageSource.addListener(PORT_CHANNEL_TABLE_NAME, this);
ScheduledExecutorService ses = threadPool.getScheduledExecutor();
deviceUpdateTask = new SingletonTask(ses, new DeviceUpdateWorker());
// Register for the OpenFlow messages we want
floodlightProvider.addOFMessageListener(OFType.PACKET_IN, this);
floodlightProvider.addOFMessageListener(OFType.PORT_STATUS, this);
// Register for switch events
floodlightProvider.addOFSwitchListener(this);
floodlightProvider.addInfoProvider("summary", this);
// Register our REST API
restApi.addRestletRoutable(new DeviceManagerWebRoutable());
// Read all our device state (MACs, IPs, attachment points) from storage
readAllDeviceStateFromStorage();
// Device and storage aging.
enableDeviceAgingTimer();
}
@Override
public void addListener(IDeviceManagerAware listener) {
deviceManagerAware.add(listener);
}
@Override
public Map<String, Object> getInfo(String type) {
if (!"summary".equals(type))
return null;
Map<String, Object> info = new HashMap<String, Object>();
info.put("# hosts", devMgrMaps.dataLayerAddressDeviceMap.size());
info.put("# IP Addresses", devMgrMaps.ipv4AddressDeviceMap.size());
int num_aps = 0;
for (Map<Integer, Device> devAps : devMgrMaps.switchPortDeviceMap.values())
num_aps += devAps.size();
info.put("# attachment points", num_aps);
return info;
}
}