Merge master branch into syncdev
Conflicts:
src/main/java/net/onrc/onos/ofcontroller/flowmanager/FlowManager.java
diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImpl.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImpl.java
index 031a528..087756c 100755
--- a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImpl.java
+++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImpl.java
@@ -39,21 +39,22 @@
import net.floodlightcontroller.core.FloodlightContext;
import net.floodlightcontroller.core.IFloodlightProviderService;
+import net.floodlightcontroller.core.IFloodlightProviderService.Role;
import net.floodlightcontroller.core.IHAListener;
import net.floodlightcontroller.core.IInfoProvider;
import net.floodlightcontroller.core.IOFMessageListener;
import net.floodlightcontroller.core.IOFSwitch;
-import net.floodlightcontroller.core.IFloodlightProviderService.Role;
+import net.floodlightcontroller.core.IUpdate;
import net.floodlightcontroller.core.module.FloodlightModuleContext;
import net.floodlightcontroller.core.module.IFloodlightModule;
import net.floodlightcontroller.core.module.IFloodlightService;
import net.floodlightcontroller.core.util.SingletonTask;
import net.floodlightcontroller.devicemanager.IDevice;
+import net.floodlightcontroller.devicemanager.IDeviceListener;
import net.floodlightcontroller.devicemanager.IDeviceService;
import net.floodlightcontroller.devicemanager.IEntityClass;
import net.floodlightcontroller.devicemanager.IEntityClassListener;
import net.floodlightcontroller.devicemanager.IEntityClassifierService;
-import net.floodlightcontroller.devicemanager.IDeviceListener;
import net.floodlightcontroller.devicemanager.SwitchPort;
import net.floodlightcontroller.devicemanager.web.DeviceRoutable;
import net.floodlightcontroller.flowcache.IFlowReconcileListener;
@@ -71,8 +72,6 @@
import net.floodlightcontroller.topology.ITopologyService;
import net.floodlightcontroller.util.MultiIterator;
import net.onrc.onos.ofcontroller.linkdiscovery.ILinkDiscovery.LDUpdate;
-import static net.floodlightcontroller.devicemanager.internal.
-DeviceManagerImpl.DeviceUpdate.Change.*;
import org.openflow.protocol.OFMatchWithSwDpid;
import org.openflow.protocol.OFMessage;
@@ -197,14 +196,14 @@
*/
protected Set<IDeviceListener> deviceListeners;
+ public enum DeviceUpdateType {
+ ADD, DELETE, CHANGE, MOVED;
+ }
+
/**
* A device update event to be dispatched
*/
- protected static class DeviceUpdate {
- public enum Change {
- ADD, DELETE, CHANGE;
- }
-
+ protected class DeviceUpdate implements IUpdate {
/**
* The affected device
*/
@@ -213,18 +212,18 @@
/**
* The change that was made
*/
- protected Change change;
+ protected DeviceUpdateType updateType;
/**
* If not added, then this is the list of fields changed
*/
protected EnumSet<DeviceField> fieldsChanged;
- public DeviceUpdate(IDevice device, Change change,
+ public DeviceUpdate(IDevice device, DeviceUpdateType updateType,
EnumSet<DeviceField> fieldsChanged) {
super();
this.device = device;
- this.change = change;
+ this.updateType = updateType;
this.fieldsChanged = fieldsChanged;
}
@@ -232,9 +231,49 @@
public String toString() {
String devIdStr = device.getEntityClass().getName() + "::" +
device.getMACAddressString();
- return "DeviceUpdate [device=" + devIdStr + ", change=" + change
+ return "DeviceUpdate [device=" + devIdStr + ", updateType=" + updateType
+ ", fieldsChanged=" + fieldsChanged + "]";
}
+
+ @Override
+ public void dispatch() {
+ if (logger.isTraceEnabled()) {
+ logger.trace("Dispatching device update: {}", this);
+ }
+ for (IDeviceListener listener : deviceListeners) {
+ switch (updateType) {
+ case ADD:
+ listener.deviceAdded(device);
+ break;
+ case DELETE:
+ listener.deviceRemoved(device);
+ break;
+ case CHANGE:
+ for (DeviceField field : fieldsChanged) {
+ switch (field) {
+ case IPV4:
+ listener.deviceIPV4AddrChanged(device);
+ break;
+ case SWITCH:
+ case PORT:
+ //listener.deviceMoved(update.device);
+ break;
+ case VLAN:
+ listener.deviceVlanChanged(device);
+ break;
+ default:
+ logger.debug("Unknown device field changed {}",
+ fieldsChanged.toString());
+ break;
+ }
+ }
+ break;
+ case MOVED:
+ listener.deviceMoved(device);
+ break;
+ }
+ }
+ }
}
@@ -1104,7 +1143,7 @@
// generate new device update
deviceUpdates =
updateUpdates(deviceUpdates,
- new DeviceUpdate(device, ADD, null));
+ new DeviceUpdate(device, DeviceUpdateType.ADD, null));
break;
}
@@ -1162,7 +1201,7 @@
if (changedFields.size() > 0)
deviceUpdates =
updateUpdates(deviceUpdates,
- new DeviceUpdate(newDevice, CHANGE,
+ new DeviceUpdate(newDevice, DeviceUpdateType.CHANGE,
changedFields));
// update the device map with a replace call
@@ -1211,7 +1250,7 @@
// generate new device update
deviceUpdates =
updateUpdates(deviceUpdates,
- new DeviceUpdate(dev, DELETE, null));
+ new DeviceUpdate(dev, DeviceUpdateType.DELETE, null));
}
}
@@ -1267,6 +1306,15 @@
* @param updates the updates to process.
*/
protected void processUpdates(Queue<DeviceUpdate> updates) {
+ if (updates == null) {
+ return;
+ }
+
+ DeviceUpdate update;
+ while (null != (update = updates.poll())) {
+ floodlightProvider.publishUpdate(update);
+ }
+ /*
if (updates == null) return;
DeviceUpdate update = null;
while (null != (update = updates.poll())) {
@@ -1304,6 +1352,7 @@
}
}
}
+ */
}
/**
@@ -1481,7 +1530,7 @@
changedFields.addAll(findChangedFields(newDevice, e));
}
if (changedFields.size() > 0)
- deviceUpdates.add(new DeviceUpdate(d, CHANGE,
+ deviceUpdates.add(new DeviceUpdate(d, DeviceUpdateType.CHANGE,
changedFields));
if (!deviceMap.replace(newDevice.getDeviceKey(),
@@ -1495,7 +1544,7 @@
continue;
}
} else {
- deviceUpdates.add(new DeviceUpdate(d, DELETE, null));
+ deviceUpdates.add(new DeviceUpdate(d, DeviceUpdateType.DELETE, null));
if (!deviceMap.remove(d.getDeviceKey(), d))
// concurrent modification; try again
// need to use device that is the map now for the next
@@ -1665,9 +1714,11 @@
* @param updates the updates to process.
*/
protected void sendDeviceMovedNotification(Device d) {
- for (IDeviceListener listener : deviceListeners) {
+ /*for (IDeviceListener listener : deviceListeners) {
listener.deviceMoved(d);
- }
+ }*/
+ floodlightProvider.publishUpdate(
+ new DeviceUpdate(d, DeviceUpdateType.MOVED, null));
}
/**
@@ -1705,8 +1756,7 @@
new LinkedList<DeviceUpdate>();
// delete this device and then re-learn all the entities
this.deleteDevice(device);
- deviceUpdates.add(new DeviceUpdate(device,
- DeviceUpdate.Change.DELETE, null));
+ deviceUpdates.add(new DeviceUpdate(device, DeviceUpdateType.DELETE, null));
if (!deviceUpdates.isEmpty())
processUpdates(deviceUpdates);
for (Entity entity: device.entities ) {
diff --git a/src/main/java/net/onrc/onos/datagrid/HazelcastDatagrid.java b/src/main/java/net/onrc/onos/datagrid/HazelcastDatagrid.java
index 481002f..d9fb7c3 100644
--- a/src/main/java/net/onrc/onos/datagrid/HazelcastDatagrid.java
+++ b/src/main/java/net/onrc/onos/datagrid/HazelcastDatagrid.java
@@ -17,9 +17,13 @@
import net.floodlightcontroller.core.module.FloodlightModuleException;
import net.floodlightcontroller.core.module.IFloodlightModule;
import net.floodlightcontroller.core.module.IFloodlightService;
+import net.floodlightcontroller.restserver.IRestApiService;
-import net.onrc.onos.ofcontroller.flowmanager.IPathComputationService;
+import net.onrc.onos.datagrid.web.DatagridWebRoutable;
+import net.onrc.onos.ofcontroller.flowmanager.IFlowEventHandlerService;
import net.onrc.onos.ofcontroller.topology.TopologyElement;
+import net.onrc.onos.ofcontroller.util.FlowEntry;
+import net.onrc.onos.ofcontroller.util.FlowEntryId;
import net.onrc.onos.ofcontroller.util.FlowId;
import net.onrc.onos.ofcontroller.util.FlowPath;
import net.onrc.onos.ofcontroller.util.serializers.KryoFactory;
@@ -46,20 +50,27 @@
protected final static Logger log = LoggerFactory.getLogger(HazelcastDatagrid.class);
protected IFloodlightProviderService floodlightProvider;
+ protected IRestApiService restApi;
protected static final String HazelcastConfigFile = "datagridConfig";
private HazelcastInstance hazelcastInstance = null;
private Config hazelcastConfig = null;
private KryoFactory kryoFactory = new KryoFactory();
+ private IFlowEventHandlerService flowEventHandlerService = null;
// State related to the Flow map
protected static final String mapFlowName = "mapFlow";
- private IPathComputationService pathComputationService = null;
private IMap<Long, byte[]> mapFlow = null;
private MapFlowListener mapFlowListener = null;
private String mapFlowListenerId = null;
+ // State related to the Flow Entry map
+ protected static final String mapFlowEntryName = "mapFlowEntry";
+ private IMap<Long, byte[]> mapFlowEntry = null;
+ private MapFlowEntryListener mapFlowEntryListener = null;
+ private String mapFlowEntryListenerId = null;
+
// State related to the Network Topology map
protected static final String mapTopologyName = "mapTopology";
private IMap<String, byte[]> mapTopology = null;
@@ -71,7 +82,7 @@
*
* The datagrid map is:
* - Key : Flow ID (Long)
- * - Value : Serialized Flow (byte[])
+ * - Value : Serialized FlowPath (byte[])
*/
class MapFlowListener implements EntryListener<Long, byte[]> {
/**
@@ -90,7 +101,7 @@
Input input = new Input(valueBytes);
FlowPath flowPath = kryo.readObject(input, FlowPath.class);
kryoFactory.deleteKryo(kryo);
- pathComputationService.notificationRecvFlowAdded(flowPath);
+ flowEventHandlerService.notificationRecvFlowAdded(flowPath);
}
/**
@@ -109,7 +120,7 @@
Input input = new Input(valueBytes);
FlowPath flowPath = kryo.readObject(input, FlowPath.class);
kryoFactory.deleteKryo(kryo);
- pathComputationService.notificationRecvFlowRemoved(flowPath);
+ flowEventHandlerService.notificationRecvFlowRemoved(flowPath);
}
/**
@@ -128,7 +139,100 @@
Input input = new Input(valueBytes);
FlowPath flowPath = kryo.readObject(input, FlowPath.class);
kryoFactory.deleteKryo(kryo);
- pathComputationService.notificationRecvFlowUpdated(flowPath);
+ flowEventHandlerService.notificationRecvFlowUpdated(flowPath);
+ }
+
+ /**
+ * Receive a notification that an entry is evicted.
+ *
+ * @param event the notification event for the entry.
+ */
+ public void entryEvicted(EntryEvent event) {
+ // NOTE: We don't use eviction for this map
+ }
+ }
+
+ /**
+ * Class for receiving notifications for FlowEntry state.
+ *
+ * The datagrid map is:
+ * - Key : FlowEntry ID (Long)
+ * - Value : Serialized FlowEntry (byte[])
+ */
+ class MapFlowEntryListener implements EntryListener<Long, byte[]> {
+ /**
+ * Receive a notification that an entry is added.
+ *
+ * @param event the notification event for the entry.
+ */
+ public void entryAdded(EntryEvent event) {
+ //
+ // NOTE: Ignore Flow Entries Events originated by this instance
+ //
+ if (event.getMember().localMember())
+ return;
+
+ Long keyLong = (Long)event.getKey();
+ byte[] valueBytes = (byte[])event.getValue();
+
+ //
+ // Decode the value and deliver the notification
+ //
+ Kryo kryo = kryoFactory.newKryo();
+ Input input = new Input(valueBytes);
+ FlowEntry flowEntry = kryo.readObject(input, FlowEntry.class);
+ kryoFactory.deleteKryo(kryo);
+ flowEventHandlerService.notificationRecvFlowEntryAdded(flowEntry);
+ }
+
+ /**
+ * Receive a notification that an entry is removed.
+ *
+ * @param event the notification event for the entry.
+ */
+ public void entryRemoved(EntryEvent event) {
+ //
+ // NOTE: Ignore Flow Entries Events originated by this instance
+ //
+ if (event.getMember().localMember())
+ return;
+
+ Long keyLong = (Long)event.getKey();
+ byte[] valueBytes = (byte[])event.getValue();
+
+ //
+ // Decode the value and deliver the notification
+ //
+ Kryo kryo = kryoFactory.newKryo();
+ Input input = new Input(valueBytes);
+ FlowEntry flowEntry = kryo.readObject(input, FlowEntry.class);
+ kryoFactory.deleteKryo(kryo);
+ flowEventHandlerService.notificationRecvFlowEntryRemoved(flowEntry);
+ }
+
+ /**
+ * Receive a notification that an entry is updated.
+ *
+ * @param event the notification event for the entry.
+ */
+ public void entryUpdated(EntryEvent event) {
+ //
+ // NOTE: Ignore Flow Entries Events originated by this instance
+ //
+ if (event.getMember().localMember())
+ return;
+
+ Long keyLong = (Long)event.getKey();
+ byte[] valueBytes = (byte[])event.getValue();
+
+ //
+ // Decode the value and deliver the notification
+ //
+ Kryo kryo = kryoFactory.newKryo();
+ Input input = new Input(valueBytes);
+ FlowEntry flowEntry = kryo.readObject(input, FlowEntry.class);
+ kryoFactory.deleteKryo(kryo);
+ flowEventHandlerService.notificationRecvFlowEntryUpdated(flowEntry);
}
/**
@@ -166,7 +270,7 @@
TopologyElement topologyElement =
kryo.readObject(input, TopologyElement.class);
kryoFactory.deleteKryo(kryo);
- pathComputationService.notificationRecvTopologyElementAdded(topologyElement);
+ flowEventHandlerService.notificationRecvTopologyElementAdded(topologyElement);
}
/**
@@ -186,7 +290,7 @@
TopologyElement topologyElement =
kryo.readObject(input, TopologyElement.class);
kryoFactory.deleteKryo(kryo);
- pathComputationService.notificationRecvTopologyElementRemoved(topologyElement);
+ flowEventHandlerService.notificationRecvTopologyElementRemoved(topologyElement);
}
/**
@@ -206,7 +310,7 @@
TopologyElement topologyElement =
kryo.readObject(input, TopologyElement.class);
kryoFactory.deleteKryo(kryo);
- pathComputationService.notificationRecvTopologyElementUpdated(topologyElement);
+ flowEventHandlerService.notificationRecvTopologyElementUpdated(topologyElement);
}
/**
@@ -302,6 +406,7 @@
Collection<Class<? extends IFloodlightService>> l =
new ArrayList<Class<? extends IFloodlightService>>();
l.add(IFloodlightProviderService.class);
+ l.add(IRestApiService.class);
return l;
}
@@ -314,6 +419,7 @@
public void init(FloodlightModuleContext context)
throws FloodlightModuleException {
floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
+ restApi = context.getServiceImpl(IRestApiService.class);
// Get the configuration file name and configure the Datagrid
Map<String, String> configMap = context.getConfigParams(this);
@@ -329,25 +435,32 @@
@Override
public void startUp(FloodlightModuleContext context) {
hazelcastInstance = Hazelcast.newHazelcastInstance(hazelcastConfig);
+
+ restApi.addRestletRoutable(new DatagridWebRoutable());
}
/**
- * Register Path Computation Service for receiving Flow-related
+ * Register Flow Event Handler Service for receiving Flow-related
* notifications.
*
- * NOTE: Only a single Path Computation Service can be registered.
+ * NOTE: Only a single Flow Event Handler Service can be registered.
*
- * @param pathComputationService the Path Computation Service to register.
+ * @param flowEventHandlerService the Flow Event Handler Service to register.
*/
@Override
- public void registerPathComputationService(IPathComputationService pathComputationService) {
- this.pathComputationService = pathComputationService;
+ public void registerFlowEventHandlerService(IFlowEventHandlerService flowEventHandlerService) {
+ this.flowEventHandlerService = flowEventHandlerService;
// Initialize the Flow-related map state
mapFlowListener = new MapFlowListener();
mapFlow = hazelcastInstance.getMap(mapFlowName);
mapFlowListenerId = mapFlow.addEntryListener(mapFlowListener, true);
+ // Initialize the FlowEntry-related map state
+ mapFlowEntryListener = new MapFlowEntryListener();
+ mapFlowEntry = hazelcastInstance.getMap(mapFlowEntryName);
+ mapFlowEntryListenerId = mapFlowEntry.addEntryListener(mapFlowEntryListener, true);
+
// Initialize the Topology-related map state
mapTopologyListener = new MapTopologyListener();
mapTopology = hazelcastInstance.getMap(mapTopologyName);
@@ -355,27 +468,32 @@
}
/**
- * De-register Path Computation Service for receiving Flow-related
+ * De-register Flow Event Handler Service for receiving Flow-related
* notifications.
*
- * NOTE: Only a single Path Computation Service can be registered.
+ * NOTE: Only a single Flow Event Handler Service can be registered.
*
- * @param pathComputationService the Path Computation Service to
+ * @param flowEventHandlerService the Flow Event Handler Service to
* de-register.
*/
@Override
- public void deregisterPathComputationService(IPathComputationService pathComputationService) {
+ public void deregisterFlowEventHandlerService(IFlowEventHandlerService flowEventHandlerService) {
// Clear the Flow-related map state
mapFlow.removeEntryListener(mapFlowListenerId);
mapFlow = null;
mapFlowListener = null;
+ // Clear the FlowEntry-related map state
+ mapFlowEntry.removeEntryListener(mapFlowEntryListenerId);
+ mapFlowEntry = null;
+ mapFlowEntryListener = null;
+
// Clear the Topology-related map state
mapTopology.removeEntryListener(mapTopologyListenerId);
mapTopology = null;
mapTopologyListener = null;
- this.pathComputationService = null;
+ this.flowEventHandlerService = null;
}
/**
@@ -408,7 +526,7 @@
/**
* Send a notification that a Flow is added.
*
- * @param flowPath the flow that is added.
+ * @param flowPath the Flow that is added.
*/
@Override
public void notificationSendFlowAdded(FlowPath flowPath) {
@@ -433,7 +551,7 @@
/**
* Send a notification that a Flow is removed.
*
- * @param flowId the Flow ID of the flow that is removed.
+ * @param flowId the Flow ID of the Flow that is removed.
*/
@Override
public void notificationSendFlowRemoved(FlowId flowId) {
@@ -448,7 +566,7 @@
/**
* Send a notification that a Flow is updated.
*
- * @param flowPath the flow that is updated.
+ * @param flowPath the Flow that is updated.
*/
@Override
public void notificationSendFlowUpdated(FlowPath flowPath) {
@@ -474,6 +592,101 @@
}
/**
+ * Get all Flow Entries that are currently in the datagrid.
+ *
+ * @return all Flow Entries that are currently in the datagrid.
+ */
+ @Override
+ public Collection<FlowEntry> getAllFlowEntries() {
+ Collection<FlowEntry> allFlowEntries = new LinkedList<FlowEntry>();
+
+ //
+ // Get all current entries
+ //
+ Collection<byte[]> values = mapFlowEntry.values();
+ Kryo kryo = kryoFactory.newKryo();
+ for (byte[] valueBytes : values) {
+ //
+ // Decode the value
+ //
+ Input input = new Input(valueBytes);
+ FlowEntry flowEntry = kryo.readObject(input, FlowEntry.class);
+ allFlowEntries.add(flowEntry);
+ }
+ kryoFactory.deleteKryo(kryo);
+
+ return allFlowEntries;
+ }
+
+ /**
+ * Send a notification that a FlowEntry is added.
+ *
+ * @param flowEntry the FlowEntry that is added.
+ */
+ @Override
+ public void notificationSendFlowEntryAdded(FlowEntry flowEntry) {
+ //
+ // Encode the value
+ //
+ byte[] buffer = new byte[MAX_BUFFER_SIZE];
+ Kryo kryo = kryoFactory.newKryo();
+ Output output = new Output(buffer, -1);
+ kryo.writeObject(output, flowEntry);
+ byte[] valueBytes = output.toBytes();
+ kryoFactory.deleteKryo(kryo);
+
+ //
+ // Put the entry:
+ // - Key : FlowEntry ID (Long)
+ // - Value : Serialized FlowEntry (byte[])
+ //
+ mapFlowEntry.putAsync(flowEntry.flowEntryId().value(), valueBytes);
+ }
+
+ /**
+ * Send a notification that a FlowEntry is removed.
+ *
+ * @param flowEntryId the FlowEntry ID of the FlowEntry that is removed.
+ */
+ @Override
+ public void notificationSendFlowEntryRemoved(FlowEntryId flowEntryId) {
+ //
+ // Remove the entry:
+ // - Key : FlowEntry ID (Long)
+ // - Value : Serialized FlowEntry (byte[])
+ //
+ mapFlowEntry.removeAsync(flowEntryId.value());
+ }
+
+ /**
+ * Send a notification that a FlowEntry is updated.
+ *
+ * @param flowEntry the FlowEntry that is updated.
+ */
+ @Override
+ public void notificationSendFlowEntryUpdated(FlowEntry flowEntry) {
+ // NOTE: Adding an entry with an existing key automatically updates it
+ notificationSendFlowEntryAdded(flowEntry);
+ }
+
+ /**
+ * Send a notification that all Flow Entries are removed.
+ */
+ @Override
+ public void notificationSendAllFlowEntriesRemoved() {
+ //
+ // Remove all entries
+ // NOTE: We remove the entries one-by-one so the per-entry
+ // notifications will be delivered.
+ //
+ // mapFlowEntry.clear();
+ Set<Long> keySet = mapFlowEntry.keySet();
+ for (Long key : keySet) {
+ mapFlowEntry.removeAsync(key);
+ }
+ }
+
+ /**
* Get all Topology Elements that are currently in the datagrid.
*
* @return all Topology Elements that are currently in the datagrid.
diff --git a/src/main/java/net/onrc/onos/datagrid/IDatagridService.java b/src/main/java/net/onrc/onos/datagrid/IDatagridService.java
index 10cd1e4..1bcf601 100644
--- a/src/main/java/net/onrc/onos/datagrid/IDatagridService.java
+++ b/src/main/java/net/onrc/onos/datagrid/IDatagridService.java
@@ -4,8 +4,10 @@
import net.floodlightcontroller.core.module.IFloodlightService;
-import net.onrc.onos.ofcontroller.flowmanager.IPathComputationService;
+import net.onrc.onos.ofcontroller.flowmanager.IFlowEventHandlerService;
import net.onrc.onos.ofcontroller.topology.TopologyElement;
+import net.onrc.onos.ofcontroller.util.FlowEntry;
+import net.onrc.onos.ofcontroller.util.FlowEntryId;
import net.onrc.onos.ofcontroller.util.FlowId;
import net.onrc.onos.ofcontroller.util.FlowPath;
@@ -14,25 +16,25 @@
*/
public interface IDatagridService extends IFloodlightService {
/**
- * Register Path Computation Service for receiving Flow-related
+ * Register Flow Event Handler Service for receiving Flow-related
* notifications.
*
- * NOTE: Only a single Path Computation Service can be registered.
+ * NOTE: Only a single Flow Event Handler Service can be registered.
*
- * @param pathComputationService the Path Computation Service to register.
+ * @param flowEventHandlerService the Flow Event Handler Service to register.
*/
- void registerPathComputationService(IPathComputationService pathComputationService);
+ void registerFlowEventHandlerService(IFlowEventHandlerService flowEventHandlerService);
/**
- * De-register Path Computation Service for receiving Flow-related
+ * De-register Flow Event Handler Service for receiving Flow-related
* notifications.
*
- * NOTE: Only a single Path Computation Service can be registered.
+ * NOTE: Only a single Flow Event Handler Service can be registered.
*
- * @param pathComputationService the Path Computation Service to
+ * @param flowEventHandlerService the Flow Event Handler Service to
* de-register.
*/
- void deregisterPathComputationService(IPathComputationService pathComputationService);
+ void deregisterFlowEventHandlerService(IFlowEventHandlerService flowEventHandlerService);
/**
* Get all Flows that are currently in the datagrid.
@@ -44,21 +46,21 @@
/**
* Send a notification that a Flow is added.
*
- * @param flowPath the flow that is added.
+ * @param flowPath the Flow that is added.
*/
void notificationSendFlowAdded(FlowPath flowPath);
/**
* Send a notification that a Flow is removed.
*
- * @param flowId the Flow ID of the flow that is removed.
+ * @param flowId the Flow ID of the Flow that is removed.
*/
void notificationSendFlowRemoved(FlowId flowId);
/**
* Send a notification that a Flow is updated.
*
- * @param flowPath the flow that is updated.
+ * @param flowPath the Flow that is updated.
*/
void notificationSendFlowUpdated(FlowPath flowPath);
@@ -68,6 +70,39 @@
void notificationSendAllFlowsRemoved();
/**
+ * Get all Flow Entries that are currently in the datagrid.
+ *
+ * @return all Flow Entries that are currently in the datagrid.
+ */
+ Collection<FlowEntry> getAllFlowEntries();
+
+ /**
+ * Send a notification that a FlowEntry is added.
+ *
+ * @param flowEntry the FlowEntry that is added.
+ */
+ void notificationSendFlowEntryAdded(FlowEntry flowEntry);
+
+ /**
+ * Send a notification that a FlowEntry is removed.
+ *
+ * @param flowEntryId the FlowEntry ID of the FlowEntry that is removed.
+ */
+ void notificationSendFlowEntryRemoved(FlowEntryId flowEntryId);
+
+ /**
+ * Send a notification that a FlowEntry is updated.
+ *
+ * @param flowEntry the FlowEntry that is updated.
+ */
+ void notificationSendFlowEntryUpdated(FlowEntry flowEntry);
+
+ /**
+ * Send a notification that all Flow Entries are removed.
+ */
+ void notificationSendAllFlowEntriesRemoved();
+
+ /**
* Get all Topology Elements that are currently in the datagrid.
*
* @return all Topology Elements that are currently in the datagrid.
diff --git a/src/main/java/net/onrc/onos/datagrid/web/DatagridWebRoutable.java b/src/main/java/net/onrc/onos/datagrid/web/DatagridWebRoutable.java
new file mode 100644
index 0000000..2c99ece
--- /dev/null
+++ b/src/main/java/net/onrc/onos/datagrid/web/DatagridWebRoutable.java
@@ -0,0 +1,30 @@
+package net.onrc.onos.datagrid.web;
+
+import net.floodlightcontroller.restserver.RestletRoutable;
+
+import org.restlet.Context;
+import org.restlet.Restlet;
+import org.restlet.routing.Router;
+
+/**
+ * REST API implementation for the Datagrid.
+ */
+public class DatagridWebRoutable implements RestletRoutable {
+ /**
+ * Create the Restlet router and bind to the proper resources.
+ */
+ @Override
+ public Restlet getRestlet(Context context) {
+ Router router = new Router(context);
+ router.attach("/get/map/{map-name}/json", GetMapResource.class);
+ return router;
+ }
+
+ /**
+ * Set the base path for the Topology
+ */
+ @Override
+ public String basePath() {
+ return "/wm/datagrid";
+ }
+}
diff --git a/src/main/java/net/onrc/onos/datagrid/web/GetMapResource.java b/src/main/java/net/onrc/onos/datagrid/web/GetMapResource.java
new file mode 100644
index 0000000..124ac28
--- /dev/null
+++ b/src/main/java/net/onrc/onos/datagrid/web/GetMapResource.java
@@ -0,0 +1,84 @@
+package net.onrc.onos.datagrid.web;
+
+import java.util.Collection;
+
+import net.onrc.onos.datagrid.IDatagridService;
+import net.onrc.onos.ofcontroller.topology.TopologyElement;
+import net.onrc.onos.ofcontroller.util.FlowEntry;
+import net.onrc.onos.ofcontroller.util.FlowPath;
+
+import org.restlet.resource.Get;
+import org.restlet.resource.ServerResource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Datagrid REST API implementation: Get the state of a map.
+ *
+ * Valid map names:
+ * - "all" : Get all maps
+ * - "flow" : Get the Flows
+ * - "flow-entry" : Get the Flow Entries
+ * - "topology" : Get the Topology
+ *
+ * GET /wm/datagrid/get/map/{map-name}/json
+ */
+public class GetMapResource extends ServerResource {
+ protected final static Logger log = LoggerFactory.getLogger(GetMapResource.class);
+
+ /**
+ * Implement the API.
+ *
+ * @return a string with the state of the map(s).
+ */
+ @Get("json")
+ public String retrieve() {
+ String result = "";
+
+ IDatagridService datagridService =
+ (IDatagridService)getContext().getAttributes().
+ get(IDatagridService.class.getCanonicalName());
+
+ if (datagridService == null) {
+ log.debug("ONOS Datagrid Service not found");
+ return result;
+ }
+
+ // Extract the arguments
+ String mapNameStr = (String)getRequestAttributes().get("map-name");
+
+ log.debug("Get Datagrid Map: " + mapNameStr);
+
+ //
+ // Get the Flows
+ //
+ if (mapNameStr.equals("flow") || mapNameStr.equals("all")) {
+ Collection<FlowPath> flowPaths = datagridService.getAllFlows();
+ result += "Flows:\n";
+ for (FlowPath flowPath : flowPaths) {
+ result += flowPath.toString() + "\n";
+ }
+ }
+
+ //
+ // Get the Flow Entries
+ //
+ if (mapNameStr.equals("flow-entry") || mapNameStr.equals("all")) {
+ Collection<FlowEntry> flowEntries = datagridService.getAllFlowEntries();
+ result += "Flow Entries:\n";
+ for (FlowEntry flowEntry : flowEntries) {
+ result += flowEntry.toString() + "\n";
+ }
+ }
+
+ if (mapNameStr.equals("topology") || mapNameStr.equals("all")) {
+ Collection<TopologyElement> topologyElements = datagridService.getAllTopologyElements();
+ result += "Topology:\n";
+ for (TopologyElement topologyElement : topologyElements) {
+ result += topologyElement.toString() + "\n";
+ }
+ }
+
+ return result;
+ }
+}
diff --git a/src/main/java/net/onrc/onos/graph/GraphDBConnection.java b/src/main/java/net/onrc/onos/graph/GraphDBConnection.java
index 232deed..bf30297 100644
--- a/src/main/java/net/onrc/onos/graph/GraphDBConnection.java
+++ b/src/main/java/net/onrc/onos/graph/GraphDBConnection.java
@@ -7,7 +7,6 @@
import com.thinkaurelius.titan.core.TitanFactory;
import com.thinkaurelius.titan.core.TitanGraph;
-import com.thinkaurelius.titan.diskstorage.StorageException;
import com.tinkerpop.blueprints.TransactionalGraph;
import com.tinkerpop.blueprints.Vertex;
import com.tinkerpop.blueprints.util.wrappers.event.EventTransactionalGraph;
@@ -82,6 +81,9 @@
if (!s.contains("switch_state")) {
graph.createKeyIndex("switch_state", Vertex.class);
}
+ if (!s.contains("ipv4_address")) {
+ graph.createKeyIndex("ipv4_address", Vertex.class);
+ }
graph.commit();
eg = new EventTransactionalGraph<TitanGraph>(graph);
}
diff --git a/src/main/java/net/onrc/onos/graph/GraphDBOperation.java b/src/main/java/net/onrc/onos/graph/GraphDBOperation.java
index f1e9b46..bfd9046 100644
--- a/src/main/java/net/onrc/onos/graph/GraphDBOperation.java
+++ b/src/main/java/net/onrc/onos/graph/GraphDBOperation.java
@@ -1,11 +1,14 @@
package net.onrc.onos.graph;
import java.util.ArrayList;
+import java.util.Iterator;
import java.util.List;
+import net.onrc.onos.ofcontroller.core.INetMapTopologyObjects.IBaseObject;
import net.onrc.onos.ofcontroller.core.INetMapTopologyObjects.IDeviceObject;
import net.onrc.onos.ofcontroller.core.INetMapTopologyObjects.IFlowEntry;
import net.onrc.onos.ofcontroller.core.INetMapTopologyObjects.IFlowPath;
+import net.onrc.onos.ofcontroller.core.INetMapTopologyObjects.IIpv4Address;
import net.onrc.onos.ofcontroller.core.INetMapTopologyObjects.IPortObject;
import net.onrc.onos.ofcontroller.core.INetMapTopologyObjects.ISwitchObject;
import net.onrc.onos.ofcontroller.core.ISwitchStorage.SwitchState;
@@ -225,6 +228,49 @@
FramedGraph<TitanGraph> fg = conn.getFramedGraph();
if (fg != null) fg.removeVertex(dev.asVertex());
}
+
+ public IIpv4Address newIpv4Address() {
+ return newVertex("ipv4Address", IIpv4Address.class);
+ }
+
+ private <T extends IBaseObject> T newVertex(String type, Class<T> vertexType) {
+ FramedGraph<TitanGraph> fg = conn.getFramedGraph();
+ T newVertex = fg.addVertex(null, vertexType);
+ if (newVertex != null) {
+ newVertex.setType(type);
+ }
+ return newVertex;
+ }
+
+ public IIpv4Address searchIpv4Address(int intIpv4Address) {
+ return searchForVertex("ipv4_address", intIpv4Address, IIpv4Address.class);
+ }
+
+ private <T> T searchForVertex(String propertyName, Object propertyValue, Class<T> vertexType) {
+ FramedGraph<TitanGraph> fg = conn.getFramedGraph();
+ if (fg != null) {
+ Iterator<T> it =
+ fg.getVertices(propertyName, propertyValue, vertexType).iterator();
+ if (it.hasNext()) {
+ return it.next();
+ }
+ }
+ return null;
+ }
+
+ public IIpv4Address ensureIpv4Address(int intIpv4Address) {
+ IIpv4Address ipv4Vertex = searchIpv4Address(intIpv4Address);
+ if (ipv4Vertex == null) {
+ ipv4Vertex = newIpv4Address();
+ ipv4Vertex.setIpv4Address(intIpv4Address);
+ }
+ return ipv4Vertex;
+ }
+
+ public void removeIpv4Address(IIpv4Address ipv4Address) {
+ FramedGraph<TitanGraph> fg = conn.getFramedGraph();
+ fg.removeVertex(ipv4Address.asVertex());
+ }
/**
* Create and return a flow path object.
diff --git a/src/main/java/net/onrc/onos/ofcontroller/bgproute/BgpRoute.java b/src/main/java/net/onrc/onos/ofcontroller/bgproute/BgpRoute.java
index 813f095..33280a6 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/bgproute/BgpRoute.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/bgproute/BgpRoute.java
@@ -34,13 +34,14 @@
import net.floodlightcontroller.util.MACAddress;
import net.onrc.onos.ofcontroller.bgproute.RibUpdate.Operation;
import net.onrc.onos.ofcontroller.core.INetMapTopologyService.ITopoLinkService;
+import net.onrc.onos.ofcontroller.core.config.IConfigInfoService;
import net.onrc.onos.ofcontroller.core.internal.TopoLinkServiceImpl;
import net.onrc.onos.ofcontroller.linkdiscovery.ILinkDiscovery;
import net.onrc.onos.ofcontroller.linkdiscovery.ILinkDiscovery.LDUpdate;
import net.onrc.onos.ofcontroller.linkdiscovery.ILinkDiscoveryService;
+import net.onrc.onos.ofcontroller.proxyarp.BgpProxyArpManager;
import net.onrc.onos.ofcontroller.proxyarp.IArpRequester;
import net.onrc.onos.ofcontroller.proxyarp.IProxyArpService;
-import net.onrc.onos.ofcontroller.proxyarp.ProxyArpManager;
import net.onrc.onos.ofcontroller.topology.ITopologyNetService;
import net.onrc.onos.ofcontroller.topology.Topology;
import net.onrc.onos.ofcontroller.topology.TopologyManager;
@@ -77,7 +78,7 @@
public class BgpRoute implements IFloodlightModule, IBgpRouteService,
ITopologyListener, IArpRequester,
- IOFSwitchListener, ILayer3InfoService,
+ IOFSwitchListener, IConfigInfoService,
IProxyArpService {
private final static Logger log = LoggerFactory.getLogger(BgpRoute.class);
@@ -88,7 +89,7 @@
private ILinkDiscoveryService linkDiscoveryService;
private IRestApiService restApi;
- private ProxyArpManager proxyArp;
+ private BgpProxyArpManager proxyArp;
private IPatriciaTrie<RibEntry> ptree;
private IPatriciaTrie<Interface> interfacePtrie;
@@ -122,6 +123,7 @@
private Map<InetAddress, BgpPeer> bgpPeers;
private SwitchPort bgpdAttachmentPoint;
private MACAddress bgpdMacAddress;
+ private short vlan;
//True when all switches have connected
private volatile boolean switchesConnected = false;
@@ -183,8 +185,8 @@
}
}
- private void readGatewaysConfiguration(String gatewaysFilename){
- File gatewaysFile = new File(gatewaysFilename);
+ private void readConfiguration(String configFilename){
+ File gatewaysFile = new File(configFilename);
ObjectMapper mapper = new ObjectMapper();
try {
@@ -205,6 +207,7 @@
new Port(config.getBgpdAttachmentPort()));
bgpdMacAddress = config.getBgpdMacAddress();
+ vlan = config.getVlan();
} catch (JsonParseException e) {
log.error("Error in JSON file", e);
System.exit(1);
@@ -228,6 +231,7 @@
Collection<Class<? extends IFloodlightService>> l
= new ArrayList<Class<? extends IFloodlightService>>();
l.add(IBgpRouteService.class);
+ l.add(IConfigInfoService.class);
return l;
}
@@ -236,7 +240,7 @@
Map<Class<? extends IFloodlightService>, IFloodlightService> m
= new HashMap<Class<? extends IFloodlightService>, IFloodlightService>();
m.put(IBgpRouteService.class, this);
- m.put(IProxyArpService.class, this);
+ m.put(IConfigInfoService.class, this);
return m;
}
@@ -267,7 +271,10 @@
//TODO We'll initialise this here for now, but it should really be done as
//part of the controller core
- proxyArp = new ProxyArpManager(floodlightProvider, topologyService, this, restApi);
+ //proxyArp = new ProxyArpManager(floodlightProvider, topologyService, this, restApi);
+ proxyArp = new BgpProxyArpManager();
+ proxyArp.init(floodlightProvider, topologyService, this, restApi);
+ //proxyArp = context.getServiceImpl(IProxyArpService.class);
linkUpdates = new ArrayList<LDUpdate>();
ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
@@ -313,7 +320,7 @@
}
log.debug("Config file set to {}", configFilename);
- readGatewaysConfiguration(configFilename);
+ readConfiguration(configFilename);
}
@Override
@@ -324,7 +331,7 @@
proxyArp.startUp();
- floodlightProvider.addOFMessageListener(OFType.PACKET_IN, proxyArp);
+ //floodlightProvider.addOFMessageListener(OFType.PACKET_IN, proxyArp);
//Retrieve the RIB from BGPd during startup
retrieveRib();
@@ -491,7 +498,7 @@
Map<Long, Interface> srcInterfaces = new HashMap<Long, Interface>();
for (Interface intf : interfaces.values()) {
if (!srcInterfaces.containsKey(intf.getDpid())
- && intf != egressInterface) {
+ && !intf.equals(egressInterface)) {
srcInterfaces.put(intf.getDpid(), intf);
}
}
@@ -693,7 +700,7 @@
List<PushedFlowMod> pushedFlows = new ArrayList<PushedFlowMod>();
for (Interface srcInterface : interfaces.values()) {
- if (dstInterface.getName().equals(srcInterface.getName())){
+ if (dstInterface.equals(srcInterface)){
continue;
}
@@ -1083,7 +1090,7 @@
private void checkTopologyReady(){
for (Interface dstInterface : interfaces.values()) {
for (Interface srcInterface : interfaces.values()) {
- if (dstInterface == srcInterface) {
+ if (dstInterface.equals(srcInterface)) {
continue;
}
@@ -1236,7 +1243,7 @@
}
/*
- * ILayer3InfoService methods
+ * IConfigInfoService methods
*/
@Override
@@ -1275,6 +1282,11 @@
public MACAddress getRouterMacAddress() {
return bgpdMacAddress;
}
+
+ @Override
+ public short getVlan() {
+ return vlan;
+ }
/*
* TODO This is a hack to get the REST API to work for ProxyArpManager.
diff --git a/src/main/java/net/onrc/onos/ofcontroller/bgproute/Configuration.java b/src/main/java/net/onrc/onos/ofcontroller/bgproute/Configuration.java
index 1d90edc..4c81d1b 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/bgproute/Configuration.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/bgproute/Configuration.java
@@ -12,6 +12,7 @@
private long bgpdAttachmentDpid;
private short bgpdAttachmentPort;
private MACAddress bgpdMacAddress;
+ private short vlan;
private List<String> switches;
private List<Interface> interfaces;
private List<BgpPeer> peers;
@@ -46,16 +47,25 @@
public void setBgpdMacAddress(String strMacAddress) {
this.bgpdMacAddress = MACAddress.valueOf(strMacAddress);
}
-
+
public List<String> getSwitches() {
return Collections.unmodifiableList(switches);
}
+
+ @JsonProperty("vlan")
+ public void setVlan(short vlan) {
+ this.vlan = vlan;
+ }
+
+ public short getVlan() {
+ return vlan;
+ }
@JsonProperty("switches")
public void setSwitches(List<String> switches) {
this.switches = switches;
}
-
+
public List<Interface> getInterfaces() {
return Collections.unmodifiableList(interfaces);
}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/bgproute/Interface.java b/src/main/java/net/onrc/onos/ofcontroller/bgproute/Interface.java
index 48b60d8..5db8f0a 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/bgproute/Interface.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/bgproute/Interface.java
@@ -59,4 +59,31 @@
public int getPrefixLength() {
return prefixLength;
}
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == null || !(other instanceof Interface)) {
+ return false;
+ }
+
+ Interface otherInterface = (Interface)other;
+
+ //Don't check switchPort as it's comprised of dpid and port
+ return (name.equals(otherInterface.name)) &&
+ (dpid == otherInterface.dpid) &&
+ (port == otherInterface.port) &&
+ (ipAddress.equals(otherInterface.ipAddress)) &&
+ (prefixLength == otherInterface.prefixLength);
+ }
+
+ @Override
+ public int hashCode() {
+ int hash = 17;
+ hash = 31 * hash + name.hashCode();
+ hash = 31 * hash + (int)(dpid ^ dpid >>> 32);
+ hash = 31 * hash + (int)port;
+ hash = 31 * hash + ipAddress.hashCode();
+ hash = 31 * hash + prefixLength;
+ return hash;
+ }
}
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 7310d8c..be495b9 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/core/IDeviceStorage.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/core/IDeviceStorage.java
@@ -9,7 +9,7 @@
public IDeviceObject updateDevice(IDevice device);
public void removeDevice(IDevice device);
public IDeviceObject getDeviceByMac(String mac);
- public IDeviceObject getDeviceByIP(String ip);
+ public IDeviceObject getDeviceByIP(int ipv4Address);
public void changeDeviceAttachments(IDevice device);
public void changeDeviceIPv4Address(IDevice device);
}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/core/ILinkStorage.java b/src/main/java/net/onrc/onos/ofcontroller/core/ILinkStorage.java
index 483fbda..8889092 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/core/ILinkStorage.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/core/ILinkStorage.java
@@ -43,7 +43,25 @@
* If only dpid is set all links associated with Switch are retrieved
*/
public List<Link> getLinks(Long dpid, short port);
+
+ /**
+ * Get list of all reverse links connected to the port specified by given DPID and port number.
+ * @param dpid DPID of desired port.
+ * @param port Port number of desired port.
+ * @return List of reverse links. Empty list if no port was found.
+ */
+ public List<Link> getReverseLinks(Long dpid, short port);
+
public List<Link> getLinks(String dpid);
+
+ /**
+ * Get list of all reverse links connected to the switch specified by
+ * given DPID.
+ * @param dpid DPID of desired switch.
+ * @return List of reverse links. Empty list if no port was found.
+ */
+ public List<Link> getReverseLinks(String dpid);
+
public List<Link> getActiveLinks();
public LinkInfo getLinkInfo(Link link);
diff --git a/src/main/java/net/onrc/onos/ofcontroller/core/INetMapTopologyObjects.java b/src/main/java/net/onrc/onos/ofcontroller/core/INetMapTopologyObjects.java
index 6f13080..869333b 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/core/INetMapTopologyObjects.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/core/INetMapTopologyObjects.java
@@ -21,24 +21,24 @@
*/
public interface INetMapTopologyObjects {
-public interface IBaseObject extends VertexFrame {
+ public interface IBaseObject extends VertexFrame {
+
+ @JsonProperty("state")
+ @Property("state")
+ public String getState();
+
+ @Property("state")
+ public void setState(final String state);
+
+ @JsonIgnore
+ @Property("type")
+ public String getType();
+ @Property("type")
+ public void setType(final String type);
+
+ }
- @JsonProperty("state")
- @Property("state")
- public String getState();
-
- @Property("state")
- public void setState(final String state);
-
- @JsonIgnore
- @Property("type")
- public String getType();
- @Property("type")
- public void setType(final String type);
-
-}
-
-public interface ISwitchObject extends IBaseObject{
+ public interface ISwitchObject extends IBaseObject{
@JsonProperty("dpid")
@Property("dpid")
@@ -51,7 +51,7 @@
@Adjacency(label="on")
public Iterable<IPortObject> getPorts();
-// Requires Frames 2.3.0
+ // Requires Frames 2.3.0
@JsonIgnore
@GremlinGroovy("it.out('on').has('number',port_num)")
public IPortObject getPort(@GremlinParam("port_num") final short port_num);
@@ -104,7 +104,6 @@
public void setPortState(Integer s);
@JsonIgnore
-// @GremlinGroovy("it.in('on')")
@Adjacency(label="on",direction = Direction.IN)
public ISwitchObject getSwitch();
@@ -129,6 +128,10 @@
@JsonIgnore
@Adjacency(label="link")
public Iterable<IPortObject> getLinkedPorts();
+
+ @JsonIgnore
+ @Adjacency(label="link",direction = Direction.IN)
+ public Iterable<IPortObject> getReverseLinkedPorts();
@Adjacency(label="link")
public void removeLink(final IPortObject dest_port);
@@ -136,9 +139,9 @@
@Adjacency(label="link")
public void setLinkPort(final IPortObject dest_port);
-// @JsonIgnore
-// @Adjacency(label="link")
-// public Iterable<ILinkObject> getLinks();
+ // @JsonIgnore
+ // @Adjacency(label="link")
+ // public Iterable<ILinkObject> getLinks();
}
public interface IDeviceObject extends IBaseObject {
@@ -146,15 +149,10 @@
@JsonProperty("mac")
@Property("dl_addr")
public String getMACAddress();
+
@Property("dl_addr")
public void setMACAddress(String macaddr);
- @JsonProperty("ipv4")
- @Property("nw_addr")
- public String getIPAddress();
- @Property("nw_addr")
- public void setIPAddress(String ipaddr);
-
@JsonIgnore
@Adjacency(label="host",direction = Direction.IN)
public Iterable<IPortObject> getAttachedPorts();
@@ -171,6 +169,23 @@
@GremlinGroovy("it.in('host').in('on')")
public Iterable<ISwitchObject> getSwitch();
+ //
+ // IPv4 Addresses
+ //
+ @JsonProperty("ipv4addresses")
+ @Adjacency(label="hasAddress")
+ public Iterable<IIpv4Address> getIpv4Addresses();
+
+ @JsonIgnore
+ @GremlinGroovy("it.out('hasAddress').has('ipv4_address', ipv4Address)")
+ public IIpv4Address getIpv4Address(@GremlinParam("ipv4Address") final int ipv4Address);
+
+ @Adjacency(label="hasAddress")
+ public void addIpv4Address(final IIpv4Address ipv4Address);
+
+ @Adjacency(label="hasAddress")
+ public void removeIpv4Address(final IIpv4Address ipv4Address);
+
/* @JsonProperty("dpid")
@GremlinGroovy("_().in('host').in('on').next().getProperty('dpid')")
public Iterable<String> getSwitchDPID();
@@ -183,8 +198,22 @@
@GremlinGroovy("_().in('host').in('on').path(){it.number}{it.dpid}")
public Iterable<SwitchPort> getAttachmentPoints();*/
}
-
-public interface IFlowPath extends IBaseObject {
+
+ public interface IIpv4Address extends IBaseObject {
+
+ @JsonProperty("ipv4")
+ @Property("ipv4_address")
+ public int getIpv4Address();
+
+ @Property("ipv4_address")
+ public void setIpv4Address(int ipv4Address);
+
+ @JsonIgnore
+ @GremlinGroovy("it.in('hasAddress')")
+ public IDeviceObject getDevice();
+ }
+
+ public interface IFlowPath extends IBaseObject {
@JsonProperty("flowId")
@Property("flow_id")
public String getFlowId();
@@ -367,7 +396,7 @@
public String getState();
}
-public interface IFlowEntry extends IBaseObject {
+ public interface IFlowEntry extends IBaseObject {
@Property("flow_entry_id")
public String getFlowEntryId();
diff --git a/src/main/java/net/onrc/onos/ofcontroller/core/ISwitchStorage.java b/src/main/java/net/onrc/onos/ofcontroller/core/ISwitchStorage.java
index b7825f9..2cfab3f 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/core/ISwitchStorage.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/core/ISwitchStorage.java
@@ -1,5 +1,7 @@
package net.onrc.onos.ofcontroller.core;
+import java.util.List;
+
import net.floodlightcontroller.core.IOFSwitch;
import org.openflow.protocol.OFPhysicalPort;
@@ -32,6 +34,10 @@
*/
public boolean deleteSwitch(String dpid);
/*
+ * Deactivate the switch and associated ports
+ */
+ public boolean deactivateSwitch(String dpid);
+ /*
* Update the port details
*/
public boolean updatePort(String dpid, short port, int state, String desc);
@@ -43,4 +49,12 @@
* Delete a port on a switch by num
*/
public boolean deletePort(String dpid, short port);
+
+ /**
+ * Get list of all ports on the switch specified by given DPID.
+ *
+ * @param dpid DPID of desired switch.
+ * @return List of port IDs. Empty list if no port was found.
+ */
+ public List<Short> getPorts(String dpid);
}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/core/config/DefaultConfiguration.java b/src/main/java/net/onrc/onos/ofcontroller/core/config/DefaultConfiguration.java
new file mode 100644
index 0000000..d9a291f
--- /dev/null
+++ b/src/main/java/net/onrc/onos/ofcontroller/core/config/DefaultConfiguration.java
@@ -0,0 +1,47 @@
+package net.onrc.onos.ofcontroller.core.config;
+
+import java.net.InetAddress;
+
+import net.floodlightcontroller.util.MACAddress;
+import net.onrc.onos.ofcontroller.bgproute.Interface;
+
+import org.openflow.util.HexString;
+
+public class DefaultConfiguration implements IConfigInfoService {
+
+ @Override
+ public boolean isInterfaceAddress(InetAddress address) {
+ return false;
+ }
+
+ @Override
+ public boolean inConnectedNetwork(InetAddress address) {
+ return false;
+ }
+
+ @Override
+ public boolean fromExternalNetwork(long inDpid, short inPort) {
+ return false;
+ }
+
+ @Override
+ public Interface getOutgoingInterface(InetAddress dstIpAddress) {
+ return null;
+ }
+
+ @Override
+ public boolean hasLayer3Configuration() {
+ return false;
+ }
+
+ @Override
+ public MACAddress getRouterMacAddress() {
+ return MACAddress.valueOf(HexString.fromHexString("000000000001"));
+ }
+
+ @Override
+ public short getVlan() {
+ return 0;
+ }
+
+}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/bgproute/ILayer3InfoService.java b/src/main/java/net/onrc/onos/ofcontroller/core/config/IConfigInfoService.java
similarity index 67%
rename from src/main/java/net/onrc/onos/ofcontroller/bgproute/ILayer3InfoService.java
rename to src/main/java/net/onrc/onos/ofcontroller/core/config/IConfigInfoService.java
index 00ddd68..7bbf483 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/bgproute/ILayer3InfoService.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/core/config/IConfigInfoService.java
@@ -1,15 +1,17 @@
-package net.onrc.onos.ofcontroller.bgproute;
+package net.onrc.onos.ofcontroller.core.config;
import java.net.InetAddress;
import net.floodlightcontroller.util.MACAddress;
+import net.onrc.onos.ofcontroller.bgproute.Interface;
+import net.onrc.onos.ofcontroller.core.module.IOnosService;
/**
* Provides information about the layer 3 properties of the network.
* This is based on IP addresses configured on ports in the network.
*
*/
-public interface ILayer3InfoService {
+public interface IConfigInfoService extends IOnosService {
public boolean isInterfaceAddress(InetAddress address);
public boolean inConnectedNetwork(InetAddress address);
public boolean fromExternalNetwork(long inDpid, short inPort);
@@ -32,4 +34,15 @@
public boolean hasLayer3Configuration();
public MACAddress getRouterMacAddress();
+
+ /**
+ * We currently have basic vlan support for the situation when the contr
+ * is running within a single vlan. In this case, packets sent from the
+ * controller (e.g. ARP) need to be tagged with that vlan.
+ * @return The vlan id configured in the config file,
+ * or 0 if no vlan is configured.
+ */
+ public short getVlan();
+
+
}
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 63eaacb..cfc411c 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
@@ -1,30 +1,31 @@
package net.onrc.onos.ofcontroller.core.internal;
-import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
+
+import net.floodlightcontroller.devicemanager.IDevice;
+import net.floodlightcontroller.devicemanager.SwitchPort;
+import net.onrc.onos.graph.GraphDBOperation;
+import net.onrc.onos.ofcontroller.core.IDeviceStorage;
+import net.onrc.onos.ofcontroller.core.INetMapTopologyObjects.IDeviceObject;
+import net.onrc.onos.ofcontroller.core.INetMapTopologyObjects.IIpv4Address;
+import net.onrc.onos.ofcontroller.core.INetMapTopologyObjects.IPortObject;
+
import org.openflow.util.HexString;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.collect.Lists;
import com.thinkaurelius.titan.core.TitanException;
-import net.floodlightcontroller.devicemanager.IDevice;
-import net.floodlightcontroller.devicemanager.SwitchPort;
-import net.floodlightcontroller.packet.IPv4;
-import net.onrc.onos.graph.GraphDBOperation;
-import net.onrc.onos.ofcontroller.core.IDeviceStorage;
-import net.onrc.onos.ofcontroller.core.INetMapTopologyObjects.IDeviceObject;
-import net.onrc.onos.ofcontroller.core.INetMapTopologyObjects.IPortObject;
-import net.onrc.onos.ofcontroller.core.internal.SwitchStorageImpl;
/**
* This is the class for storing the information of devices into CassandraDB
* @author Pankaj
*/
public class DeviceStorageImpl implements IDeviceStorage {
+ protected final static Logger log = LoggerFactory.getLogger(SwitchStorageImpl.class);
private GraphDBOperation ope;
- protected final static Logger log = LoggerFactory.getLogger(SwitchStorageImpl.class);
/***
* Initialize function. Before you use this class, please call this method
@@ -32,10 +33,10 @@
*/
@Override
public void init(String conf) {
- try{
+ try {
ope = new GraphDBOperation(conf);
- } catch(Exception e) {
- log.error(e.getMessage());
+ } catch (TitanException e) {
+ log.error("Couldn't open graph operation", e);
}
}
@@ -67,39 +68,29 @@
IDeviceObject obj = null;
try {
if ((obj = ope.searchDevice(device.getMACAddressString())) != null) {
- log.debug("Adding device {}: found existing device",device.getMACAddressString());
+ log.debug("Adding device {}: found existing device", device.getMACAddressString());
} else {
obj = ope.newDevice();
- log.debug("Adding device {}: creating new device",device.getMACAddressString());
+ log.debug("Adding device {}: creating new device", device.getMACAddressString());
}
- changeDeviceAttachments(device, obj);
-
- String multiIntString = "";
- for(Integer intValue : device.getIPv4Addresses()) {
- if (multiIntString == null || multiIntString.isEmpty()){
- multiIntString = IPv4.fromIPv4Address(intValue);
- multiIntString = "[" + IPv4.fromIPv4Address(intValue);
- }else{
- multiIntString += "," + IPv4.fromIPv4Address(intValue);
- }
- }
-
- if(multiIntString.toString() != null && !multiIntString.toString().isEmpty()){
- obj.setIPAddress(multiIntString + "]");
- }
-
- obj.setMACAddress(device.getMACAddressString());
- obj.setType("device");
- obj.setState("ACTIVE");
- ope.commit();
- log.debug("Adding device {}",device.getMACAddressString());
- } catch (Exception e) {
+ changeDeviceAttachments(device, obj);
+
+ changeDeviceIpv4Addresses(device, obj);
+
+ obj.setMACAddress(device.getMACAddressString());
+ obj.setType("device");
+ obj.setState("ACTIVE");
+ ope.commit();
+
+ //log.debug("Adding device {}",device.getMACAddressString());
+ } catch (TitanException e) {
ope.rollback();
- log.error(":addDevice mac:{} failed", device.getMACAddressString());
+ log.error("Adding device {} failed", device.getMACAddressString(), e);
obj = null;
- }
- return obj;
+ }
+
+ return obj;
}
/***
@@ -121,11 +112,15 @@
IDeviceObject dev;
try {
if ((dev = ope.searchDevice(device.getMACAddressString())) != null) {
+ for (IIpv4Address ipv4AddressVertex : dev.getIpv4Addresses()) {
+ ope.removeIpv4Address(ipv4AddressVertex);
+ }
+
ope.removeDevice(dev);
ope.commit();
log.error("DeviceStorage:removeDevice mac:{} done", device.getMACAddressString());
}
- } catch (Exception e) {
+ } catch (TitanException e) {
ope.rollback();
log.error("DeviceStorage:removeDevice mac:{} failed", device.getMACAddressString());
}
@@ -147,23 +142,15 @@
* @return IDeviceObject you want to get.
*/
@Override
- public IDeviceObject getDeviceByIP(String ip) {
- try
- {
- for(IDeviceObject dev : ope.getDevices()){
- String ips;
- if((ips = dev.getIPAddress()) != null){
- String nw_addr_wob = ips.replace("[", "").replace("]", "");
- ArrayList<String> iplists = Lists.newArrayList(nw_addr_wob.split(","));
- if(iplists.contains(ip)){
- return dev;
- }
- }
+ public IDeviceObject getDeviceByIP(int ipv4Address) {
+ try {
+ IIpv4Address ipv4AddressVertex = ope.searchIpv4Address(ipv4Address);
+ if (ipv4AddressVertex != null) {
+ return ipv4AddressVertex.getDevice();
}
return null;
}
- catch (Exception e)
- {
+ catch (TitanException e) {
log.error("DeviceStorage:getDeviceByIP:{} failed");
return null;
}
@@ -178,14 +165,14 @@
IDeviceObject obj = null;
try {
if ((obj = ope.searchDevice(device.getMACAddressString())) != null) {
- log.debug("Changing device ports {}: found existing device",device.getMACAddressString());
+ log.debug("Changing device ports {}: found existing device", device.getMACAddressString());
changeDeviceAttachments(device, obj);
ope.commit();
} else {
- log.debug("failed to search device...now adding {}",device.getMACAddressString());
+ log.debug("failed to search device...now adding {}", device.getMACAddressString());
addDevice(device);
- }
- } catch (Exception e) {
+ }
+ } catch (TitanException e) {
ope.rollback();
log.error(":addDevice mac:{} failed", device.getMACAddressString());
}
@@ -199,33 +186,33 @@
public void changeDeviceAttachments(IDevice device, IDeviceObject obj) {
SwitchPort[] attachmentPoints = device.getAttachmentPoints();
List<IPortObject> attachedPorts = Lists.newArrayList(obj.getAttachedPorts());
-
- for (SwitchPort ap : attachmentPoints) {
- //Check weather there is the port
- IPortObject port = ope.searchPort( HexString.toHexString(ap.getSwitchDPID()),
- (short) ap.getPort());
- log.debug("New Switch Port is {},{}", HexString.toHexString(ap.getSwitchDPID()),(short) ap.getPort());
-
- if(port != null){
- if(attachedPorts.contains(port))
- {
- log.debug("This is the port you already attached {}: do nothing",device.getMACAddressString());
- //This port will be remained, so remove from the removed port lists.
- attachedPorts.remove(port);
- } else {
- log.debug("Adding device {}: attaching to port",device.getMACAddressString());
- port.setDevice(obj);
- }
-
- log.debug("port number is {}", port.getNumber().toString());
- log.debug("port desc is {}", port.getDesc());
- }
- }
-
- for (IPortObject port: attachedPorts) {
- log.debug("Detouching the device {}: detouching from port",device.getMACAddressString());
- port.removeDevice(obj);
- }
+
+ for (SwitchPort ap : attachmentPoints) {
+ //Check if there is the port
+ IPortObject port = ope.searchPort(HexString.toHexString(ap.getSwitchDPID()),
+ (short) ap.getPort());
+ log.debug("New Switch Port is {},{}",
+ HexString.toHexString(ap.getSwitchDPID()), (short) ap.getPort());
+
+ if (port != null){
+ if (attachedPorts.contains(port)) {
+ log.debug("This is the port you already attached {}: do nothing", device.getMACAddressString());
+ //This port will be remained, so remove from the removed port lists.
+ attachedPorts.remove(port);
+ } else {
+ log.debug("Adding device {}: attaching to port", device.getMACAddressString());
+ port.setDevice(obj);
+ }
+
+ log.debug("port number is {}", port.getNumber().toString());
+ log.debug("port desc is {}", port.getDesc());
+ }
+ }
+
+ for (IPortObject port: attachedPorts) {
+ log.debug("Detaching the device {}: detaching from port", device.getMACAddressString());
+ port.removeDevice(obj);
+ }
}
/***
@@ -234,22 +221,12 @@
*/
@Override
public void changeDeviceIPv4Address(IDevice device) {
+ log.debug("Changing IP address for {} to {}", device.getMACAddressString(),
+ device.getIPv4Addresses());
IDeviceObject obj;
try {
if ((obj = ope.searchDevice(device.getMACAddressString())) != null) {
-
- String multiIntString = "";
- for(Integer intValue : device.getIPv4Addresses()){
- if (multiIntString == null || multiIntString.isEmpty()){
- multiIntString = "[" + IPv4.fromIPv4Address(intValue);
- } else {
- multiIntString += "," + IPv4.fromIPv4Address(intValue);
- }
- }
-
- if(multiIntString != null && !multiIntString.isEmpty()){
- obj.setIPAddress(multiIntString + "]");
- }
+ changeDeviceIpv4Addresses(device, obj);
ope.commit();
} else {
@@ -257,7 +234,23 @@
}
} catch (TitanException e) {
ope.rollback();
- log.error(":changeDeviceIPv4Address mac:{} failed due to exception {}", device.getMACAddressString(),e);
+ log.error(":changeDeviceIPv4Address mac:{} failed due to exception {}", device.getMACAddressString(), e);
+ }
+ }
+
+ private void changeDeviceIpv4Addresses(IDevice device, IDeviceObject deviceObject) {
+ for (int ipv4Address : device.getIPv4Addresses()) {
+ if (deviceObject.getIpv4Address(ipv4Address) == null) {
+ IIpv4Address dbIpv4Address = ope.ensureIpv4Address(ipv4Address);
+ deviceObject.addIpv4Address(dbIpv4Address);
+ }
+ }
+
+ List<Integer> deviceIpv4Addresses = Arrays.asList(device.getIPv4Addresses());
+ for (IIpv4Address dbIpv4Address : deviceObject.getIpv4Addresses()) {
+ if (!deviceIpv4Addresses.contains(dbIpv4Address.getIpv4Address())) {
+ deviceObject.removeIpv4Address(dbIpv4Address);
+ }
}
}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/core/internal/LinkStorageImpl.java b/src/main/java/net/onrc/onos/ofcontroller/core/internal/LinkStorageImpl.java
index 92e2831..7a3d43e 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/core/internal/LinkStorageImpl.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/core/internal/LinkStorageImpl.java
@@ -119,14 +119,19 @@
if (addLinkImpl(link)) {
// Set LinkInfo only if linfo is non-null.
if (linfo != null && (! setLinkInfoImpl(link, linfo))) {
+ log.debug("Adding linkinfo failed: {}", link);
op.rollback();
}
op.commit();
success = true;
} else {
+ // If we fail here that's because the ports aren't added
+ // before we try to add the link
+ log.debug("Adding link failed: {}", link);
op.rollback();
}
} catch (Exception e) {
+ op.rollback();
e.printStackTrace();
log.error("LinkStorageImpl:addLink link:{} linfo:{} failed", link, linfo);
}
@@ -153,6 +158,7 @@
op.commit();
success = true;
} catch (Exception e) {
+ op.rollback();
e.printStackTrace();
log.error("LinkStorageImpl:addLinks link:s{} failed", links);
}
@@ -223,24 +229,54 @@
*/
@Override
public List<Link> getLinks(Long dpid, short port) {
- List<Link> links = new ArrayList<Link>();
+ List<Link> links = new ArrayList<Link>();
+
+ IPortObject srcPort = op.searchPort(HexString.toHexString(dpid), port);
+ if (srcPort == null)
+ return links;
+ ISwitchObject srcSw = srcPort.getSwitch();
+ if (srcSw == null)
+ return links;
- IPortObject srcPort = op.searchPort(HexString.toHexString(dpid), port);
- ISwitchObject srcSw = srcPort.getSwitch();
+ for(IPortObject dstPort : srcPort.getLinkedPorts()) {
+ ISwitchObject dstSw = dstPort.getSwitch();
+ if (dstSw != null) {
+ Link link = new Link(dpid, port,
+ HexString.toLong(dstSw.getDPID()),
+ dstPort.getNumber());
+ links.add(link);
+ }
+ }
+ return links;
+ }
+
+ /**
+ * Get list of all reverse links connected to the port specified by given DPID and port number.
+ * @param dpid DPID of desired port.
+ * @param port Port number of desired port.
+ * @return List of reverse links. Empty list if no port was found.
+ */
+ @Override
+ public List<Link> getReverseLinks(Long dpid, short port) {
+ List<Link> links = new ArrayList<Link>();
- if(srcSw != null && srcPort != null) {
- for(IPortObject dstPort : srcPort.getLinkedPorts()) {
- ISwitchObject dstSw = dstPort.getSwitch();
- Link link = new Link(HexString.toLong(srcSw.getDPID()),
- srcPort.getNumber(),
- HexString.toLong(dstSw.getDPID()),
- dstPort.getNumber());
-
- links.add(link);
- }
- }
+ IPortObject srcPort = op.searchPort(HexString.toHexString(dpid), port);
+ if (srcPort == null)
+ return links;
+ ISwitchObject srcSw = srcPort.getSwitch();
+ if (srcSw == null)
+ return links;
- return links;
+ for(IPortObject dstPort : srcPort.getReverseLinkedPorts()) {
+ ISwitchObject dstSw = dstPort.getSwitch();
+ if (dstSw != null) {
+ Link link = new Link(HexString.toLong(dstSw.getDPID()),
+ dstPort.getNumber(),
+ dpid, port);
+ links.add(link);
+ }
+ }
+ return links;
}
/**
@@ -288,10 +324,42 @@
for(IPortObject dstPort : srcPort.getLinkedPorts()) {
ISwitchObject dstSw = dstPort.getSwitch();
if(dstSw != null) {
- Link link = new Link(HexString.toLong(srcSw.getDPID()),
+ Link link = new Link(HexString.toLong(dpid),
srcPort.getNumber(),
HexString.toLong(dstSw.getDPID()),
- dstPort.getNumber());
+ dstPort.getNumber());
+ links.add(link);
+ }
+ }
+ }
+ }
+
+ return links;
+ }
+
+ /**
+ * Get list of all reverse links connected to the switch specified by
+ * given DPID.
+ * @param dpid DPID of desired switch.
+ * @return List of reverse links. Empty list if no port was found.
+ */
+ @Override
+ public List<Link> getReverseLinks(String dpid) {
+ List<Link> links = new ArrayList<Link>();
+
+ ISwitchObject srcSw = op.searchSwitch(dpid);
+
+ if(srcSw != null) {
+ for(IPortObject srcPort : srcSw.getPorts()) {
+ for(IPortObject dstPort : srcPort.getReverseLinkedPorts()) {
+ ISwitchObject dstSw = dstPort.getSwitch();
+ if(dstSw != null) {
+ Link link = new Link(
+ HexString.toLong(dstSw.getDPID()),
+ dstPort.getNumber(),
+
+ HexString.toLong(dpid),
+ srcPort.getNumber());
links.add(link);
}
}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/core/internal/SwitchStorageImpl.java b/src/main/java/net/onrc/onos/ofcontroller/core/internal/SwitchStorageImpl.java
index 26d4e40..6377605 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/core/internal/SwitchStorageImpl.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/core/internal/SwitchStorageImpl.java
@@ -1,5 +1,8 @@
package net.onrc.onos.ofcontroller.core.internal;
+import java.util.ArrayList;
+import java.util.List;
+
import net.floodlightcontroller.core.IOFSwitch;
import net.onrc.onos.graph.GraphDBConnection;
import net.onrc.onos.graph.GraphDBOperation;
@@ -59,6 +62,14 @@
* @param state The state of the switch like ACTIVE, INACTIVE
* @param dmope The DM_OPERATION of the switch
*/
+ /*
+ * Jono, 11/8/2013
+ * We don't need this update method that demultiplexes DM_OPERATIONS,
+ * we can have clients just call the required methods directly.
+ * We especially don't need this update method to re-implement
+ * the functions of other methods.
+ */
+ @Deprecated
@Override
public boolean updateSwitch(String dpid, SwitchState state, DM_OPERATION dmope) {
boolean success = false;
@@ -137,9 +148,13 @@
for (OFPhysicalPort port: sw.getPorts()) {
IPortObject p = op.searchPort(dpid, port.getPortNumber());
if (p != null) {
- log.error("SwitchStorage:addPort dpid:{} port:{} exists", dpid, port.getPortNumber());
+ log.debug("SwitchStorage:addPort dpid:{} port:{} exists", dpid, port.getPortNumber());
setPortStateImpl(p, port.getState(), port.getName());
p.setState("ACTIVE");
+ if (curr.getPort(port.getPortNumber()) == null) {
+ // The port exists but the switch has no "on" link to it
+ curr.addPort(p);
+ }
} else {
p = addPortImpl(curr, port.getPortNumber());
setPortStateImpl(p, port.getState(), port.getName());
@@ -149,8 +164,9 @@
success = true;
} catch (Exception e) {
op.rollback();
- e.printStackTrace();
- log.error("SwitchStorage:addSwitch dpid:{} failed", dpid);
+ //e.printStackTrace();
+ log.error("SwitchStorage:addSwitch dpid:{} failed: {}", dpid);
+ log.error("switch write error", e);
}
return success;
@@ -160,6 +176,10 @@
* This function is for adding the switch into the DB.
* @param dpid The switch dpid you want to add into the DB.
*/
+ // This method is only called by tests, so we probably don't need it.
+ // If we need both addSwitch interfaces, one should call the other
+ // rather than implementing the same logic twice.
+ @Deprecated
@Override
public boolean addSwitch(String dpid) {
boolean success = false;
@@ -179,7 +199,7 @@
} catch (Exception e) {
op.rollback();
e.printStackTrace();
- log.info("SwitchStorage:addSwitch dpid:{} failed", dpid);
+ log.error("SwitchStorage:addSwitch dpid:{} failed", dpid, e);
}
return success;
@@ -208,6 +228,32 @@
return success;
}
+
+ public boolean deactivateSwitch(String dpid) {
+ boolean success = false;
+
+ try {
+ ISwitchObject switchObject = op.searchSwitch(dpid);
+ if (switchObject != null) {
+ setSwitchStateImpl(switchObject, SwitchState.INACTIVE);
+
+ for (IPortObject portObject : switchObject.getPorts()) {
+ portObject.setState("INACTIVE");
+ }
+ op.commit();
+ success = true;
+ }
+ else {
+ log.warn("Switch {} not found when trying to deactivate", dpid);
+ }
+ } catch (Exception e) {
+ // TODO what type of exception is thrown when we can't commit?
+ op.rollback();
+ log.error("SwitchStorage:deactivateSwitch {} failed", dpid, e);
+ }
+
+ return success;
+ }
public boolean updatePort(String dpid, short portNum, int state, String desc) {
boolean success = false;
@@ -220,8 +266,8 @@
log.info("SwitchStorage:updatePort dpid:{} port:{}", dpid, portNum);
if (p != null) {
setPortStateImpl(p, state, desc);
+ op.commit();
}
- op.commit();
success = true;
} else {
log.error("SwitchStorage:updatePort dpid:{} port:{} : failed switch does not exist", dpid, portNum);
@@ -247,6 +293,8 @@
if(((OFPortConfig.OFPPC_PORT_DOWN.getValue() & phport.getConfig()) > 0) ||
((OFPortState.OFPPS_LINK_DOWN.getValue() & phport.getState()) > 0)) {
// just dispatch to deletePort()
+ // TODO This is wrong. We need to make sure the port is in the
+ // DB with the correct info and port state.
return deletePort(dpid, phport.getPortNumber());
}
@@ -293,19 +341,41 @@
IPortObject p = sw.getPort(port);
if (p != null) {
log.info("SwitchStorage:deletePort dpid:{} port:{} found and set INACTIVE", dpid, port);
- deletePortImpl(p);
+ //deletePortImpl(p);
+ p.setState("INACTIVE");
op.commit();
}
}
+ success = true;
} catch (Exception e) {
op.rollback();
e.printStackTrace();
- log.info("SwitchStorage:deletePort dpid:{} port:{} failed", dpid, port);
+ log.error("SwitchStorage:deletePort dpid:{} port:{} failed", dpid, port);
}
return success;
}
+ /**
+ * Get list of all ports on the switch specified by given DPID.
+ *
+ * @param dpid DPID of desired switch.
+ * @return List of port IDs. Empty list if no port was found.
+ */
+ @Override
+ public List<Short> getPorts(String dpid) {
+ List<Short> ports = new ArrayList<Short>();
+
+ ISwitchObject srcSw = op.searchSwitch(dpid);
+ if (srcSw != null) {
+ for (IPortObject srcPort : srcSw.getPorts()) {
+ ports.add(srcPort.getNumber());
+ }
+ }
+
+ return ports;
+ }
+
private ISwitchObject addSwitchImpl(String dpid) {
if (dpid != null) {
ISwitchObject sw = op.newSwitch(dpid);
@@ -333,6 +403,11 @@
}
}
+ // TODO There's an issue here where a port with that ID could already
+ // exist when we try to add this one (because it's left over from an
+ // old topology). We need to remove an old port with the same ID when
+ // we add the new port. Also it seems that old ports like this are
+ // never cleaned up and will remain in the DB in the ACTIVE state forever.
private IPortObject addPortImpl(ISwitchObject sw, short portNum) {
IPortObject p = op.newPort(sw.getDPID(), portNum);
p.setState("ACTIVE");
diff --git a/src/main/java/net/onrc/onos/ofcontroller/core/module/IOnosService.java b/src/main/java/net/onrc/onos/ofcontroller/core/module/IOnosService.java
new file mode 100644
index 0000000..5828366
--- /dev/null
+++ b/src/main/java/net/onrc/onos/ofcontroller/core/module/IOnosService.java
@@ -0,0 +1,7 @@
+package net.onrc.onos.ofcontroller.core.module;
+
+import net.floodlightcontroller.core.module.IFloodlightService;
+
+public interface IOnosService extends IFloodlightService {
+
+}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/core/module/OnosModuleLoader.java b/src/main/java/net/onrc/onos/ofcontroller/core/module/OnosModuleLoader.java
new file mode 100644
index 0000000..f3d0da5
--- /dev/null
+++ b/src/main/java/net/onrc/onos/ofcontroller/core/module/OnosModuleLoader.java
@@ -0,0 +1,95 @@
+package net.onrc.onos.ofcontroller.core.module;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import net.floodlightcontroller.core.IFloodlightProviderService;
+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.devicemanager.IDeviceService;
+import net.floodlightcontroller.restserver.IRestApiService;
+import net.floodlightcontroller.topology.ITopologyService;
+import net.onrc.onos.ofcontroller.core.config.DefaultConfiguration;
+import net.onrc.onos.ofcontroller.core.config.IConfigInfoService;
+import net.onrc.onos.ofcontroller.flowmanager.IFlowService;
+import net.onrc.onos.ofcontroller.forwarding.Forwarding;
+import net.onrc.onos.ofcontroller.proxyarp.IProxyArpService;
+import net.onrc.onos.ofcontroller.proxyarp.ProxyArpManager;
+
+public class OnosModuleLoader implements IFloodlightModule {
+ private IFloodlightProviderService floodlightProvider;
+ private ITopologyService topology;
+ private IDeviceService deviceService;
+ private IConfigInfoService config;
+ private IRestApiService restApi;
+ private IFlowService flowService;
+
+ private ProxyArpManager arpManager;
+ private Forwarding forwarding;
+
+ public OnosModuleLoader() {
+ arpManager = new ProxyArpManager();
+ forwarding = new Forwarding();
+ }
+
+ @Override
+ public Collection<Class<? extends IFloodlightService>> getModuleServices() {
+ List<Class<? extends IFloodlightService>> services =
+ new ArrayList<Class<? extends IFloodlightService>>();
+ services.add(IProxyArpService.class);
+ return services;
+ }
+
+ @Override
+ public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() {
+ Map<Class<? extends IFloodlightService>, IFloodlightService> impls =
+ new HashMap<Class<? extends IFloodlightService>, IFloodlightService>();
+ impls.put(IProxyArpService.class, arpManager);
+ return impls;
+ }
+
+ @Override
+ public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
+ List<Class<? extends IFloodlightService>> dependencies =
+ new ArrayList<Class<? extends IFloodlightService>>();
+ dependencies.add(IFloodlightProviderService.class);
+ dependencies.add(ITopologyService.class);
+ dependencies.add(IDeviceService.class);
+ //dependencies.add(IConfigInfoService.class);
+ dependencies.add(IRestApiService.class);
+ dependencies.add(IFlowService.class);
+ return dependencies;
+ }
+
+ @Override
+ public void init(FloodlightModuleContext context)
+ throws FloodlightModuleException {
+ floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
+ topology = context.getServiceImpl(ITopologyService.class);
+ deviceService = context.getServiceImpl(IDeviceService.class);
+ restApi = context.getServiceImpl(IRestApiService.class);
+ flowService = context.getServiceImpl(IFlowService.class);
+
+ //This could be null because it's not mandatory to have an
+ //IConfigInfoService loaded.
+ config = context.getServiceImpl(IConfigInfoService.class);
+ if (config == null) {
+ config = new DefaultConfiguration();
+ }
+
+ arpManager.init(floodlightProvider, topology, deviceService, config, restApi);
+ forwarding.init(floodlightProvider, flowService);
+ }
+
+ @Override
+ public void startUp(FloodlightModuleContext context) {
+ arpManager.startUp();
+ forwarding.startUp();
+ }
+
+}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/floodlightlistener/NetworkGraphPublisher.java b/src/main/java/net/onrc/onos/ofcontroller/floodlightlistener/NetworkGraphPublisher.java
index 14cffd8..6364a2f 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/floodlightlistener/NetworkGraphPublisher.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/floodlightlistener/NetworkGraphPublisher.java
@@ -3,6 +3,7 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
@@ -99,7 +100,16 @@
if (hasControl) {
log.debug("got control to set inactive sw {}", HexString.toHexString(dpid));
try {
- if (swStore.updateSwitch(HexString.toHexString(dpid), SwitchState.INACTIVE, DM_OPERATION.UPDATE)) {
+ // Get the affected ports
+ List<Short> ports = swStore.getPorts(HexString.toHexString(dpid));
+ // Get the affected links
+ List<Link> links = linkStore.getLinks(HexString.toHexString(dpid));
+ // Get the affected reverse links
+ List<Link> reverseLinks = linkStore.getReverseLinks(HexString.toHexString(dpid));
+ links.addAll(reverseLinks);
+
+ //if (swStore.updateSwitch(HexString.toHexString(dpid), SwitchState.INACTIVE, DM_OPERATION.UPDATE)) {
+ if (swStore.deactivateSwitch(HexString.toHexString(dpid))) {
registryService.releaseControl(dpid);
// TODO publish UPDATE_SWITCH event here
@@ -112,6 +122,21 @@
new TopologyElement(dpid);
datagridService.notificationSendTopologyElementRemoved(topologyElement);
+ // Publish: remove the affected ports
+ for (Short port : ports) {
+ TopologyElement topologyElementPort =
+ new TopologyElement(dpid, port);
+ datagridService.notificationSendTopologyElementRemoved(topologyElementPort);
+ }
+ // Publish: remove the affected links
+ for (Link link : links) {
+ TopologyElement topologyElementLink =
+ new TopologyElement(link.getSrc(),
+ link.getSrcPort(),
+ link.getDst(),
+ link.getDstPort());
+ datagridService.notificationSendTopologyElementRemoved(topologyElementLink);
+ }
}
} catch (Exception e) {
log.error("Error in SwitchCleanup:controlChanged ", e);
@@ -212,25 +237,72 @@
// TODO publish ADD_SWITCH event here
TopologyElement topologyElement =
new TopologyElement(sw.getId());
+ datagridService.notificationSendTopologyElementAdded(topologyElement);
+
+ // Publish: add the ports
// TODO: Add only ports that are UP?
for (OFPhysicalPort port : sw.getPorts()) {
- topologyElement.addSwitchPort(port.getPortNumber());
+ TopologyElement topologyElementPort =
+ new TopologyElement(sw.getId(),
+ port.getPortNumber());
+ datagridService.notificationSendTopologyElementAdded(topologyElementPort);
}
- datagridService.notificationSendTopologyElementAdded(topologyElement);
+
+ // Add all links that might be connected already
+ List<Link> links = linkStore.getLinks(HexString.toHexString(sw.getId()));
+ // Add all reverse links as well
+ List<Link> reverseLinks = linkStore.getReverseLinks(HexString.toHexString(sw.getId()));
+ links.addAll(reverseLinks);
+
+ // Publish: add the links
+ for (Link link : links) {
+ TopologyElement topologyElementLink =
+ new TopologyElement(link.getSrc(),
+ link.getSrcPort(),
+ link.getDst(),
+ link.getDstPort());
+ datagridService.notificationSendTopologyElementAdded(topologyElementLink);
+ }
}
}
}
@Override
public void removedSwitch(IOFSwitch sw) {
+ /*
if (registryService.hasControl(sw.getId())) {
+ // Get the affected ports
+ List<Short> ports = swStore.getPorts(HexString.toHexString(sw.getId()));
+ // Get the affected links
+ List<Link> links = linkStore.getLinks(HexString.toHexString(sw.getId()));
+ // Get the affected reverse links
+ List<Link> reverseLinks = linkStore.getReverseLinks(HexString.toHexString(sw.getId()));
+ links.addAll(reverseLinks);
+
if (swStore.deleteSwitch(sw.getStringId())) {
// TODO publish DELETE_SWITCH event here
TopologyElement topologyElement =
new TopologyElement(sw.getId());
datagridService.notificationSendTopologyElementRemoved(topologyElement);
+
+ // Publish: remove the affected ports
+ for (Short port : ports) {
+ TopologyElement topologyElementPort =
+ new TopologyElement(sw.getId(), port);
+ datagridService.notificationSendTopologyElementRemoved(topologyElementPort);
+ }
+ // Publish: remove the affected links
+ for (Link link : links) {
+ TopologyElement topologyElementLink =
+ new TopologyElement(link.getSrc(),
+ link.getSrcPort(),
+ link.getDst(),
+ link.getDstPort());
+ datagridService.notificationSendTopologyElementRemoved(topologyElementLink);
+ }
}
}
+ */
}
@Override
@@ -246,16 +318,48 @@
TopologyElement topologyElement =
new TopologyElement(switchId, port.getPortNumber());
datagridService.notificationSendTopologyElementAdded(topologyElement);
+
+ // Add all links that might be connected already
+ List<Link> links = linkStore.getLinks(switchId, port.getPortNumber());
+ // Add all reverse links as well
+ List<Link> reverseLinks = linkStore.getReverseLinks(switchId, port.getPortNumber());
+ links.addAll(reverseLinks);
+
+ // Publish: add the links
+ for (Link link : links) {
+ TopologyElement topologyElementLink =
+ new TopologyElement(link.getSrc(),
+ link.getSrcPort(),
+ link.getDst(),
+ link.getDstPort());
+ datagridService.notificationSendTopologyElementAdded(topologyElementLink);
+ }
}
}
@Override
public void switchPortRemoved(Long switchId, OFPhysicalPort port) {
+ // Remove all links that might be connected already
+ List<Link> links = linkStore.getLinks(switchId, port.getPortNumber());
+ // Remove all reverse links as well
+ List<Link> reverseLinks = linkStore.getReverseLinks(switchId, port.getPortNumber());
+ links.addAll(reverseLinks);
+
if (swStore.deletePort(HexString.toHexString(switchId), port.getPortNumber())) {
// TODO publish DELETE_PORT event here
TopologyElement topologyElement =
new TopologyElement(switchId, port.getPortNumber());
datagridService.notificationSendTopologyElementRemoved(topologyElement);
+
+ // Publish: remove the links
+ for (Link link : links) {
+ TopologyElement topologyElementLink =
+ new TopologyElement(link.getSrc(),
+ link.getSrcPort(),
+ link.getDst(),
+ link.getDstPort());
+ datagridService.notificationSendTopologyElementRemoved(topologyElementLink);
+ }
}
}
@@ -273,6 +377,7 @@
@Override
public void deviceRemoved(IDevice device) {
// TODO Auto-generated method stub
+ devStore.removeDevice(device);
}
@Override
diff --git a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/FlowDatabaseOperation.java b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/FlowDatabaseOperation.java
index d06c62c..926788f 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/FlowDatabaseOperation.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/FlowDatabaseOperation.java
@@ -326,6 +326,44 @@
}
/**
+ * Delete a flow entry from the Network MAP.
+ *
+ * @param dbHandler the Graph Database handler to use.
+ * @param flowObj the corresponding Flow Path object for the Flow Entry.
+ * @param flowEntry the Flow Entry to delete.
+ * @return true on success, otherwise false.
+ */
+ static boolean deleteFlowEntry(GraphDBOperation dbHandler,
+ IFlowPath flowObj,
+ FlowEntry flowEntry) {
+ IFlowEntry flowEntryObj = null;
+ try {
+ flowEntryObj = dbHandler.searchFlowEntry(flowEntry.flowEntryId());
+ } catch (Exception e) {
+ log.error(":deleteFlowEntry FlowEntryId:{} failed",
+ flowEntry.flowEntryId().toString());
+ return false;
+ }
+ //
+ // TODO: Don't print an error for now, because multiple controller
+ // instances might be deleting the same flow entry.
+ //
+ /*
+ if (flowEntryObj == null) {
+ log.error(":deleteFlowEntry FlowEntryId:{} failed: FlowEntry object not found",
+ flowEntry.flowEntryId().toString());
+ return false;
+ }
+ */
+ if (flowEntryObj == null)
+ return true;
+
+ flowObj.removeFlowEntry(flowEntryObj);
+ dbHandler.removeFlowEntry(flowEntryObj);
+ return true;
+ }
+
+ /**
* Delete all previously added flows.
*
* @param dbHandler the Graph Database handler to use.
diff --git a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/FlowEventHandler.java b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/FlowEventHandler.java
new file mode 100644
index 0000000..0e9887a
--- /dev/null
+++ b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/FlowEventHandler.java
@@ -0,0 +1,867 @@
+package net.onrc.onos.ofcontroller.flowmanager;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+
+import net.floodlightcontroller.core.IOFSwitch;
+import net.onrc.onos.datagrid.IDatagridService;
+import net.onrc.onos.ofcontroller.topology.Topology;
+import net.onrc.onos.ofcontroller.topology.TopologyElement;
+import net.onrc.onos.ofcontroller.topology.TopologyManager;
+import net.onrc.onos.ofcontroller.util.DataPath;
+import net.onrc.onos.ofcontroller.util.EventEntry;
+import net.onrc.onos.ofcontroller.util.FlowEntry;
+import net.onrc.onos.ofcontroller.util.FlowEntryAction;
+import net.onrc.onos.ofcontroller.util.FlowEntryActions;
+import net.onrc.onos.ofcontroller.util.FlowEntryId;
+import net.onrc.onos.ofcontroller.util.FlowEntryMatch;
+import net.onrc.onos.ofcontroller.util.FlowEntrySwitchState;
+import net.onrc.onos.ofcontroller.util.FlowEntryUserState;
+import net.onrc.onos.ofcontroller.util.FlowId;
+import net.onrc.onos.ofcontroller.util.FlowPath;
+import net.onrc.onos.ofcontroller.util.FlowPathUserState;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A class for storing a pair of Flow Path and a Flow Entry.
+ */
+class FlowPathEntryPair {
+ protected FlowPath flowPath;
+ protected FlowEntry flowEntry;
+
+ protected FlowPathEntryPair(FlowPath flowPath, FlowEntry flowEntry) {
+ this.flowPath = flowPath;
+ this.flowEntry = flowEntry;
+ }
+}
+
+/**
+ * Class for FlowPath Maintenance.
+ * This class listens for FlowEvents to:
+ * - Maintain a local cache of the Network Topology.
+ * - Detect FlowPaths impacted by Topology change.
+ * - Recompute impacted FlowPath using cached Topology.
+ */
+class FlowEventHandler extends Thread implements IFlowEventHandlerService {
+ /** The logger. */
+ private final static Logger log = LoggerFactory.getLogger(FlowEventHandler.class);
+
+ private FlowManager flowManager; // The Flow Manager to use
+ private IDatagridService datagridService; // The Datagrid Service to use
+ private Topology topology; // The network topology
+ private Map<Long, FlowPath> allFlowPaths = new HashMap<Long, FlowPath>();
+ private Map<Long, FlowEntry> unmatchedFlowEntryAdd =
+ new HashMap<Long, FlowEntry>();
+
+ // The queue with Flow Path and Topology Element updates
+ private BlockingQueue<EventEntry<?>> networkEvents =
+ new LinkedBlockingQueue<EventEntry<?>>();
+
+ // The pending Topology, FlowPath, and FlowEntry events
+ private List<EventEntry<TopologyElement>> topologyEvents =
+ new LinkedList<EventEntry<TopologyElement>>();
+ private List<EventEntry<FlowPath>> flowPathEvents =
+ new LinkedList<EventEntry<FlowPath>>();
+ private List<EventEntry<FlowEntry>> flowEntryEvents =
+ new LinkedList<EventEntry<FlowEntry>>();
+
+ //
+ // Transient state for processing the Flow Paths:
+ // - The new Flow Paths
+ // - The Flow Paths that should be recomputed
+ // - The Flow Paths with modified Flow Entries
+ // - The Flow Entries that were updated
+ //
+ private List<FlowPath> newFlowPaths = new LinkedList<FlowPath>();
+ private List<FlowPath> recomputeFlowPaths = new LinkedList<FlowPath>();
+ private List<FlowPath> modifiedFlowPaths = new LinkedList<FlowPath>();
+ private List<FlowPathEntryPair> updatedFlowEntries =
+ new LinkedList<FlowPathEntryPair>();
+ private List<FlowPathEntryPair> unmatchedDeleteFlowEntries =
+ new LinkedList<FlowPathEntryPair>();
+
+
+ /**
+ * Constructor for a given Flow Manager and Datagrid Service.
+ *
+ * @param flowManager the Flow Manager to use.
+ * @param datagridService the Datagrid Service to use.
+ */
+ FlowEventHandler(FlowManager flowManager,
+ IDatagridService datagridService) {
+ this.flowManager = flowManager;
+ this.datagridService = datagridService;
+ this.topology = new Topology();
+ }
+
+ /**
+ * Get the network topology.
+ *
+ * @return the network topology.
+ */
+ protected Topology getTopology() { return this.topology; }
+
+ /**
+ * Startup processing.
+ */
+ private void startup() {
+ //
+ // Obtain the initial Topology state
+ //
+ Collection<TopologyElement> topologyElements =
+ datagridService.getAllTopologyElements();
+ for (TopologyElement topologyElement : topologyElements) {
+ EventEntry<TopologyElement> eventEntry =
+ new EventEntry<TopologyElement>(EventEntry.Type.ENTRY_ADD, topologyElement);
+ topologyEvents.add(eventEntry);
+ }
+ //
+ // Obtain the initial Flow Path state
+ //
+ Collection<FlowPath> flowPaths = datagridService.getAllFlows();
+ for (FlowPath flowPath : flowPaths) {
+ EventEntry<FlowPath> eventEntry =
+ new EventEntry<FlowPath>(EventEntry.Type.ENTRY_ADD, flowPath);
+ flowPathEvents.add(eventEntry);
+ }
+ //
+ // Obtain the initial FlowEntry state
+ //
+ Collection<FlowEntry> flowEntries = datagridService.getAllFlowEntries();
+ for (FlowEntry flowEntry : flowEntries) {
+ EventEntry<FlowEntry> eventEntry =
+ new EventEntry<FlowEntry>(EventEntry.Type.ENTRY_ADD, flowEntry);
+ flowEntryEvents.add(eventEntry);
+ }
+
+ // Process the initial events (if any)
+ processEvents();
+ }
+
+ /**
+ * Run the thread.
+ */
+ @Override
+ public void run() {
+ startup();
+
+ //
+ // The main loop
+ //
+ Collection<EventEntry<?>> collection = new LinkedList<EventEntry<?>>();
+ try {
+ while (true) {
+ EventEntry<?> eventEntry = networkEvents.take();
+ collection.add(eventEntry);
+ networkEvents.drainTo(collection);
+
+ //
+ // Demultiplex all events:
+ // - EventEntry<TopologyElement>
+ // - EventEntry<FlowPath>
+ // - EventEntry<FlowEntry>
+ //
+ for (EventEntry<?> event : collection) {
+ if (event.eventData() instanceof TopologyElement) {
+ EventEntry<TopologyElement> topologyEventEntry =
+ (EventEntry<TopologyElement>)event;
+ topologyEvents.add(topologyEventEntry);
+ } else if (event.eventData() instanceof FlowPath) {
+ EventEntry<FlowPath> flowPathEventEntry =
+ (EventEntry<FlowPath>)event;
+ flowPathEvents.add(flowPathEventEntry);
+ } else if (event.eventData() instanceof FlowEntry) {
+ EventEntry<FlowEntry> flowEntryEventEntry =
+ (EventEntry<FlowEntry>)event;
+ flowEntryEvents.add(flowEntryEventEntry);
+ }
+ }
+ collection.clear();
+
+ // Process the events (if any)
+ processEvents();
+ }
+ } catch (Exception exception) {
+ log.debug("Exception processing Network Events: ", exception);
+ }
+ }
+
+ /**
+ * Process the events (if any)
+ */
+ private void processEvents() {
+ List<FlowPathEntryPair> modifiedFlowEntries;
+
+ if (topologyEvents.isEmpty() && flowPathEvents.isEmpty() &&
+ flowEntryEvents.isEmpty()) {
+ return; // Nothing to do
+ }
+
+ processFlowPathEvents();
+ processTopologyEvents();
+ //
+ // Add all new Flows: should be done after processing the Flow Path
+ // and Topology events.
+ //
+ for (FlowPath flowPath : newFlowPaths) {
+ allFlowPaths.put(flowPath.flowId().value(), flowPath);
+ }
+
+ processFlowEntryEvents();
+
+ // Recompute all affected Flow Paths and keep only the modified
+ for (FlowPath flowPath : recomputeFlowPaths) {
+ if (recomputeFlowPath(flowPath))
+ modifiedFlowPaths.add(flowPath);
+ }
+
+ modifiedFlowEntries = extractModifiedFlowEntries(modifiedFlowPaths);
+
+ // Assign missing Flow Entry IDs
+ assignFlowEntryId(modifiedFlowEntries);
+
+ //
+ // Push the modified Flow Entries to switches, datagrid and database
+ //
+ flowManager.pushModifiedFlowEntriesToSwitches(modifiedFlowEntries);
+ flowManager.pushModifiedFlowEntriesToDatagrid(modifiedFlowEntries);
+ flowManager.pushModifiedFlowEntriesToDatabase(modifiedFlowEntries);
+ flowManager.pushModifiedFlowEntriesToDatabase(updatedFlowEntries);
+ flowManager.pushModifiedFlowEntriesToDatabase(unmatchedDeleteFlowEntries);
+
+ //
+ // Remove Flow Entries that were deleted
+ //
+ for (FlowPath flowPath : modifiedFlowPaths)
+ flowPath.dataPath().removeDeletedFlowEntries();
+
+ // Cleanup
+ topologyEvents.clear();
+ flowPathEvents.clear();
+ flowEntryEvents.clear();
+ //
+ newFlowPaths.clear();
+ recomputeFlowPaths.clear();
+ modifiedFlowPaths.clear();
+ updatedFlowEntries.clear();
+ unmatchedDeleteFlowEntries.clear();
+ }
+
+ /**
+ * Extract the modified Flow Entries.
+ */
+ private List<FlowPathEntryPair> extractModifiedFlowEntries(
+ List<FlowPath> modifiedFlowPaths) {
+ List<FlowPathEntryPair> modifiedFlowEntries =
+ new LinkedList<FlowPathEntryPair>();
+
+ // Extract only the modified Flow Entries
+ for (FlowPath flowPath : modifiedFlowPaths) {
+ for (FlowEntry flowEntry : flowPath.flowEntries()) {
+ if (flowEntry.flowEntrySwitchState() ==
+ FlowEntrySwitchState.FE_SWITCH_NOT_UPDATED) {
+ FlowPathEntryPair flowPair =
+ new FlowPathEntryPair(flowPath, flowEntry);
+ modifiedFlowEntries.add(flowPair);
+ }
+ }
+ }
+ return modifiedFlowEntries;
+ }
+
+ /**
+ * Assign the Flow Entry ID as needed.
+ */
+ private void assignFlowEntryId(List<FlowPathEntryPair> modifiedFlowEntries) {
+ if (modifiedFlowEntries.isEmpty())
+ return;
+
+ Map<Long, IOFSwitch> mySwitches = flowManager.getMySwitches();
+
+ //
+ // Assign the Flow Entry ID only for Flow Entries for my switches
+ //
+ for (FlowPathEntryPair flowPair : modifiedFlowEntries) {
+ FlowEntry flowEntry = flowPair.flowEntry;
+ // Update the Flow Entries only for my switches
+ IOFSwitch mySwitch = mySwitches.get(flowEntry.dpid().value());
+ if (mySwitch == null)
+ continue;
+ if (! flowEntry.isValidFlowEntryId()) {
+ long id = flowManager.getNextFlowEntryId();
+ flowEntry.setFlowEntryId(new FlowEntryId(id));
+ }
+ }
+ }
+
+ /**
+ * Process the Flow Path events.
+ */
+ private void processFlowPathEvents() {
+ //
+ // Process all Flow Path events and update the appropriate state
+ //
+ for (EventEntry<FlowPath> eventEntry : flowPathEvents) {
+ FlowPath flowPath = eventEntry.eventData();
+
+ log.debug("Flow Event: {} {}", eventEntry.eventType(),
+ flowPath.toString());
+
+ switch (eventEntry.eventType()) {
+ case ENTRY_ADD: {
+ //
+ // Add a new Flow Path
+ //
+ if (allFlowPaths.get(flowPath.flowId().value()) != null) {
+ //
+ // TODO: What to do if the Flow Path already exists?
+ // Remove and then re-add it, or merge the info?
+ // For now, we don't have to do anything.
+ //
+ break;
+ }
+
+ switch (flowPath.flowPathType()) {
+ case FP_TYPE_SHORTEST_PATH:
+ //
+ // Reset the Data Path, in case it was set already, because
+ // we are going to recompute it anyway.
+ //
+ flowPath.flowEntries().clear();
+ recomputeFlowPaths.add(flowPath);
+ break;
+ case FP_TYPE_EXPLICIT_PATH:
+ //
+ // Mark all Flow Entries for installation in the switches.
+ //
+ for (FlowEntry flowEntry : flowPath.flowEntries()) {
+ flowEntry.setFlowEntrySwitchState(FlowEntrySwitchState.FE_SWITCH_NOT_UPDATED);
+ }
+ modifiedFlowPaths.add(flowPath);
+ break;
+ }
+ newFlowPaths.add(flowPath);
+
+ break;
+ }
+
+ case ENTRY_REMOVE: {
+ //
+ // Remove an existing Flow Path.
+ //
+ // Find the Flow Path, and mark the Flow and its Flow Entries
+ // for deletion.
+ //
+ FlowPath existingFlowPath =
+ allFlowPaths.get(flowPath.flowId().value());
+ if (existingFlowPath == null)
+ continue; // Nothing to do
+
+ existingFlowPath.setFlowPathUserState(FlowPathUserState.FP_USER_DELETE);
+ for (FlowEntry flowEntry : existingFlowPath.flowEntries()) {
+ flowEntry.setFlowEntryUserState(FlowEntryUserState.FE_USER_DELETE);
+ flowEntry.setFlowEntrySwitchState(FlowEntrySwitchState.FE_SWITCH_NOT_UPDATED);
+ }
+
+ allFlowPaths.remove(existingFlowPath.flowId().value());
+ modifiedFlowPaths.add(existingFlowPath);
+
+ break;
+ }
+ }
+ }
+ }
+
+ /**
+ * Process the Topology events.
+ */
+ private void processTopologyEvents() {
+ //
+ // Process all Topology events and update the appropriate state
+ //
+ boolean isTopologyModified = false;
+ for (EventEntry<TopologyElement> eventEntry : topologyEvents) {
+ TopologyElement topologyElement = eventEntry.eventData();
+
+ log.debug("Topology Event: {} {}", eventEntry.eventType(),
+ topologyElement.toString());
+
+ switch (eventEntry.eventType()) {
+ case ENTRY_ADD:
+ isTopologyModified |= topology.addTopologyElement(topologyElement);
+ break;
+ case ENTRY_REMOVE:
+ isTopologyModified |= topology.removeTopologyElement(topologyElement);
+ break;
+ }
+ }
+ if (isTopologyModified) {
+ // TODO: For now, if the topology changes, we recompute all Flows
+ recomputeFlowPaths.addAll(allFlowPaths.values());
+ }
+ }
+
+ /**
+ * Process the Flow Entry events.
+ */
+ private void processFlowEntryEvents() {
+ FlowPathEntryPair flowPair;
+ FlowPath flowPath;
+ FlowEntry updatedFlowEntry;
+
+ //
+ // Update Flow Entries with previously unmatched Flow Entry updates
+ //
+ if (! unmatchedFlowEntryAdd.isEmpty()) {
+ Map<Long, FlowEntry> remainingUpdates = new HashMap<Long, FlowEntry>();
+ for (FlowEntry flowEntry : unmatchedFlowEntryAdd.values()) {
+ flowPath = allFlowPaths.get(flowEntry.flowId().value());
+ if (flowPath == null)
+ continue;
+ updatedFlowEntry = updateFlowEntryAdd(flowPath, flowEntry);
+ if (updatedFlowEntry == null) {
+ remainingUpdates.put(flowEntry.flowEntryId().value(),
+ flowEntry);
+ continue;
+ }
+ flowPair = new FlowPathEntryPair(flowPath, updatedFlowEntry);
+ updatedFlowEntries.add(flowPair);
+ }
+ unmatchedFlowEntryAdd = remainingUpdates;
+ }
+
+ //
+ // Process all Flow Entry events and update the appropriate state
+ //
+ for (EventEntry<FlowEntry> eventEntry : flowEntryEvents) {
+ FlowEntry flowEntry = eventEntry.eventData();
+
+ log.debug("Flow Entry Event: {} {}", eventEntry.eventType(),
+ flowEntry.toString());
+
+ if ((! flowEntry.isValidFlowId()) ||
+ (! flowEntry.isValidFlowEntryId())) {
+ continue;
+ }
+
+ switch (eventEntry.eventType()) {
+ case ENTRY_ADD:
+ flowPath = allFlowPaths.get(flowEntry.flowId().value());
+ if (flowPath == null) {
+ // Flow Path not found: keep the entry for later matching
+ unmatchedFlowEntryAdd.put(flowEntry.flowEntryId().value(),
+ flowEntry);
+ break;
+ }
+ updatedFlowEntry = updateFlowEntryAdd(flowPath, flowEntry);
+ if (updatedFlowEntry == null) {
+ // Flow Entry not found: keep the entry for later matching
+ unmatchedFlowEntryAdd.put(flowEntry.flowEntryId().value(),
+ flowEntry);
+ break;
+ }
+ // Add the updated entry to the list of updated Flow Entries
+ flowPair = new FlowPathEntryPair(flowPath, updatedFlowEntry);
+ updatedFlowEntries.add(flowPair);
+ break;
+
+ case ENTRY_REMOVE:
+ flowEntry.setFlowEntryUserState(FlowEntryUserState.FE_USER_DELETE);
+ if (unmatchedFlowEntryAdd.remove(flowEntry.flowEntryId().value()) != null) {
+ continue; // Match found
+ }
+
+ flowPath = allFlowPaths.get(flowEntry.flowId().value());
+ if (flowPath == null) {
+ // Flow Path not found: ignore the update
+ break;
+ }
+ updatedFlowEntry = updateFlowEntryRemove(flowPath, flowEntry);
+ if (updatedFlowEntry == null) {
+ // Flow Entry not found: add to list of deleted entries
+ flowPair = new FlowPathEntryPair(flowPath, flowEntry);
+ unmatchedDeleteFlowEntries.add(flowPair);
+ break;
+ }
+ flowPair = new FlowPathEntryPair(flowPath, updatedFlowEntry);
+ updatedFlowEntries.add(flowPair);
+ break;
+ }
+ }
+ }
+
+ /**
+ * Update a Flow Entry because of an external ENTRY_ADD event.
+ *
+ * @param flowPath the FlowPath for the Flow Entry to update.
+ * @param flowEntry the FlowEntry with the new state.
+ * @return the updated Flow Entry if found, otherwise null.
+ */
+ private FlowEntry updateFlowEntryAdd(FlowPath flowPath,
+ FlowEntry flowEntry) {
+ //
+ // Iterate over all Flow Entries and find a match.
+ //
+ for (FlowEntry localFlowEntry : flowPath.flowEntries()) {
+ if (! TopologyManager.isSameFlowEntryDataPath(localFlowEntry,
+ flowEntry)) {
+ continue;
+ }
+
+ //
+ // Local Flow Entry match found
+ //
+ if (localFlowEntry.isValidFlowEntryId()) {
+ if (localFlowEntry.flowEntryId().value() !=
+ flowEntry.flowEntryId().value()) {
+ //
+ // Find a local Flow Entry, but the Flow Entry ID doesn't
+ // match. Keep looking.
+ //
+ continue;
+ }
+ } else {
+ // Update the Flow Entry ID
+ FlowEntryId flowEntryId =
+ new FlowEntryId(flowEntry.flowEntryId().value());
+ localFlowEntry.setFlowEntryId(flowEntryId);
+ }
+
+ //
+ // Update the local Flow Entry.
+ //
+ localFlowEntry.setFlowEntryUserState(flowEntry.flowEntryUserState());
+ localFlowEntry.setFlowEntrySwitchState(flowEntry.flowEntrySwitchState());
+ return localFlowEntry;
+ }
+
+ return null; // Entry not found
+ }
+
+ /**
+ * Update a Flow Entry because of an external ENTRY_REMOVE event.
+ *
+ * @param flowPath the FlowPath for the Flow Entry to update.
+ * @param flowEntry the FlowEntry with the new state.
+ * @return the updated Flow Entry if found, otherwise null.
+ */
+ private FlowEntry updateFlowEntryRemove(FlowPath flowPath,
+ FlowEntry flowEntry) {
+ //
+ // Iterate over all Flow Entries and find a match based on
+ // the Flow Entry ID.
+ //
+ for (FlowEntry localFlowEntry : flowPath.flowEntries()) {
+ if (! localFlowEntry.isValidFlowEntryId())
+ continue;
+ if (localFlowEntry.flowEntryId().value() !=
+ flowEntry.flowEntryId().value()) {
+ continue;
+ }
+ //
+ // Update the local Flow Entry.
+ //
+ localFlowEntry.setFlowEntryUserState(flowEntry.flowEntryUserState());
+ localFlowEntry.setFlowEntrySwitchState(flowEntry.flowEntrySwitchState());
+ return localFlowEntry;
+ }
+
+ return null; // Entry not found
+ }
+
+ /**
+ * Recompute a Flow Path.
+ *
+ * @param flowPath the Flow Path to recompute.
+ * @return true if the recomputed Flow Path has changed, otherwise false.
+ */
+ private boolean recomputeFlowPath(FlowPath flowPath) {
+ boolean hasChanged = false;
+
+ //
+ // Test whether the Flow Path needs to be recomputed
+ //
+ switch (flowPath.flowPathType()) {
+ case FP_TYPE_UNKNOWN:
+ return false; // Can't recompute on Unknown FlowType
+ case FP_TYPE_SHORTEST_PATH:
+ break;
+ case FP_TYPE_EXPLICIT_PATH:
+ return false; // An explicit path never changes
+ }
+
+ DataPath oldDataPath = flowPath.dataPath();
+
+ // Compute the new path
+ DataPath newDataPath = TopologyManager.computeNetworkPath(topology,
+ flowPath);
+ if (newDataPath == null) {
+ // We need the DataPath to compare the paths
+ newDataPath = new DataPath();
+ }
+ newDataPath.applyFlowPathFlags(flowPath.flowPathFlags());
+
+ //
+ // Test whether the new path is same
+ //
+ if (oldDataPath.flowEntries().size() !=
+ newDataPath.flowEntries().size()) {
+ hasChanged = true;
+ } else {
+ Iterator<FlowEntry> oldIter = oldDataPath.flowEntries().iterator();
+ Iterator<FlowEntry> newIter = newDataPath.flowEntries().iterator();
+ while (oldIter.hasNext() && newIter.hasNext()) {
+ FlowEntry oldFlowEntry = oldIter.next();
+ FlowEntry newFlowEntry = newIter.next();
+ if (! TopologyManager.isSameFlowEntryDataPath(oldFlowEntry,
+ newFlowEntry)) {
+ hasChanged = true;
+ break;
+ }
+ }
+ }
+ if (! hasChanged)
+ return hasChanged;
+
+ //
+ // Merge the changes in the path:
+ // - If a Flow Entry for a switch is in the old data path, but not
+ // in the new data path, then mark it for deletion.
+ // - If a Flow Entry for a switch is in the new data path, but not
+ // in the old data path, then mark it for addition.
+ // - If a Flow Entry for a switch is in both the old and the new
+ // data path, but it has changed, e.g., the incoming and/or outgoing
+ // port(s), then mark the old Flow Entry for deletion, and mark
+ // the new Flow Entry for addition.
+ // - If a Flow Entry for a switch is in both the old and the new
+ // data path, and it hasn't changed, then just keep it.
+ //
+ // NOTE: We use the Switch DPID of each entry to match the entries
+ //
+ Map<Long, FlowEntry> oldFlowEntriesMap = new HashMap<Long, FlowEntry>();
+ Map<Long, FlowEntry> newFlowEntriesMap = new HashMap<Long, FlowEntry>();
+ ArrayList<FlowEntry> finalFlowEntries = new ArrayList<FlowEntry>();
+ List<FlowEntry> deletedFlowEntries = new LinkedList<FlowEntry>();
+
+ // Prepare maps with the Flow Entries, so they are fast to lookup
+ for (FlowEntry flowEntry : oldDataPath.flowEntries())
+ oldFlowEntriesMap.put(flowEntry.dpid().value(), flowEntry);
+ for (FlowEntry flowEntry : newDataPath.flowEntries())
+ newFlowEntriesMap.put(flowEntry.dpid().value(), flowEntry);
+
+ //
+ // Find the old Flow Entries that should be deleted
+ //
+ for (FlowEntry oldFlowEntry : oldDataPath.flowEntries()) {
+ FlowEntry newFlowEntry =
+ newFlowEntriesMap.get(oldFlowEntry.dpid().value());
+ if (newFlowEntry == null) {
+ // The old Flow Entry should be deleted: not on the path
+ oldFlowEntry.setFlowEntryUserState(FlowEntryUserState.FE_USER_DELETE);
+ oldFlowEntry.setFlowEntrySwitchState(FlowEntrySwitchState.FE_SWITCH_NOT_UPDATED);
+ deletedFlowEntries.add(oldFlowEntry);
+ }
+ }
+
+ //
+ // Find the new Flow Entries that should be added or updated
+ //
+ int idx = 0;
+ for (FlowEntry newFlowEntry : newDataPath.flowEntries()) {
+ FlowEntry oldFlowEntry =
+ oldFlowEntriesMap.get(newFlowEntry.dpid().value());
+
+ if ((oldFlowEntry != null) &&
+ TopologyManager.isSameFlowEntryDataPath(oldFlowEntry,
+ newFlowEntry)) {
+ //
+ // Both Flow Entries are same
+ //
+ finalFlowEntries.add(oldFlowEntry);
+ idx++;
+ continue;
+ }
+
+ if (oldFlowEntry != null) {
+ //
+ // The old Flow Entry should be deleted: path diverges
+ //
+ oldFlowEntry.setFlowEntryUserState(FlowEntryUserState.FE_USER_DELETE);
+ oldFlowEntry.setFlowEntrySwitchState(FlowEntrySwitchState.FE_SWITCH_NOT_UPDATED);
+ deletedFlowEntries.add(oldFlowEntry);
+ }
+
+ //
+ // Add the new Flow Entry
+ //
+ //
+ // NOTE: Assign only the Flow ID.
+ // The Flow Entry ID is assigned later only for the Flow Entries
+ // this instance is responsible for.
+ //
+ newFlowEntry.setFlowId(new FlowId(flowPath.flowId().value()));
+
+ // Set the incoming port matching
+ FlowEntryMatch flowEntryMatch = new FlowEntryMatch();
+ newFlowEntry.setFlowEntryMatch(flowEntryMatch);
+ flowEntryMatch.enableInPort(newFlowEntry.inPort());
+
+ //
+ // Set the actions:
+ // If the first Flow Entry, copy the Flow Path actions to it.
+ //
+ FlowEntryActions flowEntryActions = newFlowEntry.flowEntryActions();
+ if ((idx == 0) && (flowPath.flowEntryActions() != null)) {
+ FlowEntryActions flowActions =
+ new FlowEntryActions(flowPath.flowEntryActions());
+ for (FlowEntryAction action : flowActions.actions())
+ flowEntryActions.addAction(action);
+ }
+ idx++;
+
+ //
+ // Add the outgoing port output action
+ //
+ FlowEntryAction flowEntryAction = new FlowEntryAction();
+ flowEntryAction.setActionOutput(newFlowEntry.outPort());
+ flowEntryActions.addAction(flowEntryAction);
+
+ //
+ // Set the state of the new Flow Entry
+ //
+ newFlowEntry.setFlowEntryUserState(FlowEntryUserState.FE_USER_ADD);
+ newFlowEntry.setFlowEntrySwitchState(FlowEntrySwitchState.FE_SWITCH_NOT_UPDATED);
+ finalFlowEntries.add(newFlowEntry);
+ }
+
+ //
+ // Replace the old Flow Entries with the new Flow Entries.
+ // Note that the Flow Entries that will be deleted are added at
+ // the end.
+ //
+ finalFlowEntries.addAll(deletedFlowEntries);
+ flowPath.dataPath().setFlowEntries(finalFlowEntries);
+
+ return hasChanged;
+ }
+
+ /**
+ * Receive a notification that a Flow is added.
+ *
+ * @param flowPath the Flow that is added.
+ */
+ @Override
+ public void notificationRecvFlowAdded(FlowPath flowPath) {
+ EventEntry<FlowPath> eventEntry =
+ new EventEntry<FlowPath>(EventEntry.Type.ENTRY_ADD, flowPath);
+ networkEvents.add(eventEntry);
+ }
+
+ /**
+ * Receive a notification that a Flow is removed.
+ *
+ * @param flowPath the Flow that is removed.
+ */
+ @Override
+ public void notificationRecvFlowRemoved(FlowPath flowPath) {
+ EventEntry<FlowPath> eventEntry =
+ new EventEntry<FlowPath>(EventEntry.Type.ENTRY_REMOVE, flowPath);
+ networkEvents.add(eventEntry);
+ }
+
+ /**
+ * Receive a notification that a Flow is updated.
+ *
+ * @param flowPath the Flow that is updated.
+ */
+ @Override
+ public void notificationRecvFlowUpdated(FlowPath flowPath) {
+ // NOTE: The ADD and UPDATE events are processed in same way
+ EventEntry<FlowPath> eventEntry =
+ new EventEntry<FlowPath>(EventEntry.Type.ENTRY_ADD, flowPath);
+ networkEvents.add(eventEntry);
+ }
+
+ /**
+ * Receive a notification that a FlowEntry is added.
+ *
+ * @param flowEntry the FlowEntry that is added.
+ */
+ @Override
+ public void notificationRecvFlowEntryAdded(FlowEntry flowEntry) {
+ EventEntry<FlowEntry> eventEntry =
+ new EventEntry<FlowEntry>(EventEntry.Type.ENTRY_ADD, flowEntry);
+ networkEvents.add(eventEntry);
+ }
+
+ /**
+ * Receive a notification that a FlowEntry is removed.
+ *
+ * @param flowEntry the FlowEntry that is removed.
+ */
+ @Override
+ public void notificationRecvFlowEntryRemoved(FlowEntry flowEntry) {
+ EventEntry<FlowEntry> eventEntry =
+ new EventEntry<FlowEntry>(EventEntry.Type.ENTRY_REMOVE, flowEntry);
+ networkEvents.add(eventEntry);
+ }
+
+ /**
+ * Receive a notification that a FlowEntry is updated.
+ *
+ * @param flowEntry the FlowEntry that is updated.
+ */
+ @Override
+ public void notificationRecvFlowEntryUpdated(FlowEntry flowEntry) {
+ // NOTE: The ADD and UPDATE events are processed in same way
+ EventEntry<FlowEntry> eventEntry =
+ new EventEntry<FlowEntry>(EventEntry.Type.ENTRY_ADD, flowEntry);
+ networkEvents.add(eventEntry);
+ }
+
+ /**
+ * Receive a notification that a Topology Element is added.
+ *
+ * @param topologyElement the Topology Element that is added.
+ */
+ @Override
+ public void notificationRecvTopologyElementAdded(TopologyElement topologyElement) {
+ EventEntry<TopologyElement> eventEntry =
+ new EventEntry<TopologyElement>(EventEntry.Type.ENTRY_ADD, topologyElement);
+ networkEvents.add(eventEntry);
+ }
+
+ /**
+ * Receive a notification that a Topology Element is removed.
+ *
+ * @param topologyElement the Topology Element that is removed.
+ */
+ @Override
+ public void notificationRecvTopologyElementRemoved(TopologyElement topologyElement) {
+ EventEntry<TopologyElement> eventEntry =
+ new EventEntry<TopologyElement>(EventEntry.Type.ENTRY_REMOVE, topologyElement);
+ networkEvents.add(eventEntry);
+ }
+
+ /**
+ * Receive a notification that a Topology Element is updated.
+ *
+ * @param topologyElement the Topology Element that is updated.
+ */
+ @Override
+ public void notificationRecvTopologyElementUpdated(TopologyElement topologyElement) {
+ // NOTE: The ADD and UPDATE events are processed in same way
+ EventEntry<TopologyElement> eventEntry =
+ new EventEntry<TopologyElement>(EventEntry.Type.ENTRY_ADD, topologyElement);
+ networkEvents.add(eventEntry);
+ }
+}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/FlowManager.java b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/FlowManager.java
index 1ef4431..ab4be1a 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/FlowManager.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/FlowManager.java
@@ -18,6 +18,7 @@
import net.floodlightcontroller.core.module.IFloodlightModule;
import net.floodlightcontroller.core.module.IFloodlightService;
import net.floodlightcontroller.restserver.IRestApiService;
+import net.floodlightcontroller.util.OFMessageDamper;
import net.onrc.onos.datagrid.IDatagridService;
import net.onrc.onos.graph.GraphDBOperation;
import net.onrc.onos.ofcontroller.core.INetMapStorage;
@@ -26,33 +27,49 @@
import net.onrc.onos.ofcontroller.floodlightlistener.INetworkGraphService;
import net.onrc.onos.ofcontroller.flowmanager.web.FlowWebRoutable;
import net.onrc.onos.ofcontroller.flowprogrammer.FlowPusher;
-import net.onrc.onos.ofcontroller.flowprogrammer.IFlowPusherService;
import net.onrc.onos.ofcontroller.topology.ITopologyNetService;
import net.onrc.onos.ofcontroller.topology.Topology;
import net.onrc.onos.ofcontroller.topology.TopologyElement;
import net.onrc.onos.ofcontroller.util.*;
-import org.openflow.protocol.OFMessage;
+import org.openflow.protocol.OFType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Flow Manager class for handling the network flows.
*/
-public class FlowManager implements IFloodlightModule, IFlowService, INetMapStorage,
- IFlowPusherService {
+public class FlowManager implements IFloodlightModule, IFlowService, INetMapStorage {
- protected GraphDBOperation dbHandler;
+ //
+ // TODO: A temporary variable to switch between the poll-based and
+ // notification mechanism for the Flow Manager.
+ //
+ private final static boolean enableNotifications = false;
+
+ protected GraphDBOperation dbHandlerApi;
+ protected GraphDBOperation dbHandlerInner;
protected volatile IFloodlightProviderService floodlightProvider;
protected volatile ITopologyNetService topologyNetService;
protected volatile IDatagridService datagridService;
protected IRestApiService restApi;
protected FloodlightModuleContext context;
- protected PathComputation pathComputation;
+ protected FlowEventHandler flowEventHandler;
protected FlowPusher pusher;
+ private static final int NUM_PUSHER_THREAD = 1;
+// LEGACY
+// protected OFMessageDamper messageDamper;
+//
+// //
+// // TODO: Values copied from elsewhere (class LearningSwitch).
+// // The local copy should go away!
+// //
+// protected static final int OFMESSAGE_DAMPER_CAPACITY = 50000; // TODO: find sweet spot
+// protected static final int OFMESSAGE_DAMPER_TIMEOUT = 250; // ms
+
// Flow Entry ID generation state
private static Random randomGenerator = new Random();
private static int nextFlowEntryIdPrefix = 0;
@@ -76,7 +93,7 @@
runImpl();
} catch (Exception e) {
log.debug("Exception processing All Flow Entries from the Network MAP: ", e);
- dbHandler.rollback();
+ dbHandlerInner.rollback();
return;
}
}
@@ -107,10 +124,8 @@
// switches.
//
Iterable<IFlowEntry> allFlowEntries =
- dbHandler.getAllSwitchNotUpdatedFlowEntries();
+ dbHandlerInner.getAllSwitchNotUpdatedFlowEntries();
for (IFlowEntry flowEntryObj : allFlowEntries) {
- log.debug("flowEntryobj : {}", flowEntryObj);
-
counterAllFlowEntries++;
String dpidStr = flowEntryObj.getSwitchDpid();
@@ -122,7 +137,7 @@
continue; // Ignore the entry: not my switch
IFlowPath flowObj =
- dbHandler.getFlowPathByFlowEntry(flowEntryObj);
+ dbHandlerInner.getFlowPathByFlowEntry(flowEntryObj);
if (flowObj == null)
continue; // Should NOT happen
if (flowObj.getFlowId() == null)
@@ -145,15 +160,13 @@
}
counterMyNotUpdatedFlowEntries++;
}
-
- log.debug("addFlowEntries : {}", addFlowEntries);
//
// Process the Flow Entries that need to be added
//
for (IFlowEntry flowEntryObj : addFlowEntries) {
IFlowPath flowObj =
- dbHandler.getFlowPathByFlowEntry(flowEntryObj);
+ dbHandlerInner.getFlowPathByFlowEntry(flowEntryObj);
if (flowObj == null)
continue; // Should NOT happen
if (flowObj.getFlowId() == null)
@@ -177,16 +190,16 @@
while (! deleteFlowEntries.isEmpty()) {
IFlowEntry flowEntryObj = deleteFlowEntries.poll();
IFlowPath flowObj =
- dbHandler.getFlowPathByFlowEntry(flowEntryObj);
+ dbHandlerInner.getFlowPathByFlowEntry(flowEntryObj);
if (flowObj == null) {
log.debug("Did not find FlowPath to be deleted");
continue;
}
flowObj.removeFlowEntry(flowEntryObj);
- dbHandler.removeFlowEntry(flowEntryObj);
+ dbHandlerInner.removeFlowEntry(flowEntryObj);
}
- dbHandler.commit();
+ dbHandlerInner.commit();
long estimatedTime = System.nanoTime() - startTime;
double rate = 0.0;
@@ -211,7 +224,7 @@
runImpl();
} catch (Exception e) {
log.debug("Exception processing All Flows from the Network MAP: ", e);
- dbHandler.rollback();
+ dbHandlerInner.rollback();
return;
}
}
@@ -238,7 +251,7 @@
// Flow Paths this controller is responsible for.
//
Topology topology = topologyNetService.newDatabaseTopology();
- Iterable<IFlowPath> allFlowPaths = dbHandler.getAllFlowPaths();
+ Iterable<IFlowPath> allFlowPaths = dbHandlerInner.getAllFlowPaths();
for (IFlowPath flowPathObj : allFlowPaths) {
counterAllFlowPaths++;
if (flowPathObj == null)
@@ -340,12 +353,12 @@
//
while (! deleteFlows.isEmpty()) {
IFlowPath flowPathObj = deleteFlows.poll();
- dbHandler.removeFlowPath(flowPathObj);
+ dbHandlerInner.removeFlowPath(flowPathObj);
}
topologyNetService.dropTopology(topology);
- dbHandler.commit();
+ dbHandlerInner.commit();
long estimatedTime = System.nanoTime() - startTime;
double rate = 0.0;
@@ -368,7 +381,8 @@
*/
@Override
public void init(String conf) {
- dbHandler = new GraphDBOperation(conf);
+ dbHandlerApi = new GraphDBOperation(conf);
+ dbHandlerInner = new GraphDBOperation(conf);
}
/**
@@ -383,9 +397,9 @@
*/
@Override
public void close() {
- datagridService.deregisterPathComputationService(pathComputation);
- dbHandler.close();
- pusher.stop();
+ datagridService.deregisterFlowEventHandlerService(flowEventHandler);
+ dbHandlerApi.close();
+ dbHandlerInner.close();
}
/**
@@ -447,10 +461,14 @@
topologyNetService = context.getServiceImpl(ITopologyNetService.class);
datagridService = context.getServiceImpl(IDatagridService.class);
restApi = context.getServiceImpl(IRestApiService.class);
-
- pusher = new FlowPusher();
+
+// LEGACY
+// messageDamper = new OFMessageDamper(OFMESSAGE_DAMPER_CAPACITY,
+// EnumSet.of(OFType.FLOW_MOD),
+// OFMESSAGE_DAMPER_TIMEOUT);
+
+ pusher = new FlowPusher(NUM_PUSHER_THREAD);
pusher.init(null, floodlightProvider.getOFMessageFactory(), null);
-
this.init("");
mapReaderScheduler = Executors.newScheduledThreadPool(1);
@@ -493,20 +511,22 @@
nextFlowEntryIdPrefix = randomGenerator.nextInt();
pusher.start();
-
+
//
- // Create the Path Computation thread and register it with the
+ // Create the Flow Event Handler thread and register it with the
// Datagrid Service
//
- pathComputation = new PathComputation(this, datagridService);
- datagridService.registerPathComputationService(pathComputation);
+ flowEventHandler = new FlowEventHandler(this, datagridService);
+ datagridService.registerFlowEventHandlerService(flowEventHandler);
// Schedule the threads and periodic tasks
- pathComputation.start();
- mapReaderScheduler.scheduleAtFixedRate(
+ flowEventHandler.start();
+ if (! enableNotifications) {
+ mapReaderScheduler.scheduleAtFixedRate(
mapReader, 3, 3, TimeUnit.SECONDS);
- shortestPathReconcileScheduler.scheduleAtFixedRate(
+ shortestPathReconcileScheduler.scheduleAtFixedRate(
shortestPathReconcile, 3, 3, TimeUnit.SECONDS);
+ }
}
/**
@@ -519,7 +539,7 @@
@Override
public boolean addFlow(FlowPath flowPath, FlowId flowId) {
//
- // NOTE: We need to explicitly initialize the Flow Entry Switch State,
+ // NOTE: We need to explicitly initialize some of the state,
// in case the application didn't do it.
//
for (FlowEntry flowEntry : flowPath.flowEntries()) {
@@ -527,9 +547,11 @@
FlowEntrySwitchState.FE_SWITCH_UNKNOWN) {
flowEntry.setFlowEntrySwitchState(FlowEntrySwitchState.FE_SWITCH_NOT_UPDATED);
}
+ if (! flowEntry.isValidFlowId())
+ flowEntry.setFlowId(new FlowId(flowPath.flowId().value()));
}
- if (FlowDatabaseOperation.addFlow(this, dbHandler, flowPath, flowId)) {
+ if (FlowDatabaseOperation.addFlow(this, dbHandlerApi, flowPath, flowId)) {
datagridService.notificationSendFlowAdded(flowPath);
return true;
}
@@ -544,8 +566,20 @@
* @return the added Flow Entry object on success, otherwise null.
*/
private IFlowEntry addFlowEntry(IFlowPath flowObj, FlowEntry flowEntry) {
- return FlowDatabaseOperation.addFlowEntry(this, dbHandler, flowObj,
- flowEntry);
+ return FlowDatabaseOperation.addFlowEntry(this, dbHandlerInner,
+ flowObj, flowEntry);
+ }
+
+ /**
+ * Delete a flow entry from the Network MAP.
+ *
+ * @param flowObj the corresponding Flow Path object for the Flow Entry.
+ * @param flowEntry the Flow Entry to delete.
+ * @return true on success, otherwise false.
+ */
+ private boolean deleteFlowEntry(IFlowPath flowObj, FlowEntry flowEntry) {
+ return FlowDatabaseOperation.deleteFlowEntry(dbHandlerInner,
+ flowObj, flowEntry);
}
/**
@@ -555,7 +589,7 @@
*/
@Override
public boolean deleteAllFlows() {
- if (FlowDatabaseOperation.deleteAllFlows(dbHandler)) {
+ if (FlowDatabaseOperation.deleteAllFlows(dbHandlerApi)) {
datagridService.notificationSendAllFlowsRemoved();
return true;
}
@@ -570,7 +604,7 @@
*/
@Override
public boolean deleteFlow(FlowId flowId) {
- if (FlowDatabaseOperation.deleteFlow(dbHandler, flowId)) {
+ if (FlowDatabaseOperation.deleteFlow(dbHandlerApi, flowId)) {
datagridService.notificationSendFlowRemoved(flowId);
return true;
}
@@ -584,7 +618,7 @@
*/
@Override
public boolean clearAllFlows() {
- if (FlowDatabaseOperation.clearAllFlows(dbHandler)) {
+ if (FlowDatabaseOperation.clearAllFlows(dbHandlerApi)) {
datagridService.notificationSendAllFlowsRemoved();
return true;
}
@@ -599,7 +633,7 @@
*/
@Override
public boolean clearFlow(FlowId flowId) {
- if (FlowDatabaseOperation.clearFlow(dbHandler, flowId)) {
+ if (FlowDatabaseOperation.clearFlow(dbHandlerApi, flowId)) {
datagridService.notificationSendFlowRemoved(flowId);
return true;
}
@@ -614,7 +648,7 @@
*/
@Override
public FlowPath getFlow(FlowId flowId) {
- return FlowDatabaseOperation.getFlow(dbHandler, flowId);
+ return FlowDatabaseOperation.getFlow(dbHandlerApi, flowId);
}
/**
@@ -624,7 +658,7 @@
*/
@Override
public ArrayList<FlowPath> getAllFlows() {
- return FlowDatabaseOperation.getAllFlows(dbHandler);
+ return FlowDatabaseOperation.getAllFlows(dbHandlerApi);
}
/**
@@ -638,7 +672,7 @@
@Override
public ArrayList<FlowPath> getAllFlows(CallerId installerId,
DataPathEndpoints dataPathEndpoints) {
- return FlowDatabaseOperation.getAllFlows(dbHandler, installerId,
+ return FlowDatabaseOperation.getAllFlows(dbHandlerApi, installerId,
dataPathEndpoints);
}
@@ -650,7 +684,8 @@
*/
@Override
public ArrayList<FlowPath> getAllFlows(DataPathEndpoints dataPathEndpoints) {
- return FlowDatabaseOperation.getAllFlows(dbHandler, dataPathEndpoints);
+ return FlowDatabaseOperation.getAllFlows(dbHandlerApi,
+ dataPathEndpoints);
}
/**
@@ -663,7 +698,7 @@
@Override
public ArrayList<IFlowPath> getAllFlowsSummary(FlowId flowId,
int maxFlows) {
- return FlowDatabaseOperation.getAllFlowsSummary(dbHandler, flowId,
+ return FlowDatabaseOperation.getAllFlowsSummary(dbHandlerApi, flowId,
maxFlows);
}
@@ -673,7 +708,7 @@
* @return all Flows information, without the associated Flow Entries.
*/
public ArrayList<IFlowPath> getAllFlowsWithoutFlowEntries() {
- return FlowDatabaseOperation.getAllFlowsWithoutFlowEntries(dbHandler);
+ return FlowDatabaseOperation.getAllFlowsWithoutFlowEntries(dbHandlerApi);
}
/**
@@ -700,6 +735,24 @@
}
/**
+ * Get the collection of my switches.
+ *
+ * @return the collection of my switches.
+ */
+ public Map<Long, IOFSwitch> getMySwitches() {
+ return floodlightProvider.getSwitches();
+ }
+
+ /**
+ * Get the network topology.
+ *
+ * @return the network topology.
+ */
+ public Topology getTopology() {
+ return flowEventHandler.getTopology();
+ }
+
+ /**
* Reconcile a flow.
*
* @param flowObj the flow that needs to be reconciliated.
@@ -707,6 +760,7 @@
* @return true on success, otherwise false.
*/
private boolean reconcileFlow(IFlowPath flowObj, DataPath newDataPath) {
+ String flowIdStr = flowObj.getFlowId();
//
// Set the incoming port matching and the outgoing port output
@@ -714,6 +768,8 @@
//
int idx = 0;
for (FlowEntry flowEntry : newDataPath.flowEntries()) {
+ flowEntry.setFlowId(new FlowId(flowIdStr));
+
// Mark the Flow Entry as not updated in the switch
flowEntry.setFlowEntrySwitchState(FlowEntrySwitchState.FE_SWITCH_NOT_UPDATED);
// Set the incoming port matching
@@ -788,6 +844,11 @@
private boolean installFlowEntry(IOFSwitch mySwitch, IFlowPath flowObj,
IFlowEntry flowEntryObj) {
return pusher.add(mySwitch, flowObj, flowEntryObj);
+
+// LEGACY
+// return FlowSwitchOperation.installFlowEntry(
+// floodlightProvider.getOFMessageFactory(),
+// messageDamper, mySwitch, flowObj, flowEntryObj);
}
/**
@@ -801,6 +862,11 @@
private boolean installFlowEntry(IOFSwitch mySwitch, FlowPath flowPath,
FlowEntry flowEntry) {
return pusher.add(mySwitch, flowPath, flowEntry);
+
+// LEGACY
+// return FlowSwitchOperation.installFlowEntry(
+// floodlightProvider.getOFMessageFactory(),
+// messageDamper, mySwitch, flowPath, flowEntry);
}
/**
@@ -821,130 +887,217 @@
}
/**
- * Push the modified Flow Entries of a collection of Flow Paths.
- * Only the Flow Entries to switches controlled by this instance
+ * Push modified Flow Entries to switches.
+ *
+ * NOTE: Only the Flow Entries to switches controlled by this instance
* are pushed.
*
- * NOTE: Currently, we write to both the Network MAP and the switches.
- *
- * @param modifiedFlowPaths the collection of Flow Paths with the modified
- * Flow Entries.
+ * @param modifiedFlowEntries the collection of modified Flow Entries.
*/
- public void pushModifiedFlowEntries(Collection<FlowPath> modifiedFlowPaths) {
-
+ public void pushModifiedFlowEntriesToSwitches(
+ Collection<FlowPathEntryPair> modifiedFlowEntries) {
// TODO: For now, the pushing of Flow Entries is disabled
- if (true)
+ if (! enableNotifications)
return;
- Map<Long, IOFSwitch> mySwitches = floodlightProvider.getSwitches();
+ if (modifiedFlowEntries.isEmpty())
+ return;
- for (FlowPath flowPath : modifiedFlowPaths) {
- IFlowPath flowObj = dbHandler.searchFlowPath(flowPath.flowId());
- if (flowObj == null) {
- String logMsg = "Cannot find Network MAP entry for Flow Path " +
- flowPath.flowId();
+ Map<Long, IOFSwitch> mySwitches = getMySwitches();
+
+ for (FlowPathEntryPair flowPair : modifiedFlowEntries) {
+ FlowPath flowPath = flowPair.flowPath;
+ FlowEntry flowEntry = flowPair.flowEntry;
+
+ IOFSwitch mySwitch = mySwitches.get(flowEntry.dpid().value());
+ if (mySwitch == null)
+ continue;
+
+ log.debug("Pushing Flow Entry To Switch: {}", flowEntry.toString());
+
+ //
+ // Install the Flow Entry into the switch
+ //
+ if (! installFlowEntry(mySwitch, flowPath, flowEntry)) {
+ String logMsg = "Cannot install Flow Entry " +
+ flowEntry.flowEntryId() +
+ " from Flow Path " + flowPath.flowId() +
+ " on switch " + flowEntry.dpid();
log.error(logMsg);
continue;
}
- for (FlowEntry flowEntry : flowPath.flowEntries()) {
- if (flowEntry.flowEntrySwitchState() !=
- FlowEntrySwitchState.FE_SWITCH_NOT_UPDATED) {
- continue; // No need to update the entry
- }
+ //
+ // NOTE: Here we assume that the switch has been
+ // successfully updated.
+ //
+ flowEntry.setFlowEntrySwitchState(FlowEntrySwitchState.FE_SWITCH_UPDATED);
+ }
+ }
- IOFSwitch mySwitch = mySwitches.get(flowEntry.dpid().value());
+ /**
+ * Push modified Flow Entries to the datagrid.
+ *
+ * @param modifiedFlowEntries the collection of modified Flow Entries.
+ */
+ public void pushModifiedFlowEntriesToDatagrid(
+ Collection<FlowPathEntryPair> modifiedFlowEntries) {
+ // TODO: For now, the pushing of Flow Entries is disabled
+ if (! enableNotifications)
+ return;
+
+ if (modifiedFlowEntries.isEmpty())
+ return;
+
+ Map<Long, IOFSwitch> mySwitches = getMySwitches();
+
+ for (FlowPathEntryPair flowPair : modifiedFlowEntries) {
+ FlowEntry flowEntry = flowPair.flowEntry;
+
+ IOFSwitch mySwitch = mySwitches.get(flowEntry.dpid().value());
+
+ //
+ // TODO: For now Flow Entries are removed by all instances,
+ // even if this Flow Entry is not for our switches.
+ //
+ // This is needed to handle the case a switch going down:
+ // it has no Master controller instance, hence no
+ // controller instance will cleanup its flow entries.
+ // This is sub-optimal: we need to elect a controller
+ // instance to handle the cleanup of such orphaned flow
+ // entries.
+ //
+ if (mySwitch == null) {
+ if (flowEntry.flowEntryUserState() !=
+ FlowEntryUserState.FE_USER_DELETE) {
+ continue;
+ }
+ if (! flowEntry.isValidFlowEntryId())
+ continue;
+ }
+
+ log.debug("Pushing Flow Entry To Datagrid: {}", flowEntry.toString());
+ //
+ // Write the Flow Entry to the Datagrid
+ //
+ switch (flowEntry.flowEntryUserState()) {
+ case FE_USER_ADD:
if (mySwitch == null)
- continue; // Ignore the entry: not my switch
+ break; // Install only flow entries for my switches
+ datagridService.notificationSendFlowEntryAdded(flowEntry);
+ break;
+ case FE_USER_MODIFY:
+ if (mySwitch == null)
+ break; // Install only flow entries for my switches
+ datagridService.notificationSendFlowEntryUpdated(flowEntry);
+ break;
+ case FE_USER_DELETE:
+ datagridService.notificationSendFlowEntryRemoved(flowEntry.flowEntryId());
+ break;
+ }
+ }
+ }
- //
- // Assign the FlowEntry ID if needed
- //
- if ((flowEntry.flowEntryId() == null) ||
- (flowEntry.flowEntryId().value() == 0)) {
- long id = getNextFlowEntryId();
- flowEntry.setFlowEntryId(new FlowEntryId(id));
- }
+ /**
+ * Push Flow Entries to the Network MAP.
+ *
+ * NOTE: The Flow Entries are pushed only on the instance responsible
+ * for the first switch. This is to avoid database errors when multiple
+ * instances are writing Flow Entries for the same Flow Path.
+ *
+ * @param modifiedFlowEntries the collection of Flow Entries to push.
+ */
+ public void pushModifiedFlowEntriesToDatabase(
+ Collection<FlowPathEntryPair> modifiedFlowEntries) {
+ // TODO: For now, the pushing of Flow Entries is disabled
+ if (! enableNotifications)
+ return;
- //
- // Install the Flow Entry into the switch
- //
- if (! installFlowEntry(mySwitch, flowPath, flowEntry)) {
- String logMsg = "Cannot install Flow Entry " +
- flowEntry.flowEntryId() +
- " from Flow Path " + flowPath.flowId() +
- " on switch " + flowEntry.dpid();
- log.error(logMsg);
- continue;
- }
+ if (modifiedFlowEntries.isEmpty())
+ return;
- //
- // NOTE: Here we assume that the switch has been successfully
- // updated.
- //
- flowEntry.setFlowEntrySwitchState(FlowEntrySwitchState.FE_SWITCH_UPDATED);
+ Map<Long, IOFSwitch> mySwitches = getMySwitches();
- //
- // Write the Flow Entry to the Network Map
- //
+ for (FlowPathEntryPair flowPair : modifiedFlowEntries) {
+ FlowPath flowPath = flowPair.flowPath;
+ FlowEntry flowEntry = flowPair.flowEntry;
+
+ if (! flowEntry.isValidFlowEntryId())
+ continue;
+
+ //
+ // Push the changes only on the instance responsible for the
+ // first switch.
+ //
+ Dpid srcDpid = flowPath.dataPath().srcPort().dpid();
+ IOFSwitch mySrcSwitch = mySwitches.get(srcDpid.value());
+ if (mySrcSwitch == null)
+ continue;
+
+ log.debug("Pushing Flow Entry To Database: {}", flowEntry.toString());
+ //
+ // Write the Flow Entry to the Network Map
+ //
+ // NOTE: We try a number of times, in case somehow some other
+ // instances are writing at the same time.
+ // Apparently, if other instances are writing at the same time
+ // this will trigger an error.
+ //
+ for (int i = 0; i < 6; i++) {
try {
- if (addFlowEntry(flowObj, flowEntry) == null) {
- String logMsg = "Cannot write to Network MAP Flow Entry " +
- flowEntry.flowEntryId() +
- " from Flow Path " + flowPath.flowId() +
- " on switch " + flowEntry.dpid();
+ //
+ // Find the Flow Path in the Network MAP.
+ //
+ // NOTE: The Flow Path might not be found if the Flow was
+ // just removed by some other controller instance.
+ //
+ IFlowPath flowObj =
+ dbHandlerInner.searchFlowPath(flowEntry.flowId());
+ if (flowObj == null) {
+ String logMsg = "Cannot find Network MAP entry for Flow Path " + flowEntry.flowId();
log.error(logMsg);
- continue;
+ break;
}
+
+ // Write the Flow Entry
+ switch (flowEntry.flowEntryUserState()) {
+ case FE_USER_ADD:
+ // FALLTHROUGH
+ case FE_USER_MODIFY:
+ if (addFlowEntry(flowObj, flowEntry) == null) {
+ String logMsg = "Cannot write to Network MAP Flow Entry " +
+ flowEntry.flowEntryId() +
+ " from Flow Path " + flowEntry.flowId() +
+ " on switch " + flowEntry.dpid();
+ log.error(logMsg);
+ }
+ break;
+ case FE_USER_DELETE:
+ if (deleteFlowEntry(flowObj, flowEntry) == false) {
+ String logMsg = "Cannot remove from Network MAP Flow Entry " +
+ flowEntry.flowEntryId() +
+ " from Flow Path " + flowEntry.flowId() +
+ " on switch " + flowEntry.dpid();
+ log.error(logMsg);
+ }
+ break;
+ }
+
+ // Commit to the database
+ dbHandlerInner.commit();
+ break; // Success
+
} catch (Exception e) {
- String logMsg = "Exception writing Flow Entry to Network MAP";
- log.debug(logMsg);
- dbHandler.rollback();
- continue;
+ log.debug("Exception writing Flow Entry to Network MAP: ", e);
+ dbHandlerInner.rollback();
+ // Wait a bit (random value [1ms, 20ms] and try again
+ int delay = 1 + randomGenerator.nextInt() % 20;
+ try {
+ Thread.sleep(delay);
+ } catch (Exception e0) {
+ }
}
}
}
-
- dbHandler.commit();
}
-
- @Override
- public void addMessage(long dpid, OFMessage msg) {
- IOFSwitch sw = floodlightProvider.getSwitches().get(dpid);
- if (sw == null) {
- return;
- }
-
- pusher.add(sw, msg);
- }
-
- @Override
- public boolean suspend(long dpid) {
- IOFSwitch sw = floodlightProvider.getSwitches().get(dpid);
- if (sw == null) {
- return false;
- }
-
- return pusher.suspend(sw);
- }
-
- @Override
- public boolean resume(long dpid) {
- IOFSwitch sw = floodlightProvider.getSwitches().get(dpid);
- if (sw == null) {
- return false;
- }
-
- return pusher.resume(sw);
- }
-
- @Override
- public boolean isSuspended(long dpid) {
- IOFSwitch sw = floodlightProvider.getSwitches().get(dpid);
- if (sw == null) {
- return false;
- }
-
- return pusher.isSuspended(sw);
- }
}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/IFlowEventHandlerService.java b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/IFlowEventHandlerService.java
new file mode 100644
index 0000000..78562e1
--- /dev/null
+++ b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/IFlowEventHandlerService.java
@@ -0,0 +1,73 @@
+package net.onrc.onos.ofcontroller.flowmanager;
+
+import net.onrc.onos.ofcontroller.topology.TopologyElement;
+import net.onrc.onos.ofcontroller.util.FlowEntry;
+import net.onrc.onos.ofcontroller.util.FlowPath;
+
+/**
+ * Interface for providing Flow Event Handler Service to other modules.
+ */
+public interface IFlowEventHandlerService {
+ /**
+ * Receive a notification that a Flow is added.
+ *
+ * @param flowPath the Flow that is added.
+ */
+ void notificationRecvFlowAdded(FlowPath flowPath);
+
+ /**
+ * Receive a notification that a Flow is removed.
+ *
+ * @param flowPath the Flow that is removed.
+ */
+ void notificationRecvFlowRemoved(FlowPath flowPath);
+
+ /**
+ * Receive a notification that a Flow is updated.
+ *
+ * @param flowPath the Flow that is updated.
+ */
+ void notificationRecvFlowUpdated(FlowPath flowPath);
+
+ /**
+ * Receive a notification that a FlowEntry is added.
+ *
+ * @param flowEntry the FlowEntry that is added.
+ */
+ void notificationRecvFlowEntryAdded(FlowEntry flowEntry);
+
+ /**
+ * Receive a notification that a FlowEntry is removed.
+ *
+ * @param flowEntry the FlowEntry that is removed.
+ */
+ void notificationRecvFlowEntryRemoved(FlowEntry flowEntry);
+
+ /**
+ * Receive a notification that a FlowEntry is updated.
+ *
+ * @param flowEntry the FlowEntry that is updated.
+ */
+ void notificationRecvFlowEntryUpdated(FlowEntry flowEntry);
+
+ /**
+ * Receive a notification that a Topology Element is added.
+ *
+ * @param topologyElement the Topology Element that is added.
+ */
+ void notificationRecvTopologyElementAdded(TopologyElement topologyElement);
+
+ /**
+ * Receive a notification that a Topology Element is removed.
+ *
+ * @param topologyElement the Topology Element that is removed.
+ */
+ void notificationRecvTopologyElementRemoved(TopologyElement topologyElement);
+
+ /**
+ * Receive a notification that a Topology Element is updated.
+ *
+ * @param topologyElement the Topology Element that is updated.
+ */
+ void notificationRecvTopologyElementUpdated(TopologyElement topologyElement);
+}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/IFlowService.java b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/IFlowService.java
index 1f8cd5b..8d362d1 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/IFlowService.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/IFlowService.java
@@ -4,6 +4,7 @@
import net.floodlightcontroller.core.module.IFloodlightService;
import net.onrc.onos.ofcontroller.core.INetMapTopologyObjects.IFlowPath;
+import net.onrc.onos.ofcontroller.topology.Topology;
import net.onrc.onos.ofcontroller.util.CallerId;
import net.onrc.onos.ofcontroller.util.DataPathEndpoints;
import net.onrc.onos.ofcontroller.util.FlowId;
@@ -112,4 +113,11 @@
* @return the added shortest-path flow on success, otherwise null.
*/
FlowPath addAndMaintainShortestPathFlow(FlowPath flowPath);
+
+ /**
+ * Get the network topology.
+ *
+ * @return the network topology.
+ */
+ Topology getTopology();
}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/IPathComputationService.java b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/IPathComputationService.java
deleted file mode 100644
index 1bc0be1..0000000
--- a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/IPathComputationService.java
+++ /dev/null
@@ -1,51 +0,0 @@
-package net.onrc.onos.ofcontroller.flowmanager;
-
-import net.onrc.onos.ofcontroller.topology.TopologyElement;
-import net.onrc.onos.ofcontroller.util.FlowPath;
-
-/**
- * Interface for providing Path Computation Service to other modules.
- */
-public interface IPathComputationService {
- /**
- * Receive a notification that a Flow is added.
- *
- * @param flowPath the flow that is added.
- */
- void notificationRecvFlowAdded(FlowPath flowPath);
-
- /**
- * Receive a notification that a Flow is removed.
- *
- * @param flowPath the flow that is removed.
- */
- void notificationRecvFlowRemoved(FlowPath flowPath);
-
- /**
- * Receive a notification that a Flow is updated.
- *
- * @param flowPath the flow that is updated.
- */
- void notificationRecvFlowUpdated(FlowPath flowPath);
-
- /**
- * Receive a notification that a Topology Element is added.
- *
- * @param topologyElement the Topology Element that is added.
- */
- void notificationRecvTopologyElementAdded(TopologyElement topologyElement);
-
- /**
- * Receive a notification that a Topology Element is removed.
- *
- * @param topologyElement the Topology Element that is removed.
- */
- void notificationRecvTopologyElementRemoved(TopologyElement topologyElement);
-
- /**
- * Receive a notification that a Topology Element is updated.
- *
- * @param topologyElement the Topology Element that is updated.
- */
- void notificationRecvTopologyElementUpdated(TopologyElement topologyElement);
-}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/PathComputation.java b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/PathComputation.java
deleted file mode 100644
index ae14e09..0000000
--- a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/PathComputation.java
+++ /dev/null
@@ -1,495 +0,0 @@
-package net.onrc.onos.ofcontroller.flowmanager;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.LinkedBlockingQueue;
-
-import net.onrc.onos.datagrid.IDatagridService;
-import net.onrc.onos.ofcontroller.topology.ShortestPath;
-import net.onrc.onos.ofcontroller.topology.Topology;
-import net.onrc.onos.ofcontroller.topology.TopologyElement;
-import net.onrc.onos.ofcontroller.topology.TopologyManager;
-import net.onrc.onos.ofcontroller.util.DataPath;
-import net.onrc.onos.ofcontroller.util.EventEntry;
-import net.onrc.onos.ofcontroller.util.FlowEntry;
-import net.onrc.onos.ofcontroller.util.FlowEntryAction;
-import net.onrc.onos.ofcontroller.util.FlowEntryActions;
-import net.onrc.onos.ofcontroller.util.FlowEntryMatch;
-import net.onrc.onos.ofcontroller.util.FlowEntrySwitchState;
-import net.onrc.onos.ofcontroller.util.FlowEntryUserState;
-import net.onrc.onos.ofcontroller.util.FlowPath;
-import net.onrc.onos.ofcontroller.util.FlowPathUserState;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Class for implementing the Path Computation and Path Maintenance.
- */
-class PathComputation extends Thread implements IPathComputationService {
- /** The logger. */
- private final static Logger log = LoggerFactory.getLogger(PathComputation.class);
-
- private FlowManager flowManager; // The Flow Manager to use
- private IDatagridService datagridService; // The Datagrid Service to use
- private Topology topology; // The network topology
- private Map<Long, FlowPath> allFlowPaths = new HashMap<Long, FlowPath>();
-
- // The queue with Flow Path and Topology Element updates
- private BlockingQueue<EventEntry<?>> networkEvents =
- new LinkedBlockingQueue<EventEntry<?>>();
-
- // The pending Topology and Flow Path events
- private List<EventEntry<TopologyElement>> topologyEvents =
- new LinkedList<EventEntry<TopologyElement>>();
- private List<EventEntry<FlowPath>> flowPathEvents =
- new LinkedList<EventEntry<FlowPath>>();
-
- /**
- * Constructor for a given Flow Manager and Datagrid Service.
- *
- * @param flowManager the Flow Manager to use.
- * @param datagridService the Datagrid Service to use.
- */
- PathComputation(FlowManager flowManager,
- IDatagridService datagridService) {
- this.flowManager = flowManager;
- this.datagridService = datagridService;
- this.topology = new Topology();
- }
-
- /**
- * Run the thread.
- */
- @Override
- public void run() {
- //
- // Obtain the initial Topology state
- //
- Collection<TopologyElement> topologyElements =
- datagridService.getAllTopologyElements();
- for (TopologyElement topologyElement : topologyElements) {
- EventEntry<TopologyElement> eventEntry =
- new EventEntry<TopologyElement>(EventEntry.Type.ENTRY_ADD, topologyElement);
- topologyEvents.add(eventEntry);
- }
- //
- // Obtain the initial Flow Path state
- //
- Collection<FlowPath> flowPaths = datagridService.getAllFlows();
- for (FlowPath flowPath : flowPaths) {
- EventEntry<FlowPath> eventEntry =
- new EventEntry<FlowPath>(EventEntry.Type.ENTRY_ADD, flowPath);
- flowPathEvents.add(eventEntry);
- }
- // Process the events (if any)
- processEvents();
-
- //
- // The main loop
- //
- Collection<EventEntry<?>> collection = new LinkedList<EventEntry<?>>();
- try {
- while (true) {
- EventEntry<?> eventEntry = networkEvents.take();
- collection.add(eventEntry);
- networkEvents.drainTo(collection);
-
- //
- // Demultiplex all events:
- // - EventEntry<TopologyElement>
- // - EventEntry<FlowPath>
- //
- for (EventEntry<?> event : collection) {
- if (event.eventData() instanceof TopologyElement) {
- EventEntry<TopologyElement> topologyEventEntry =
- (EventEntry<TopologyElement>)event;
- topologyEvents.add(topologyEventEntry);
- } else if (event.eventData() instanceof FlowPath) {
- EventEntry<FlowPath> flowPathEventEntry =
- (EventEntry<FlowPath>)event;
- flowPathEvents.add(flowPathEventEntry);
- }
- }
- collection.clear();
-
- // Process the events (if any)
- processEvents();
- }
- } catch (Exception exception) {
- log.debug("Exception processing Network Events: ", exception);
- }
- }
-
- /**
- * Process the events (if any)
- */
- private void processEvents() {
- List<FlowPath> newFlowPaths = new LinkedList<FlowPath>();
- List<FlowPath> recomputeFlowPaths = new LinkedList<FlowPath>();
- List<FlowPath> modifiedFlowPaths = new LinkedList<FlowPath>();
-
- if (topologyEvents.isEmpty() && flowPathEvents.isEmpty())
- return; // Nothing to do
-
- //
- // Process the Flow Path events
- //
- for (EventEntry<FlowPath> eventEntry : flowPathEvents) {
- FlowPath flowPath = eventEntry.eventData();
-
- switch (eventEntry.eventType()) {
- case ENTRY_ADD: {
- //
- // Add a new Flow Path
- //
- if (allFlowPaths.get(flowPath.flowId().value()) != null) {
- //
- // TODO: What to do if the Flow Path already exists?
- // Remove and then re-add it, or merge the info?
- // For now, we don't have to do anything.
- //
- break;
- }
-
- switch (flowPath.flowPathType()) {
- case FP_TYPE_SHORTEST_PATH:
- //
- // Reset the Data Path, in case it was set already, because
- // we are going to recompute it anyway.
- //
- flowPath.flowEntries().clear();
- recomputeFlowPaths.add(flowPath);
- break;
- case FP_TYPE_EXPLICIT_PATH:
- //
- // Mark all Flow Entries for installation in the switches.
- //
- for (FlowEntry flowEntry : flowPath.flowEntries()) {
- flowEntry.setFlowEntrySwitchState(FlowEntrySwitchState.FE_SWITCH_NOT_UPDATED);
- }
- modifiedFlowPaths.add(flowPath);
- break;
- }
- newFlowPaths.add(flowPath);
-
- break;
- }
-
- case ENTRY_REMOVE: {
- //
- // Remove an existing Flow Path.
- //
- // Find the Flow Path, and mark the Flow and its Flow Entries
- // for deletion.
- //
- FlowPath existingFlowPath =
- allFlowPaths.get(flowPath.flowId().value());
- if (existingFlowPath == null)
- continue; // Nothing to do
-
- existingFlowPath.setFlowPathUserState(FlowPathUserState.FP_USER_DELETE);
- for (FlowEntry flowEntry : existingFlowPath.flowEntries()) {
- flowEntry.setFlowEntryUserState(FlowEntryUserState.FE_USER_DELETE);
- flowEntry.setFlowEntrySwitchState(FlowEntrySwitchState.FE_SWITCH_NOT_UPDATED);
- }
-
- allFlowPaths.remove(existingFlowPath.flowId());
- modifiedFlowPaths.add(existingFlowPath);
-
- break;
- }
- }
- }
-
- //
- // Process the topology events
- //
- boolean isTopologyModified = false;
- for (EventEntry<TopologyElement> eventEntry : topologyEvents) {
- TopologyElement topologyElement = eventEntry.eventData();
- switch (eventEntry.eventType()) {
- case ENTRY_ADD:
- isTopologyModified = topology.addTopologyElement(topologyElement);
- break;
- case ENTRY_REMOVE:
- isTopologyModified = topology.removeTopologyElement(topologyElement);
- break;
- }
- }
- if (isTopologyModified) {
- // TODO: For now, if the topology changes, we recompute all Flows
- recomputeFlowPaths.addAll(allFlowPaths.values());
- }
-
- // Add all new Flows
- for (FlowPath flowPath : newFlowPaths) {
- allFlowPaths.put(flowPath.flowId().value(), flowPath);
- }
-
- // Recompute all affected Flow Paths and keep only the modified
- for (FlowPath flowPath : recomputeFlowPaths) {
- if (recomputeFlowPath(flowPath))
- modifiedFlowPaths.add(flowPath);
- }
-
- //
- // Push the Flow Entries that have been modified
- //
- flowManager.pushModifiedFlowEntries(modifiedFlowPaths);
-
- // Cleanup
- topologyEvents.clear();
- flowPathEvents.clear();
- }
-
- /**
- * Recompute a Flow Path.
- *
- * @param flowPath the Flow Path to recompute.
- * @return true if the recomputed Flow Path has changed, otherwise false.
- */
- private boolean recomputeFlowPath(FlowPath flowPath) {
- boolean hasChanged = false;
-
- //
- // Test whether the Flow Path needs to be recomputed
- //
- switch (flowPath.flowPathType()) {
- case FP_TYPE_SHORTEST_PATH:
- break;
- case FP_TYPE_EXPLICIT_PATH:
- return false; // An explicit path never changes
- }
-
- DataPath oldDataPath = flowPath.dataPath();
-
- // Compute the new path
- DataPath newDataPath = TopologyManager.computeNetworkPath(topology,
- flowPath);
- if (newDataPath == null) {
- // We need the DataPath to compare the paths
- newDataPath = new DataPath();
- }
- newDataPath.applyFlowPathFlags(flowPath.flowPathFlags());
-
- //
- // Test whether the new path is same
- //
- if (oldDataPath.flowEntries().size() !=
- newDataPath.flowEntries().size()) {
- hasChanged = true;
- } else {
- Iterator<FlowEntry> oldIter = oldDataPath.flowEntries().iterator();
- Iterator<FlowEntry> newIter = newDataPath.flowEntries().iterator();
- while (oldIter.hasNext() && newIter.hasNext()) {
- FlowEntry oldFlowEntry = oldIter.next();
- FlowEntry newFlowEntry = newIter.next();
- if (! TopologyManager.isSameFlowEntryDataPath(oldFlowEntry,
- newFlowEntry)) {
- hasChanged = true;
- break;
- }
- }
- }
- if (! hasChanged)
- return hasChanged;
-
- //
- // Merge the changes in the path:
- // - If a Flow Entry for a switch is in the old data path, but not
- // in the new data path, then mark it for deletion.
- // - If a Flow Entry for a switch is in the new data path, but not
- // in the old data path, then mark it for addition.
- // - If a Flow Entry for a switch is in both the old and the new
- // data path, but it has changed, e.g., the incoming and/or outgoing
- // port(s), then mark the old Flow Entry for deletion, and mark
- // the new Flow Entry for addition.
- // - If a Flow Entry for a switch is in both the old and the new
- // data path, and it hasn't changed, then just keep it.
- //
- // NOTE: We use the Switch DPID of each entry to match the entries
- //
- Map<Long, FlowEntry> oldFlowEntriesMap = new HashMap<Long, FlowEntry>();
- Map<Long, FlowEntry> newFlowEntriesMap = new HashMap<Long, FlowEntry>();
- ArrayList<FlowEntry> finalFlowEntries = new ArrayList<FlowEntry>();
- List<FlowEntry> deletedFlowEntries = new LinkedList<FlowEntry>();
-
- // Prepare maps with the Flow Entries, so they are fast to lookup
- for (FlowEntry flowEntry : oldDataPath.flowEntries())
- oldFlowEntriesMap.put(flowEntry.dpid().value(), flowEntry);
- for (FlowEntry flowEntry : newDataPath.flowEntries())
- newFlowEntriesMap.put(flowEntry.dpid().value(), flowEntry);
-
- //
- // Find the old Flow Entries that should be deleted
- //
- for (FlowEntry oldFlowEntry : oldDataPath.flowEntries()) {
- FlowEntry newFlowEntry =
- newFlowEntriesMap.get(oldFlowEntry.dpid().value());
- if (newFlowEntry == null) {
- // The old Flow Entry should be deleted
- oldFlowEntry.setFlowEntryUserState(FlowEntryUserState.FE_USER_DELETE);
- oldFlowEntry.setFlowEntrySwitchState(FlowEntrySwitchState.FE_SWITCH_NOT_UPDATED);
- deletedFlowEntries.add(oldFlowEntry);
- }
- }
-
- //
- // Find the new Flow Entries that should be added or updated
- //
- int idx = 0;
- for (FlowEntry newFlowEntry : newDataPath.flowEntries()) {
- FlowEntry oldFlowEntry =
- oldFlowEntriesMap.get(newFlowEntry.dpid().value());
-
- if ((oldFlowEntry != null) &&
- TopologyManager.isSameFlowEntryDataPath(oldFlowEntry,
- newFlowEntry)) {
- //
- // Both Flow Entries are same
- //
- finalFlowEntries.add(oldFlowEntry);
- idx++;
- continue;
- }
-
- if (oldFlowEntry != null) {
- //
- // The old Flow Entry should be deleted
- //
- oldFlowEntry.setFlowEntryUserState(FlowEntryUserState.FE_USER_DELETE);
- oldFlowEntry.setFlowEntrySwitchState(FlowEntrySwitchState.FE_SWITCH_NOT_UPDATED);
- deletedFlowEntries.add(oldFlowEntry);
- }
-
- //
- // Add the new Flow Entry
- //
-
- // Set the incoming port matching
- FlowEntryMatch flowEntryMatch = new FlowEntryMatch();
- newFlowEntry.setFlowEntryMatch(flowEntryMatch);
- flowEntryMatch.enableInPort(newFlowEntry.inPort());
-
- //
- // Set the actions:
- // If the first Flow Entry, copy the Flow Path actions to it.
- //
- FlowEntryActions flowEntryActions = newFlowEntry.flowEntryActions();
- if ((idx == 0) && (flowPath.flowEntryActions() != null)) {
- FlowEntryActions flowActions =
- new FlowEntryActions(flowPath.flowEntryActions());
- for (FlowEntryAction action : flowActions.actions())
- flowEntryActions.addAction(action);
- }
- idx++;
-
- //
- // Add the outgoing port output action
- //
- FlowEntryAction flowEntryAction = new FlowEntryAction();
- flowEntryAction.setActionOutput(newFlowEntry.outPort());
- flowEntryActions.addAction(flowEntryAction);
-
- //
- // Set the state of the new Flow Entry
- //
- newFlowEntry.setFlowEntryUserState(FlowEntryUserState.FE_USER_ADD);
- newFlowEntry.setFlowEntrySwitchState(FlowEntrySwitchState.FE_SWITCH_NOT_UPDATED);
- finalFlowEntries.add(newFlowEntry);
- }
-
- //
- // Replace the old Flow Entries with the new Flow Entries.
- // Note that the Flow Entries that will be deleted are added at
- // the end.
- //
- for (FlowEntry flowEntry : deletedFlowEntries)
- finalFlowEntries.add(flowEntry);
- flowPath.dataPath().setFlowEntries(finalFlowEntries);
-
- return hasChanged;
- }
-
- /**
- * Receive a notification that a Flow is added.
- *
- * @param flowPath the flow that is added.
- */
- @Override
- public void notificationRecvFlowAdded(FlowPath flowPath) {
- EventEntry<FlowPath> eventEntry =
- new EventEntry<FlowPath>(EventEntry.Type.ENTRY_ADD, flowPath);
- networkEvents.add(eventEntry);
- }
-
- /**
- * Receive a notification that a Flow is removed.
- *
- * @param flowPath the flow that is removed.
- */
- @Override
- public void notificationRecvFlowRemoved(FlowPath flowPath) {
- EventEntry<FlowPath> eventEntry =
- new EventEntry<FlowPath>(EventEntry.Type.ENTRY_REMOVE, flowPath);
- networkEvents.add(eventEntry);
- }
-
- /**
- * Receive a notification that a Flow is updated.
- *
- * @param flowPath the flow that is updated.
- */
- @Override
- public void notificationRecvFlowUpdated(FlowPath flowPath) {
- // NOTE: The ADD and UPDATE events are processed in same way
- EventEntry<FlowPath> eventEntry =
- new EventEntry<FlowPath>(EventEntry.Type.ENTRY_ADD, flowPath);
- networkEvents.add(eventEntry);
- }
-
- /**
- * Receive a notification that a Topology Element is added.
- *
- * @param topologyElement the Topology Element that is added.
- */
- @Override
- public void notificationRecvTopologyElementAdded(TopologyElement topologyElement) {
- EventEntry<TopologyElement> eventEntry =
- new EventEntry<TopologyElement>(EventEntry.Type.ENTRY_ADD, topologyElement);
- networkEvents.add(eventEntry);
- }
-
- /**
- * Receive a notification that a Topology Element is removed.
- *
- * @param topologyElement the Topology Element that is removed.
- */
- @Override
- public void notificationRecvTopologyElementRemoved(TopologyElement topologyElement) {
- EventEntry<TopologyElement> eventEntry =
- new EventEntry<TopologyElement>(EventEntry.Type.ENTRY_REMOVE, topologyElement);
- networkEvents.add(eventEntry);
- }
-
- /**
- * Receive a notification that a Topology Element is updated.
- *
- * @param topologyElement the Topology Element that is updated.
- */
- @Override
- public void notificationRecvTopologyElementUpdated(TopologyElement topologyElement) {
- // NOTE: The ADD and UPDATE events are processed in same way
- EventEntry<TopologyElement> eventEntry =
- new EventEntry<TopologyElement>(EventEntry.Type.ENTRY_ADD, topologyElement);
- networkEvents.add(eventEntry);
- }
-}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/forwarding/Forwarding.java b/src/main/java/net/onrc/onos/ofcontroller/forwarding/Forwarding.java
new file mode 100644
index 0000000..d6bac5c
--- /dev/null
+++ b/src/main/java/net/onrc/onos/ofcontroller/forwarding/Forwarding.java
@@ -0,0 +1,157 @@
+package net.onrc.onos.ofcontroller.forwarding;
+
+import java.util.Iterator;
+
+import net.floodlightcontroller.core.FloodlightContext;
+import net.floodlightcontroller.core.IFloodlightProviderService;
+import net.floodlightcontroller.core.IOFMessageListener;
+import net.floodlightcontroller.core.IOFSwitch;
+import net.floodlightcontroller.packet.Ethernet;
+import net.floodlightcontroller.util.MACAddress;
+import net.onrc.onos.ofcontroller.core.IDeviceStorage;
+import net.onrc.onos.ofcontroller.core.INetMapTopologyObjects.IDeviceObject;
+import net.onrc.onos.ofcontroller.core.INetMapTopologyObjects.IPortObject;
+import net.onrc.onos.ofcontroller.core.INetMapTopologyObjects.ISwitchObject;
+import net.onrc.onos.ofcontroller.core.internal.DeviceStorageImpl;
+import net.onrc.onos.ofcontroller.flowmanager.FlowManager;
+import net.onrc.onos.ofcontroller.flowmanager.IFlowService;
+import net.onrc.onos.ofcontroller.topology.TopologyManager;
+import net.onrc.onos.ofcontroller.util.CallerId;
+import net.onrc.onos.ofcontroller.util.DataPath;
+import net.onrc.onos.ofcontroller.util.Dpid;
+import net.onrc.onos.ofcontroller.util.FlowId;
+import net.onrc.onos.ofcontroller.util.FlowPath;
+import net.onrc.onos.ofcontroller.util.FlowPathType;
+import net.onrc.onos.ofcontroller.util.FlowPathUserState;
+import net.onrc.onos.ofcontroller.util.Port;
+import net.onrc.onos.ofcontroller.util.SwitchPort;
+
+import org.openflow.protocol.OFMessage;
+import org.openflow.protocol.OFPacketIn;
+import org.openflow.protocol.OFType;
+import org.openflow.util.HexString;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class Forwarding implements IOFMessageListener {
+ private final static Logger log = LoggerFactory.getLogger(Forwarding.class);
+
+ private IFloodlightProviderService floodlightProvider;
+ private IFlowService flowService;
+
+ private IDeviceStorage deviceStorage;
+ private TopologyManager topologyService;
+
+ public Forwarding() {
+
+ }
+
+ public void init(IFloodlightProviderService floodlightProvider,
+ IFlowService flowService) {
+ this.floodlightProvider = floodlightProvider;
+ this.flowService = flowService;
+
+ floodlightProvider.addOFMessageListener(OFType.PACKET_IN, this);
+
+ deviceStorage = new DeviceStorageImpl();
+ deviceStorage.init("");
+ topologyService = new TopologyManager();
+ topologyService.init("");
+ }
+
+ public void startUp() {
+ // no-op
+ }
+
+ @Override
+ public String getName() {
+ return "onosforwarding";
+ }
+
+ @Override
+ public boolean isCallbackOrderingPrereq(OFType type, String name) {
+ return (type == OFType.PACKET_IN) &&
+ (name.equals("devicemanager") || name.equals("proxyarpmanager"));
+ }
+
+ @Override
+ public boolean isCallbackOrderingPostreq(OFType type, String name) {
+ return false;
+ }
+
+ @Override
+ public Command receive(
+ IOFSwitch sw, OFMessage msg, FloodlightContext cntx) {
+
+ if (msg.getType() != OFType.PACKET_IN) {
+ return Command.CONTINUE;
+ }
+
+ OFPacketIn pi = (OFPacketIn) msg;
+
+ Ethernet eth = IFloodlightProviderService.bcStore.
+ get(cntx, IFloodlightProviderService.CONTEXT_PI_PAYLOAD);
+
+ // We don't want to handle broadcast traffic
+ if (eth.isBroadcast()) {
+ return Command.CONTINUE;
+ }
+
+ handlePacketIn(sw, pi, eth);
+
+ return Command.STOP;
+ }
+
+ private void handlePacketIn(IOFSwitch sw, OFPacketIn pi, Ethernet eth) {
+ String destinationMac = HexString.toHexString(eth.getDestinationMACAddress());
+
+ IDeviceObject deviceObject = deviceStorage.getDeviceByMac(
+ destinationMac);
+
+ if (deviceObject == null) {
+ log.debug("No device entry found for {}", destinationMac);
+ return;
+ }
+
+ Iterator<IPortObject> ports = deviceObject.getAttachedPorts().iterator();
+ if (!ports.hasNext()) {
+ log.debug("No attachment point found for device {}", destinationMac);
+ return;
+ }
+ IPortObject portObject = ports.next();
+ short destinationPort = portObject.getNumber();
+ ISwitchObject switchObject = portObject.getSwitch();
+ long destinationDpid = HexString.toLong(switchObject.getDPID());
+
+ SwitchPort srcSwitchPort = new SwitchPort(
+ new Dpid(sw.getId()), new Port(pi.getInPort()));
+ SwitchPort dstSwitchPort = new SwitchPort(
+ new Dpid(destinationDpid), new Port(destinationPort));
+ DataPath shortestPath =
+ topologyService.getDatabaseShortestPath(srcSwitchPort, dstSwitchPort);
+
+ if (shortestPath == null) {
+ log.debug("Shortest path not found between {} and {}",
+ srcSwitchPort, dstSwitchPort);
+ return;
+ }
+
+ MACAddress srcMacAddress = MACAddress.valueOf(eth.getSourceMACAddress());
+ MACAddress dstMacAddress = MACAddress.valueOf(eth.getDestinationMACAddress());
+
+ FlowId flowId = new FlowId(1L); //dummy flow ID
+ FlowPath flowPath = new FlowPath();
+ flowPath.setFlowId(flowId);
+ flowPath.setInstallerId(new CallerId("Forwarding"));
+ flowPath.setFlowPathType(FlowPathType.FP_TYPE_SHORTEST_PATH);
+ flowPath.setFlowPathUserState(FlowPathUserState.FP_USER_ADD);
+ flowPath.flowEntryMatch().enableSrcMac(srcMacAddress);
+ flowPath.flowEntryMatch().enableDstMac(dstMacAddress);
+ // For now just forward IPv4 packets. This prevents accidentally
+ // other stuff like ARP.
+ flowPath.flowEntryMatch().enableEthernetFrameType(Ethernet.TYPE_IPv4);
+ flowService.addFlow(flowPath, flowId);
+ //flowService.addAndMaintainShortestPathFlow(shortestPath.)
+ }
+
+}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/proxyarp/BgpProxyArpManager.java b/src/main/java/net/onrc/onos/ofcontroller/proxyarp/BgpProxyArpManager.java
new file mode 100644
index 0000000..801e414
--- /dev/null
+++ b/src/main/java/net/onrc/onos/ofcontroller/proxyarp/BgpProxyArpManager.java
@@ -0,0 +1,637 @@
+package net.onrc.onos.ofcontroller.proxyarp;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.Timer;
+import java.util.TimerTask;
+
+import net.floodlightcontroller.core.FloodlightContext;
+import net.floodlightcontroller.core.IFloodlightProviderService;
+import net.floodlightcontroller.core.IOFMessageListener;
+import net.floodlightcontroller.core.IOFSwitch;
+import net.floodlightcontroller.packet.ARP;
+import net.floodlightcontroller.packet.Ethernet;
+import net.floodlightcontroller.packet.IPv4;
+import net.floodlightcontroller.restserver.IRestApiService;
+import net.floodlightcontroller.topology.ITopologyService;
+import net.floodlightcontroller.util.MACAddress;
+import net.onrc.onos.ofcontroller.bgproute.Interface;
+import net.onrc.onos.ofcontroller.core.IDeviceStorage;
+import net.onrc.onos.ofcontroller.core.config.IConfigInfoService;
+import net.onrc.onos.ofcontroller.core.internal.DeviceStorageImpl;
+
+import org.openflow.protocol.OFMessage;
+import org.openflow.protocol.OFPacketIn;
+import org.openflow.protocol.OFPacketOut;
+import org.openflow.protocol.OFPort;
+import org.openflow.protocol.OFType;
+import org.openflow.protocol.action.OFAction;
+import org.openflow.protocol.action.OFActionOutput;
+import org.openflow.util.HexString;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.Multimaps;
+import com.google.common.collect.SetMultimap;
+
+public class BgpProxyArpManager implements IProxyArpService, IOFMessageListener {
+ private final static Logger log = LoggerFactory.getLogger(BgpProxyArpManager.class);
+
+ private final long ARP_TIMER_PERIOD = 60000; //ms (== 1 min)
+
+ private static final int ARP_REQUEST_TIMEOUT = 2000; //ms
+
+ private IFloodlightProviderService floodlightProvider;
+ private ITopologyService topology;
+ //private IDeviceService deviceService;
+ private IConfigInfoService configService;
+ private IRestApiService restApi;
+
+ private IDeviceStorage deviceStorage;
+
+ private short vlan;
+ private static final short NO_VLAN = 0;
+
+ private ArpCache arpCache;
+
+ private SetMultimap<InetAddress, ArpRequest> arpRequests;
+
+ private static class ArpRequest {
+ private final IArpRequester requester;
+ private final boolean retry;
+ private long requestTime;
+
+ public ArpRequest(IArpRequester requester, boolean retry){
+ this.requester = requester;
+ this.retry = retry;
+ this.requestTime = System.currentTimeMillis();
+ }
+
+ public ArpRequest(ArpRequest old) {
+ this.requester = old.requester;
+ this.retry = old.retry;
+ this.requestTime = System.currentTimeMillis();
+ }
+
+ public boolean isExpired() {
+ return (System.currentTimeMillis() - requestTime) > ARP_REQUEST_TIMEOUT;
+ }
+
+ public boolean shouldRetry() {
+ return retry;
+ }
+
+ public void dispatchReply(InetAddress ipAddress, MACAddress replyMacAddress) {
+ requester.arpResponse(ipAddress, replyMacAddress);
+ }
+ }
+
+ private class HostArpRequester implements IArpRequester {
+ private final ARP arpRequest;
+ private final long dpid;
+ private final short port;
+
+ public HostArpRequester(ARP arpRequest, long dpid, short port) {
+ this.arpRequest = arpRequest;
+ this.dpid = dpid;
+ this.port = port;
+ }
+
+ @Override
+ public void arpResponse(InetAddress ipAddress, MACAddress macAddress) {
+ BgpProxyArpManager.this.sendArpReply(arpRequest, dpid, port, macAddress);
+ }
+ }
+
+ /*
+ public ProxyArpManager(IFloodlightProviderService floodlightProvider,
+ ITopologyService topology, IConfigInfoService configService,
+ IRestApiService restApi){
+
+ }
+ */
+
+ public void init(IFloodlightProviderService floodlightProvider,
+ ITopologyService topology,
+ IConfigInfoService config, IRestApiService restApi){
+ this.floodlightProvider = floodlightProvider;
+ this.topology = topology;
+ //this.deviceService = deviceService;
+ this.configService = config;
+ this.restApi = restApi;
+
+ arpCache = new ArpCache();
+
+ arpRequests = Multimaps.synchronizedSetMultimap(
+ HashMultimap.<InetAddress, ArpRequest>create());
+ }
+
+ public void startUp() {
+ this.vlan = configService.getVlan();
+ log.info("vlan set to {}", this.vlan);
+
+ restApi.addRestletRoutable(new ArpWebRoutable());
+ floodlightProvider.addOFMessageListener(OFType.PACKET_IN, this);
+
+ deviceStorage = new DeviceStorageImpl();
+ deviceStorage.init("");
+
+ Timer arpTimer = new Timer("arp-processing");
+ arpTimer.scheduleAtFixedRate(new TimerTask() {
+ @Override
+ public void run() {
+ doPeriodicArpProcessing();
+ }
+ }, 0, ARP_TIMER_PERIOD);
+ }
+
+ /*
+ * Function that runs periodically to manage the asynchronous request mechanism.
+ * It basically cleans up old ARP requests if we don't get a response for them.
+ * The caller can designate that a request should be retried indefinitely, and
+ * this task will handle that as well.
+ */
+ private void doPeriodicArpProcessing() {
+ SetMultimap<InetAddress, ArpRequest> retryList
+ = HashMultimap.<InetAddress, ArpRequest>create();
+
+ //Have to synchronize externally on the Multimap while using an iterator,
+ //even though it's a synchronizedMultimap
+ synchronized (arpRequests) {
+ log.debug("Current have {} outstanding requests",
+ arpRequests.size());
+
+ Iterator<Map.Entry<InetAddress, ArpRequest>> it
+ = arpRequests.entries().iterator();
+
+ while (it.hasNext()) {
+ Map.Entry<InetAddress, ArpRequest> entry
+ = it.next();
+ ArpRequest request = entry.getValue();
+ if (request.isExpired()) {
+ log.debug("Cleaning expired ARP request for {}",
+ entry.getKey().getHostAddress());
+
+ it.remove();
+
+ if (request.shouldRetry()) {
+ retryList.put(entry.getKey(), request);
+ }
+ }
+ }
+ }
+
+ for (Map.Entry<InetAddress, Collection<ArpRequest>> entry
+ : retryList.asMap().entrySet()) {
+
+ InetAddress address = entry.getKey();
+
+ log.debug("Resending ARP request for {}", address.getHostAddress());
+
+ sendArpRequestForAddress(address);
+
+ for (ArpRequest request : entry.getValue()) {
+ arpRequests.put(address, new ArpRequest(request));
+ }
+ }
+ }
+
+ @Override
+ public String getName() {
+ return "proxyarpmanager";
+ }
+
+ @Override
+ public boolean isCallbackOrderingPrereq(OFType type, String name) {
+ if (type == OFType.PACKET_IN) {
+ return "devicemanager".equals(name);
+ }
+ else {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isCallbackOrderingPostreq(OFType type, String name) {
+ return false;
+ }
+
+ @Override
+ public Command receive(
+ IOFSwitch sw, OFMessage msg, FloodlightContext cntx) {
+
+ if (msg.getType() != OFType.PACKET_IN){
+ return Command.CONTINUE;
+ }
+
+ OFPacketIn pi = (OFPacketIn) msg;
+
+ Ethernet eth = IFloodlightProviderService.bcStore.get(cntx,
+ IFloodlightProviderService.CONTEXT_PI_PAYLOAD);
+
+ if (eth.getEtherType() == Ethernet.TYPE_ARP){
+ ARP arp = (ARP) eth.getPayload();
+
+ if (arp.getOpCode() == ARP.OP_REQUEST) {
+ //TODO check what the DeviceManager does about propagating
+ //or swallowing ARPs. We want to go after DeviceManager in the
+ //chain but we really need it to CONTINUE ARP packets so we can
+ //get them.
+ handleArpRequest(sw, pi, arp);
+ }
+ else if (arp.getOpCode() == ARP.OP_REPLY) {
+ handleArpReply(sw, pi, arp);
+ }
+ }
+
+ //TODO should we propagate ARP or swallow it?
+ //Always propagate for now so DeviceManager can learn the host location
+ return Command.CONTINUE;
+ }
+
+ private void handleArpRequest(IOFSwitch sw, OFPacketIn pi, ARP arp) {
+ if (log.isTraceEnabled()) {
+ log.trace("ARP request received for {}",
+ inetAddressToString(arp.getTargetProtocolAddress()));
+ }
+
+ InetAddress target;
+ try {
+ target = InetAddress.getByAddress(arp.getTargetProtocolAddress());
+ } catch (UnknownHostException e) {
+ log.debug("Invalid address in ARP request", e);
+ return;
+ }
+
+ if (configService.fromExternalNetwork(sw.getId(), pi.getInPort())) {
+ //If the request came from outside our network, we only care if
+ //it was a request for one of our interfaces.
+ if (configService.isInterfaceAddress(target)) {
+ log.trace("ARP request for our interface. Sending reply {} => {}",
+ target.getHostAddress(), configService.getRouterMacAddress());
+
+ sendArpReply(arp, sw.getId(), pi.getInPort(),
+ configService.getRouterMacAddress());
+ }
+
+ return;
+ }
+
+ MACAddress macAddress = arpCache.lookup(target);
+
+ //IDevice dstDevice = deviceService.fcStore.get(cntx, IDeviceService.CONTEXT_DST_DEVICE);
+ //Iterator<? extends IDevice> it = deviceService.queryDevices(
+ //null, null, InetAddresses.coerceToInteger(target), null, null);
+
+ //IDevice targetDevice = null;
+ //if (it.hasNext()) {
+ //targetDevice = it.next();
+ //}
+ /*IDeviceObject targetDevice =
+ deviceStorage.getDeviceByIP(InetAddresses.coerceToInteger(target));
+
+ if (targetDevice != null) {
+ //We have the device in our database, so send a reply
+ MACAddress macAddress = MACAddress.valueOf(targetDevice.getMACAddress());
+
+ if (log.isTraceEnabled()) {
+ log.trace("Sending reply: {} => {} to host at {}/{}", new Object [] {
+ inetAddressToString(arp.getTargetProtocolAddress()),
+ macAddress.toString(),
+ HexString.toHexString(sw.getId()), pi.getInPort()});
+ }
+
+ sendArpReply(arp, sw.getId(), pi.getInPort(), macAddress);
+ }*/
+
+ if (macAddress == null){
+ //MAC address is not in our ARP cache.
+
+ //Record where the request came from so we know where to send the reply
+ arpRequests.put(target, new ArpRequest(
+ new HostArpRequester(arp, sw.getId(), pi.getInPort()), false));
+
+ //Flood the request out edge ports
+ sendArpRequestToSwitches(target, pi.getPacketData(), sw.getId(), pi.getInPort());
+ }
+ else {
+ //We know the address, so send a reply
+ if (log.isTraceEnabled()) {
+ log.trace("Sending reply: {} => {} to host at {}/{}", new Object [] {
+ inetAddressToString(arp.getTargetProtocolAddress()),
+ macAddress.toString(),
+ HexString.toHexString(sw.getId()), pi.getInPort()});
+ }
+
+ sendArpReply(arp, sw.getId(), pi.getInPort(), macAddress);
+ }
+ }
+
+ private void handleArpReply(IOFSwitch sw, OFPacketIn pi, ARP arp){
+ if (log.isTraceEnabled()) {
+ log.trace("ARP reply recieved: {} => {}, on {}/{}", new Object[] {
+ inetAddressToString(arp.getSenderProtocolAddress()),
+ HexString.toHexString(arp.getSenderHardwareAddress()),
+ HexString.toHexString(sw.getId()), pi.getInPort()});
+ }
+
+ InetAddress senderIpAddress;
+ try {
+ senderIpAddress = InetAddress.getByAddress(arp.getSenderProtocolAddress());
+ } catch (UnknownHostException e) {
+ log.debug("Invalid address in ARP reply", e);
+ return;
+ }
+
+ MACAddress senderMacAddress = MACAddress.valueOf(arp.getSenderHardwareAddress());
+
+ arpCache.update(senderIpAddress, senderMacAddress);
+
+ //See if anyone's waiting for this ARP reply
+ Set<ArpRequest> requests = arpRequests.get(senderIpAddress);
+
+ //Synchronize on the Multimap while using an iterator for one of the sets
+ List<ArpRequest> requestsToSend = new ArrayList<ArpRequest>(requests.size());
+ synchronized (arpRequests) {
+ Iterator<ArpRequest> it = requests.iterator();
+ while (it.hasNext()) {
+ ArpRequest request = it.next();
+ it.remove();
+ requestsToSend.add(request);
+ }
+ }
+
+ //Don't hold an ARP lock while dispatching requests
+ for (ArpRequest request : requestsToSend) {
+ request.dispatchReply(senderIpAddress, senderMacAddress);
+ }
+ }
+
+ private void sendArpRequestForAddress(InetAddress ipAddress) {
+ //TODO what should the sender IP address and MAC address be if no
+ //IP addresses are configured? Will there ever be a need to send
+ //ARP requests from the controller in that case?
+ //All-zero MAC address doesn't seem to work - hosts don't respond to it
+
+ byte[] zeroIpv4 = {0x0, 0x0, 0x0, 0x0};
+ byte[] zeroMac = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
+ byte[] genericNonZeroMac = {0x0, 0x0, 0x0, 0x0, 0x0, 0x01};
+ byte[] broadcastMac = {(byte)0xff, (byte)0xff, (byte)0xff,
+ (byte)0xff, (byte)0xff, (byte)0xff};
+
+ ARP arpRequest = new ARP();
+
+ arpRequest.setHardwareType(ARP.HW_TYPE_ETHERNET)
+ .setProtocolType(ARP.PROTO_TYPE_IP)
+ .setHardwareAddressLength((byte)Ethernet.DATALAYER_ADDRESS_LENGTH)
+ .setProtocolAddressLength((byte)IPv4.ADDRESS_LENGTH)
+ .setOpCode(ARP.OP_REQUEST)
+ .setTargetHardwareAddress(zeroMac)
+ .setTargetProtocolAddress(ipAddress.getAddress());
+
+ MACAddress routerMacAddress = configService.getRouterMacAddress();
+ //TODO hack for now as it's unclear what the MAC address should be
+ byte[] senderMacAddress = genericNonZeroMac;
+ if (routerMacAddress != null) {
+ senderMacAddress = routerMacAddress.toBytes();
+ }
+ arpRequest.setSenderHardwareAddress(senderMacAddress);
+
+ byte[] senderIPAddress = zeroIpv4;
+ Interface intf = configService.getOutgoingInterface(ipAddress);
+ if (intf != null) {
+ senderIPAddress = intf.getIpAddress().getAddress();
+ }
+
+ arpRequest.setSenderProtocolAddress(senderIPAddress);
+
+ Ethernet eth = new Ethernet();
+ eth.setSourceMACAddress(senderMacAddress)
+ .setDestinationMACAddress(broadcastMac)
+ .setEtherType(Ethernet.TYPE_ARP)
+ .setPayload(arpRequest);
+
+ if (vlan != NO_VLAN) {
+ eth.setVlanID(vlan)
+ .setPriorityCode((byte)0);
+ }
+
+ sendArpRequestToSwitches(ipAddress, eth.serialize());
+ }
+
+ private void sendArpRequestToSwitches(InetAddress dstAddress, byte[] arpRequest) {
+ sendArpRequestToSwitches(dstAddress, arpRequest,
+ 0, OFPort.OFPP_NONE.getValue());
+ }
+
+ private void sendArpRequestToSwitches(InetAddress dstAddress, byte[] arpRequest,
+ long inSwitch, short inPort) {
+
+ if (configService.hasLayer3Configuration()) {
+ Interface intf = configService.getOutgoingInterface(dstAddress);
+ if (intf != null) {
+ sendArpRequestOutPort(arpRequest, intf.getDpid(), intf.getPort());
+ }
+ else {
+ //TODO here it should be broadcast out all non-interface edge ports.
+ //I think we can assume that if it's not a request for an external
+ //network, it's an ARP for a host in our own network. So we want to
+ //send it out all edge ports that don't have an interface configured
+ //to ensure it reaches all hosts in our network.
+ log.debug("No interface found to send ARP request for {}",
+ dstAddress.getHostAddress());
+ }
+ }
+ else {
+ broadcastArpRequestOutEdge(arpRequest, inSwitch, inPort);
+ }
+ }
+
+ private void broadcastArpRequestOutEdge(byte[] arpRequest, long inSwitch, short inPort) {
+ for (IOFSwitch sw : floodlightProvider.getSwitches().values()){
+ Collection<Short> enabledPorts = sw.getEnabledPortNumbers();
+ Set<Short> linkPorts = topology.getPortsWithLinks(sw.getId());
+
+ if (linkPorts == null){
+ //I think this means the switch doesn't have any links.
+ //continue;
+ linkPorts = new HashSet<Short>();
+ }
+
+
+ OFPacketOut po = new OFPacketOut();
+ po.setInPort(OFPort.OFPP_NONE)
+ .setBufferId(-1)
+ .setPacketData(arpRequest);
+
+ List<OFAction> actions = new ArrayList<OFAction>();
+
+ for (short portNum : enabledPorts){
+ if (linkPorts.contains(portNum) ||
+ (sw.getId() == inSwitch && portNum == inPort)){
+ //If this port isn't an edge port or is the ingress port
+ //for the ARP, don't broadcast out it
+ continue;
+ }
+
+ actions.add(new OFActionOutput(portNum));
+ }
+
+ po.setActions(actions);
+ short actionsLength = (short) (actions.size() * OFActionOutput.MINIMUM_LENGTH);
+ po.setActionsLength(actionsLength);
+ po.setLengthU(OFPacketOut.MINIMUM_LENGTH + actionsLength
+ + arpRequest.length);
+
+ List<OFMessage> msgList = new ArrayList<OFMessage>();
+ msgList.add(po);
+
+ try {
+ sw.write(msgList, null);
+ sw.flush();
+ } catch (IOException e) {
+ log.error("Failure writing packet out to switch", e);
+ }
+ }
+ }
+
+ private void sendArpRequestOutPort(byte[] arpRequest, long dpid, short port) {
+ if (log.isTraceEnabled()) {
+ log.trace("Sending ARP request out {}/{}",
+ HexString.toHexString(dpid), port);
+ }
+
+ OFPacketOut po = new OFPacketOut();
+ po.setInPort(OFPort.OFPP_NONE)
+ .setBufferId(-1)
+ .setPacketData(arpRequest);
+
+ List<OFAction> actions = new ArrayList<OFAction>();
+ actions.add(new OFActionOutput(port));
+ po.setActions(actions);
+ short actionsLength = (short) (actions.size() * OFActionOutput.MINIMUM_LENGTH);
+ po.setActionsLength(actionsLength);
+ po.setLengthU(OFPacketOut.MINIMUM_LENGTH + actionsLength
+ + arpRequest.length);
+
+ IOFSwitch sw = floodlightProvider.getSwitches().get(dpid);
+
+ if (sw == null) {
+ log.warn("Switch not found when sending ARP request");
+ return;
+ }
+
+ try {
+ sw.write(po, null);
+ sw.flush();
+ } catch (IOException e) {
+ log.error("Failure writing packet out to switch", e);
+ }
+ }
+
+ private void sendArpReply(ARP arpRequest, long dpid, short port, MACAddress targetMac) {
+ if (log.isTraceEnabled()) {
+ log.trace("Sending reply {} => {} to {}", new Object[] {
+ inetAddressToString(arpRequest.getTargetProtocolAddress()),
+ targetMac,
+ inetAddressToString(arpRequest.getSenderProtocolAddress())});
+ }
+
+ ARP arpReply = new ARP();
+ arpReply.setHardwareType(ARP.HW_TYPE_ETHERNET)
+ .setProtocolType(ARP.PROTO_TYPE_IP)
+ .setHardwareAddressLength((byte)Ethernet.DATALAYER_ADDRESS_LENGTH)
+ .setProtocolAddressLength((byte)IPv4.ADDRESS_LENGTH)
+ .setOpCode(ARP.OP_REPLY)
+ .setSenderHardwareAddress(targetMac.toBytes())
+ .setSenderProtocolAddress(arpRequest.getTargetProtocolAddress())
+ .setTargetHardwareAddress(arpRequest.getSenderHardwareAddress())
+ .setTargetProtocolAddress(arpRequest.getSenderProtocolAddress());
+
+
+
+ Ethernet eth = new Ethernet();
+ eth.setDestinationMACAddress(arpRequest.getSenderHardwareAddress())
+ .setSourceMACAddress(targetMac.toBytes())
+ .setEtherType(Ethernet.TYPE_ARP)
+ .setPayload(arpReply);
+
+ if (vlan != NO_VLAN) {
+ eth.setVlanID(vlan)
+ .setPriorityCode((byte)0);
+ }
+
+ List<OFAction> actions = new ArrayList<OFAction>();
+ actions.add(new OFActionOutput(port));
+
+ OFPacketOut po = new OFPacketOut();
+ po.setInPort(OFPort.OFPP_NONE)
+ .setBufferId(-1)
+ .setPacketData(eth.serialize())
+ .setActions(actions)
+ .setActionsLength((short)OFActionOutput.MINIMUM_LENGTH)
+ .setLengthU(OFPacketOut.MINIMUM_LENGTH + OFActionOutput.MINIMUM_LENGTH
+ + po.getPacketData().length);
+
+ List<OFMessage> msgList = new ArrayList<OFMessage>();
+ msgList.add(po);
+
+ IOFSwitch sw = floodlightProvider.getSwitches().get(dpid);
+
+ if (sw == null) {
+ log.warn("Switch {} not found when sending ARP reply",
+ HexString.toHexString(dpid));
+ return;
+ }
+
+ try {
+ sw.write(msgList, null);
+ sw.flush();
+ } catch (IOException e) {
+ log.error("Failure writing packet out to switch", e);
+ }
+ }
+
+ private String inetAddressToString(byte[] bytes) {
+ try {
+ return InetAddress.getByAddress(bytes).getHostAddress();
+ } catch (UnknownHostException e) {
+ log.debug("Invalid IP address", e);
+ return "";
+ }
+ }
+
+ /*
+ * IProxyArpService methods
+ */
+
+ @Override
+ public MACAddress getMacAddress(InetAddress ipAddress) {
+ return arpCache.lookup(ipAddress);
+ }
+
+ @Override
+ public void sendArpRequest(InetAddress ipAddress, IArpRequester requester,
+ boolean retry) {
+ arpRequests.put(ipAddress, new ArpRequest(requester, retry));
+
+ //Sanity check to make sure we don't send a request for our own address
+ if (!configService.isInterfaceAddress(ipAddress)) {
+ sendArpRequestForAddress(ipAddress);
+ }
+ }
+
+ @Override
+ public List<String> getMappings() {
+ return arpCache.getMappings();
+ }
+}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/proxyarp/IProxyArpService.java b/src/main/java/net/onrc/onos/ofcontroller/proxyarp/IProxyArpService.java
index 97844d3..71546a1 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/proxyarp/IProxyArpService.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/proxyarp/IProxyArpService.java
@@ -3,11 +3,11 @@
import java.net.InetAddress;
import java.util.List;
-import net.floodlightcontroller.core.module.IFloodlightService;
import net.floodlightcontroller.util.MACAddress;
+import net.onrc.onos.ofcontroller.core.module.IOnosService;
//Extends IFloodlightService so we can access it from REST API resources
-public interface IProxyArpService extends IFloodlightService{
+public interface IProxyArpService extends IOnosService{
/**
* Returns the MAC address if there is a valid entry in the cache.
* Otherwise returns null.
diff --git a/src/main/java/net/onrc/onos/ofcontroller/proxyarp/ProxyArpManager.java b/src/main/java/net/onrc/onos/ofcontroller/proxyarp/ProxyArpManager.java
index b6a9591..a5dabc9 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/proxyarp/ProxyArpManager.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/proxyarp/ProxyArpManager.java
@@ -5,6 +5,7 @@
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
@@ -16,14 +17,18 @@
import net.floodlightcontroller.core.IFloodlightProviderService;
import net.floodlightcontroller.core.IOFMessageListener;
import net.floodlightcontroller.core.IOFSwitch;
+import net.floodlightcontroller.devicemanager.IDeviceService;
import net.floodlightcontroller.packet.ARP;
import net.floodlightcontroller.packet.Ethernet;
import net.floodlightcontroller.packet.IPv4;
import net.floodlightcontroller.restserver.IRestApiService;
import net.floodlightcontroller.topology.ITopologyService;
import net.floodlightcontroller.util.MACAddress;
-import net.onrc.onos.ofcontroller.bgproute.ILayer3InfoService;
import net.onrc.onos.ofcontroller.bgproute.Interface;
+import net.onrc.onos.ofcontroller.core.IDeviceStorage;
+import net.onrc.onos.ofcontroller.core.INetMapTopologyObjects.IDeviceObject;
+import net.onrc.onos.ofcontroller.core.config.IConfigInfoService;
+import net.onrc.onos.ofcontroller.core.internal.DeviceStorageImpl;
import org.openflow.protocol.OFMessage;
import org.openflow.protocol.OFPacketIn;
@@ -39,6 +44,7 @@
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimaps;
import com.google.common.collect.SetMultimap;
+import com.google.common.net.InetAddresses;
public class ProxyArpManager implements IProxyArpService, IOFMessageListener {
private final static Logger log = LoggerFactory.getLogger(ProxyArpManager.class);
@@ -47,14 +53,20 @@
private static final int ARP_REQUEST_TIMEOUT = 2000; //ms
- private final IFloodlightProviderService floodlightProvider;
- private final ITopologyService topology;
- private final ILayer3InfoService layer3;
- private final IRestApiService restApi;
+ private IFloodlightProviderService floodlightProvider;
+ private ITopologyService topology;
+ private IDeviceService deviceService;
+ private IConfigInfoService configService;
+ private IRestApiService restApi;
- private final ArpCache arpCache;
+ private IDeviceStorage deviceStorage;
+
+ private short vlan;
+ private static final short NO_VLAN = 0;
+
+ private ArpCache arpCache;
- private final SetMultimap<InetAddress, ArpRequest> arpRequests;
+ private SetMultimap<InetAddress, ArpRequest> arpRequests;
private static class ArpRequest {
private final IArpRequester requester;
@@ -103,12 +115,21 @@
}
}
+ /*
public ProxyArpManager(IFloodlightProviderService floodlightProvider,
- ITopologyService topology, ILayer3InfoService layer3,
+ ITopologyService topology, IConfigInfoService configService,
IRestApiService restApi){
+
+ }
+ */
+
+ public void init(IFloodlightProviderService floodlightProvider,
+ ITopologyService topology, IDeviceService deviceService,
+ IConfigInfoService config, IRestApiService restApi){
this.floodlightProvider = floodlightProvider;
this.topology = topology;
- this.layer3 = layer3;
+ this.deviceService = deviceService;
+ this.configService = config;
this.restApi = restApi;
arpCache = new ArpCache();
@@ -118,7 +139,14 @@
}
public void startUp() {
+ this.vlan = configService.getVlan();
+ log.info("vlan set to {}", this.vlan);
+
restApi.addRestletRoutable(new ArpWebRoutable());
+ floodlightProvider.addOFMessageListener(OFType.PACKET_IN, this);
+
+ deviceStorage = new DeviceStorageImpl();
+ deviceStorage.init("");
Timer arpTimer = new Timer("arp-processing");
arpTimer.scheduleAtFixedRate(new TimerTask() {
@@ -182,12 +210,17 @@
@Override
public String getName() {
- return "ProxyArpManager";
+ return "proxyarpmanager";
}
@Override
public boolean isCallbackOrderingPrereq(OFType type, String name) {
- return false;
+ if (type == OFType.PACKET_IN) {
+ return "devicemanager".equals(name);
+ }
+ else {
+ return false;
+ }
}
@Override
@@ -212,10 +245,14 @@
ARP arp = (ARP) eth.getPayload();
if (arp.getOpCode() == ARP.OP_REQUEST) {
+ //TODO check what the DeviceManager does about propagating
+ //or swallowing ARPs. We want to go after DeviceManager in the
+ //chain but we really need it to CONTINUE ARP packets so we can
+ //get them.
handleArpRequest(sw, pi, arp);
}
else if (arp.getOpCode() == ARP.OP_REPLY) {
- handleArpReply(sw, pi, arp);
+ //handleArpReply(sw, pi, arp);
}
}
@@ -238,31 +275,56 @@
return;
}
- if (layer3.fromExternalNetwork(sw.getId(), pi.getInPort())) {
+ if (configService.fromExternalNetwork(sw.getId(), pi.getInPort())) {
//If the request came from outside our network, we only care if
//it was a request for one of our interfaces.
- if (layer3.isInterfaceAddress(target)) {
+ if (configService.isInterfaceAddress(target)) {
log.trace("ARP request for our interface. Sending reply {} => {}",
- target.getHostAddress(), layer3.getRouterMacAddress());
+ target.getHostAddress(), configService.getRouterMacAddress());
sendArpReply(arp, sw.getId(), pi.getInPort(),
- layer3.getRouterMacAddress());
+ configService.getRouterMacAddress());
}
return;
}
- MACAddress macAddress = arpCache.lookup(target);
+ //MACAddress macAddress = arpCache.lookup(target);
- if (macAddress == null){
+ //IDevice dstDevice = deviceService.fcStore.get(cntx, IDeviceService.CONTEXT_DST_DEVICE);
+ //Iterator<? extends IDevice> it = deviceService.queryDevices(
+ //null, null, InetAddresses.coerceToInteger(target), null, null);
+
+ //IDevice targetDevice = null;
+ //if (it.hasNext()) {
+ //targetDevice = it.next();
+ //}
+ IDeviceObject targetDevice =
+ deviceStorage.getDeviceByIP(InetAddresses.coerceToInteger(target));
+
+ if (targetDevice != null) {
+ //We have the device in our database, so send a reply
+ MACAddress macAddress = MACAddress.valueOf(targetDevice.getMACAddress());
+
+ if (log.isTraceEnabled()) {
+ log.trace("Sending reply: {} => {} to host at {}/{}", new Object [] {
+ inetAddressToString(arp.getTargetProtocolAddress()),
+ macAddress.toString(),
+ HexString.toHexString(sw.getId()), pi.getInPort()});
+ }
+
+ sendArpReply(arp, sw.getId(), pi.getInPort(), macAddress);
+ }
+
+ /*if (macAddress == null){
//MAC address is not in our ARP cache.
//Record where the request came from so we know where to send the reply
- arpRequests.put(target, new ArpRequest(
- new HostArpRequester(arp, sw.getId(), pi.getInPort()), false));
+ //arpRequests.put(target, new ArpRequest(
+ //new HostArpRequester(arp, sw.getId(), pi.getInPort()), false));
//Flood the request out edge ports
- sendArpRequestToSwitches(target, pi.getPacketData(), sw.getId(), pi.getInPort());
+ //sendArpRequestToSwitches(target, pi.getPacketData(), sw.getId(), pi.getInPort());
}
else {
//We know the address, so send a reply
@@ -274,7 +336,7 @@
}
sendArpReply(arp, sw.getId(), pi.getInPort(), macAddress);
- }
+ }*/
}
private void handleArpReply(IOFSwitch sw, OFPacketIn pi, ARP arp){
@@ -339,7 +401,7 @@
.setTargetHardwareAddress(zeroMac)
.setTargetProtocolAddress(ipAddress.getAddress());
- MACAddress routerMacAddress = layer3.getRouterMacAddress();
+ MACAddress routerMacAddress = configService.getRouterMacAddress();
//TODO hack for now as it's unclear what the MAC address should be
byte[] senderMacAddress = genericNonZeroMac;
if (routerMacAddress != null) {
@@ -348,7 +410,7 @@
arpRequest.setSenderHardwareAddress(senderMacAddress);
byte[] senderIPAddress = zeroIpv4;
- Interface intf = layer3.getOutgoingInterface(ipAddress);
+ Interface intf = configService.getOutgoingInterface(ipAddress);
if (intf != null) {
senderIPAddress = intf.getIpAddress().getAddress();
}
@@ -361,6 +423,11 @@
.setEtherType(Ethernet.TYPE_ARP)
.setPayload(arpRequest);
+ if (vlan != NO_VLAN) {
+ eth.setVlanID(vlan)
+ .setPriorityCode((byte)0);
+ }
+
sendArpRequestToSwitches(ipAddress, eth.serialize());
}
@@ -372,8 +439,8 @@
private void sendArpRequestToSwitches(InetAddress dstAddress, byte[] arpRequest,
long inSwitch, short inPort) {
- if (layer3.hasLayer3Configuration()) {
- Interface intf = layer3.getOutgoingInterface(dstAddress);
+ if (configService.hasLayer3Configuration()) {
+ Interface intf = configService.getOutgoingInterface(dstAddress);
if (intf != null) {
sendArpRequestOutPort(arpRequest, intf.getDpid(), intf.getPort());
}
@@ -398,11 +465,12 @@
Set<Short> linkPorts = topology.getPortsWithLinks(sw.getId());
if (linkPorts == null){
- //I think this means the switch isn't known to topology yet.
- //Maybe it only just joined.
- continue;
+ //I think this means the switch doesn't have any links.
+ //continue;
+ linkPorts = new HashSet<Short>();
}
+
OFPacketOut po = new OFPacketOut();
po.setInPort(OFPort.OFPP_NONE)
.setBufferId(-1)
@@ -492,12 +560,19 @@
.setTargetHardwareAddress(arpRequest.getSenderHardwareAddress())
.setTargetProtocolAddress(arpRequest.getSenderProtocolAddress());
+
+
Ethernet eth = new Ethernet();
eth.setDestinationMACAddress(arpRequest.getSenderHardwareAddress())
.setSourceMACAddress(targetMac.toBytes())
.setEtherType(Ethernet.TYPE_ARP)
.setPayload(arpReply);
+ if (vlan != NO_VLAN) {
+ eth.setVlanID(vlan)
+ .setPriorityCode((byte)0);
+ }
+
List<OFAction> actions = new ArrayList<OFAction>();
actions.add(new OFActionOutput(port));
@@ -553,7 +628,7 @@
arpRequests.put(ipAddress, new ArpRequest(requester, retry));
//Sanity check to make sure we don't send a request for our own address
- if (!layer3.isInterfaceAddress(ipAddress)) {
+ if (!configService.isInterfaceAddress(ipAddress)) {
sendArpRequestForAddress(ipAddress);
}
}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/topology/ShortestPath.java b/src/main/java/net/onrc/onos/ofcontroller/topology/ShortestPath.java
index dabe916..f187c27 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/topology/ShortestPath.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/topology/ShortestPath.java
@@ -24,7 +24,8 @@
import com.tinkerpop.blueprints.Vertex;
/**
- * A class for implementing the Shortest Path in a topology.
+ * Class to calculate a shortest DataPath between 2 SwitchPorts
+ * based on hops in Network Topology.
*/
public class ShortestPath {
/**
diff --git a/src/main/java/net/onrc/onos/ofcontroller/topology/Topology.java b/src/main/java/net/onrc/onos/ofcontroller/topology/Topology.java
index 612b72a..dbf9ada 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/topology/Topology.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/topology/Topology.java
@@ -1,7 +1,9 @@
package net.onrc.onos.ofcontroller.topology;
-import java.util.HashMap;
+import java.util.List;
+import java.util.LinkedList;
import java.util.Map;
+import java.util.TreeMap;
import net.onrc.onos.graph.GraphDBOperation;
import net.onrc.onos.ofcontroller.core.INetMapTopologyObjects.ISwitchObject;
@@ -44,9 +46,14 @@
};
public long nodeId; // The node ID
- public HashMap<Integer, Link> links; // The links from this node
- private HashMap<Integer, Link> reverseLinksMap; // The links to this node
- private HashMap<Integer, Integer> portsMap; // The ports for this node
+ public TreeMap<Integer, Link> links; // The links from this node:
+ // (src PortID -> Link)
+ private TreeMap<Integer, Link> reverseLinksMap; // The links to this node:
+ // (dst PortID -> Link)
+ private TreeMap<Integer, Integer> portsMap; // The ports on this node:
+ // (PortID -> PortID)
+ // TODO: In the future will be:
+ // (PortID -> Port)
/**
* Node constructor.
@@ -55,9 +62,9 @@
*/
public Node(long nodeId) {
this.nodeId = nodeId;
- links = new HashMap<Integer, Link>();
- reverseLinksMap = new HashMap<Integer, Link>();
- portsMap = new HashMap<Integer, Integer>();
+ links = new TreeMap<Integer, Link>();
+ reverseLinksMap = new TreeMap<Integer, Link>();
+ portsMap = new TreeMap<Integer, Integer>();
}
/**
@@ -78,7 +85,7 @@
* @return the port if found, otherwise null.
*/
public Integer getPort(int portId) {
- return portsMap.get(nodeId);
+ return portsMap.get(portId);
}
/**
@@ -114,13 +121,13 @@
Link reverseLink = reverseLinksMap.get(portId);
if (reverseLink != null) {
// NOTE: reverseLink.myPort is the neighbor's outgoing port
- reverseLink.neighbor.removeLink(reverseLink.myPort);
+ reverseLink.me.removeLink(reverseLink.myPort);
removeReverseLink(reverseLink);
}
portsMap.remove(portId);
}
-
+
/**
* Get a link on a port to a neighbor.
*
@@ -186,7 +193,7 @@
* Default constructor.
*/
public Topology() {
- nodesMap = new HashMap<Long, Node>();
+ nodesMap = new TreeMap<Long, Node>();
}
/**
@@ -206,14 +213,6 @@
node = addNode(topologyElement.getSwitch());
isModified = true;
}
- // Add the ports for the switch
- for (Integer portId : topologyElement.getSwitchPorts().values()) {
- Integer port = node.getPort(portId);
- if (port == null) {
- node.addPort(portId);
- isModified = true;
- }
- }
break;
}
case ELEMENT_PORT: {
@@ -353,7 +352,20 @@
// Remove all ports one-by-one. This operation will also remove the
// incoming links originating from the neighbors.
//
- for (Integer portId : node.ports().keySet())
+ // NOTE: We have to extract all Port IDs in advance, otherwise we
+ // cannot loop over the Ports collection and remove entries at the
+ // same time.
+ // TODO: If there is a large number of ports, the implementation
+ // below can be sub-optimal. It should be refactored as follows:
+ // 1. Modify removePort() to perform all the cleanup, except
+ // removing the Port entry from the portsMap
+ // 2. Call portsMap.clear() at the end of this method
+ // 3. In all other methods: if removePort() is called somewhere else,
+ // add an explicit removal of the Port entry from the portsMap.
+ //
+ List<Integer> allPortIdKeys = new LinkedList<Integer>();
+ allPortIdKeys.addAll(node.ports().keySet());
+ for (Integer portId : allPortIdKeys)
node.removePort(portId);
nodesMap.remove(node.nodeId);
diff --git a/src/main/java/net/onrc/onos/ofcontroller/topology/TopologyElement.java b/src/main/java/net/onrc/onos/ofcontroller/topology/TopologyElement.java
index fe84654..b01c7d3 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/topology/TopologyElement.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/topology/TopologyElement.java
@@ -24,9 +24,6 @@
private long toSwitchDpid = 0; // The Neighbor Switch DPID
private int toSwitchPort = 0; // The Neighbor Switch Port
- // All (known) ports for a Switch
- private Map<Integer, Integer> switchPorts = new TreeMap<Integer, Integer>();
-
/**
* Default constructor.
*/
@@ -95,28 +92,6 @@
}
/**
- * Get the Switch Ports.
- *
- * NOTE: Applies for Type.ELEMENT_SWITCH
- *
- * @return the collection of Switch Ports.
- */
- public Map<Integer, Integer> getSwitchPorts() {
- return switchPorts;
- }
-
- /**
- * Add a Switch Port.
- *
- * NOTE: Applies for Type.ELEMENT_SWITCH
- *
- * @param switchPort the Switch Port to add.
- */
- public void addSwitchPort(int switchPort) {
- switchPorts.put(switchPort, switchPort);
- }
-
- /**
* Get the Switch Port.
*
* NOTE: Applies for Type.ELEMENT_PORT
@@ -195,4 +170,15 @@
assert(false);
return null;
}
+
+ /**
+ * Convert the Topology Element to a string.
+ *
+ * @return the Topology Element as a string.
+ */
+ @Override
+ public String toString() {
+ // For now, we just return the Element ID.
+ return elementId();
+ }
}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/topology/TopologyManager.java b/src/main/java/net/onrc/onos/ofcontroller/topology/TopologyManager.java
index ffe806a..c0e04f2 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/topology/TopologyManager.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/topology/TopologyManager.java
@@ -24,7 +24,10 @@
import org.slf4j.LoggerFactory;
/**
- * A class for implementing Topology Network Service.
+ * A class for obtaining Topology Snapshot
+ * and PathComputation.
+ *
+ * TODO: PathComputation part should be refactored out to separate class.
*/
public class TopologyManager implements IFloodlightModule,
ITopologyNetService {
diff --git a/src/main/java/net/onrc/onos/ofcontroller/topology/web/RouteResource.java b/src/main/java/net/onrc/onos/ofcontroller/topology/web/RouteResource.java
index b340996..0d33b27 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/topology/web/RouteResource.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/topology/web/RouteResource.java
@@ -1,5 +1,6 @@
package net.onrc.onos.ofcontroller.topology.web;
+import net.onrc.onos.ofcontroller.flowmanager.IFlowService;
import net.onrc.onos.ofcontroller.topology.ITopologyNetService;
import net.onrc.onos.ofcontroller.topology.TopologyManager;
import net.onrc.onos.ofcontroller.util.DataPath;
@@ -18,11 +19,22 @@
@Get("json")
public DataPath retrieve() {
- ITopologyNetService topologyNetService = new TopologyManager("");
+ // Get the services that are needed for the computation
+ ITopologyNetService topologyNetService =
+ (ITopologyNetService)getContext().getAttributes().
+ get(ITopologyNetService.class.getCanonicalName());
+ IFlowService flowService =
+ (IFlowService)getContext().getAttributes().
+ get(IFlowService.class.getCanonicalName());
+
if (topologyNetService == null) {
log.debug("Topology Net Service not found");
return null;
}
+ if (flowService == null) {
+ log.debug("Flow Service not found");
+ return null;
+ }
String srcDpidStr = (String) getRequestAttributes().get("src-dpid");
String srcPortStr = (String) getRequestAttributes().get("src-port");
@@ -37,7 +49,8 @@
Port dstPort = new Port(Short.parseShort(dstPortStr));
DataPath result =
- topologyNetService.getDatabaseShortestPath(
+ topologyNetService.getTopologyShortestPath(
+ flowService.getTopology(),
new SwitchPort(srcDpid, srcPort),
new SwitchPort(dstDpid, dstPort));
if (result != null) {
diff --git a/src/main/java/net/onrc/onos/ofcontroller/util/DataPath.java b/src/main/java/net/onrc/onos/ofcontroller/util/DataPath.java
index 7c6597d..044cc6d 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/util/DataPath.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/util/DataPath.java
@@ -101,6 +101,41 @@
}
/**
+ * Remove Flow Entries that were deleted.
+ */
+ public void removeDeletedFlowEntries() {
+ //
+ // NOTE: We create a new ArrayList, and add only the Flow Entries
+ // that are NOT FE_USER_DELETE.
+ // This is sub-optimal: if it adds notable processing cost,
+ // the Flow Entries container should be changed to LinkedList
+ // or some other container that has O(1) cost of removing an entry.
+ //
+
+ // Test first whether any Flow Entry was deleted
+ boolean foundDeletedFlowEntry = false;
+ for (FlowEntry flowEntry : this.flowEntries) {
+ if (flowEntry.flowEntryUserState() ==
+ FlowEntryUserState.FE_USER_DELETE) {
+ foundDeletedFlowEntry = true;
+ break;
+ }
+ }
+ if (! foundDeletedFlowEntry)
+ return; // Nothing to do
+
+ // Create a new collection and exclude the deleted flow entries
+ ArrayList<FlowEntry> newFlowEntries = new ArrayList<FlowEntry>();
+ for (FlowEntry flowEntry : this.flowEntries()) {
+ if (flowEntry.flowEntryUserState() !=
+ FlowEntryUserState.FE_USER_DELETE) {
+ newFlowEntries.add(flowEntry);
+ }
+ }
+ setFlowEntries(newFlowEntries);
+ }
+
+ /**
* Get a string with the summary of the shortest-path data path
* computation.
*
diff --git a/src/main/java/net/onrc/onos/ofcontroller/util/FlowEntry.java b/src/main/java/net/onrc/onos/ofcontroller/util/FlowEntry.java
index 762d272..15a6233 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/util/FlowEntry.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/util/FlowEntry.java
@@ -116,10 +116,11 @@
/**
* Get the Flow ID.
+ *
* @return the Flow ID.
*/
@JsonIgnore
- public FlowId getFlowId() { return flowId; }
+ public FlowId flowId() { return flowId; }
/**
* Set the Flow ID.
@@ -131,6 +132,17 @@
}
/**
+ * Test whether the Flow ID is valid.
+ *
+ * @return true if the Flow ID is valid, otherwise false.
+ */
+ public boolean isValidFlowId() {
+ if (this.flowId == null)
+ return false;
+ return (this.flowId.value() != 0);
+ }
+
+ /**
* Get the Flow Entry ID.
*
* @return the Flow Entry ID.
@@ -149,6 +161,17 @@
}
/**
+ * Test whether the Flow Entry ID is valid.
+ *
+ * @return true if the Flow Entry ID is valid, otherwise false.
+ */
+ public boolean isValidFlowEntryId() {
+ if (this.flowEntryId == null)
+ return false;
+ return (this.flowEntryId.value() != 0);
+ }
+
+ /**
* Get the Flow Entry Match.
*
* @return the Flow Entry Match.
@@ -331,6 +354,9 @@
} else {
ret.append("[");
}
+ if ( flowId != null ) {
+ ret.append(" flowId=" + this.flowId.toString());
+ }
if ( flowEntryMatch != null ) {
ret.append(" flowEntryMatch=" + this.flowEntryMatch.toString());
}