Merge pull request #445 from jonohart/master
Various improvements and bug fixes
diff --git a/src/main/java/net/floodlightcontroller/util/MACAddress.java b/src/main/java/net/floodlightcontroller/util/MACAddress.java
index 88dbda2..b77d4cc 100644
--- a/src/main/java/net/floodlightcontroller/util/MACAddress.java
+++ b/src/main/java/net/floodlightcontroller/util/MACAddress.java
@@ -19,6 +19,18 @@
public static final int MAC_ADDRESS_LENGTH = 6;
private byte[] address = new byte[MAC_ADDRESS_LENGTH];
+ /**
+ * Default constructor.
+ */
+ public MACAddress() {
+ this.address = new byte[] { 0, 0, 0, 0, 0, 0};
+ }
+
+ /**
+ * Constructor for a given address stored in a byte array.
+ *
+ * @param address the address stored in a byte array.
+ */
public MACAddress(byte[] address) {
this.address = Arrays.copyOf(address, MAC_ADDRESS_LENGTH);
}
diff --git a/src/main/java/net/onrc/onos/datagrid/HazelcastDatagrid.java b/src/main/java/net/onrc/onos/datagrid/HazelcastDatagrid.java
index 7de2869..41b4957 100644
--- a/src/main/java/net/onrc/onos/datagrid/HazelcastDatagrid.java
+++ b/src/main/java/net/onrc/onos/datagrid/HazelcastDatagrid.java
@@ -4,7 +4,13 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
+import java.util.LinkedList;
import java.util.Map;
+import java.util.Set;
+
+import com.esotericsoftware.kryo2.Kryo;
+import com.esotericsoftware.kryo2.io.Input;
+import com.esotericsoftware.kryo2.io.Output;
import net.floodlightcontroller.core.IFloodlightProviderService;
import net.floodlightcontroller.core.module.FloodlightModuleContext;
@@ -12,13 +18,24 @@
import net.floodlightcontroller.core.module.IFloodlightModule;
import net.floodlightcontroller.core.module.IFloodlightService;
+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;
+
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.hazelcast.config.Config;
import com.hazelcast.config.FileSystemXmlConfig;
+import com.hazelcast.core.EntryEvent;
+import com.hazelcast.core.EntryListener;
import com.hazelcast.core.Hazelcast;
import com.hazelcast.core.HazelcastInstance;
+import com.hazelcast.core.IMap;
import com.hazelcast.instance.GroupProperties;
/**
@@ -27,13 +44,264 @@
* appropriate in a multi-node cluster.
*/
public class HazelcastDatagrid implements IFloodlightModule, IDatagridService {
+ private final static int MAX_BUFFER_SIZE = 64*1024;
+
protected final static Logger log = LoggerFactory.getLogger(HazelcastDatagrid.class);
protected IFloodlightProviderService floodlightProvider;
protected static final String HazelcastConfigFile = "datagridConfig";
- private HazelcastInstance hazelcast = null;
+ 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 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;
+ private MapTopologyListener mapTopologyListener = null;
+ private String mapTopologyListenerId = null;
+
+ /**
+ * Class for receiving notifications for Flow state.
+ *
+ * The datagrid map is:
+ * - Key : Flow ID (Long)
+ * - Value : Serialized Flow (byte[])
+ */
+ class MapFlowListener 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) {
+ 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);
+ FlowPath flowPath = kryo.readObject(input, FlowPath.class);
+ kryoFactory.deleteKryo(kryo);
+ flowEventHandlerService.notificationRecvFlowAdded(flowPath);
+ }
+
+ /**
+ * Receive a notification that an entry is removed.
+ *
+ * @param event the notification event for the entry.
+ */
+ public void entryRemoved(EntryEvent event) {
+ 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);
+ FlowPath flowPath = kryo.readObject(input, FlowPath.class);
+ kryoFactory.deleteKryo(kryo);
+ flowEventHandlerService.notificationRecvFlowRemoved(flowPath);
+ }
+
+ /**
+ * Receive a notification that an entry is updated.
+ *
+ * @param event the notification event for the entry.
+ */
+ public void entryUpdated(EntryEvent event) {
+ 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);
+ FlowPath flowPath = kryo.readObject(input, FlowPath.class);
+ kryoFactory.deleteKryo(kryo);
+ 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) {
+ 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) {
+ 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) {
+ 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);
+ }
+
+ /**
+ * 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 Network Topology state.
+ *
+ * The datagrid map is:
+ * - Key: TopologyElement ID (String)
+ * - Value: Serialized TopologyElement (byte[])
+ */
+ class MapTopologyListener implements EntryListener<String, byte[]> {
+ /**
+ * Receive a notification that an entry is added.
+ *
+ * @param event the notification event for the entry.
+ */
+ public void entryAdded(EntryEvent event) {
+ String keyString = (String)event.getKey();
+ byte[] valueBytes = (byte[])event.getValue();
+
+ //
+ // Decode the value and deliver the notification
+ //
+ Kryo kryo = kryoFactory.newKryo();
+ Input input = new Input(valueBytes);
+ TopologyElement topologyElement =
+ kryo.readObject(input, TopologyElement.class);
+ kryoFactory.deleteKryo(kryo);
+ flowEventHandlerService.notificationRecvTopologyElementAdded(topologyElement);
+ }
+
+ /**
+ * Receive a notification that an entry is removed.
+ *
+ * @param event the notification event for the entry.
+ */
+ public void entryRemoved(EntryEvent event) {
+ String keyString = (String)event.getKey();
+ byte[] valueBytes = (byte[])event.getValue();
+
+ //
+ // Decode the value and deliver the notification
+ //
+ Kryo kryo = kryoFactory.newKryo();
+ Input input = new Input(valueBytes);
+ TopologyElement topologyElement =
+ kryo.readObject(input, TopologyElement.class);
+ kryoFactory.deleteKryo(kryo);
+ flowEventHandlerService.notificationRecvTopologyElementRemoved(topologyElement);
+ }
+
+ /**
+ * Receive a notification that an entry is updated.
+ *
+ * @param event the notification event for the entry.
+ */
+ public void entryUpdated(EntryEvent event) {
+ String keyString = (String)event.getKey();
+ byte[] valueBytes = (byte[])event.getValue();
+
+ //
+ // Decode the value and deliver the notification
+ //
+ Kryo kryo = kryoFactory.newKryo();
+ Input input = new Input(valueBytes);
+ TopologyElement topologyElement =
+ kryo.readObject(input, TopologyElement.class);
+ kryoFactory.deleteKryo(kryo);
+ flowEventHandlerService.notificationRecvTopologyElementUpdated(topologyElement);
+ }
+
+ /**
+ * 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
+ }
+ }
+
/**
* Initialize the Hazelcast Datagrid operation.
*
@@ -143,6 +411,350 @@
*/
@Override
public void startUp(FloodlightModuleContext context) {
- hazelcast = Hazelcast.newHazelcastInstance(hazelcastConfig);
+ hazelcastInstance = Hazelcast.newHazelcastInstance(hazelcastConfig);
+ }
+
+ /**
+ * Register Flow Event Handler Service for receiving Flow-related
+ * notifications.
+ *
+ * NOTE: Only a single Flow Event Handler Service can be registered.
+ *
+ * @param flowEventHandlerService the Flow Event Handler Service to register.
+ */
+ @Override
+ 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);
+ mapTopologyListenerId = mapTopology.addEntryListener(mapTopologyListener, true);
+ }
+
+ /**
+ * De-register Flow Event Handler Service for receiving Flow-related
+ * notifications.
+ *
+ * NOTE: Only a single Flow Event Handler Service can be registered.
+ *
+ * @param flowEventHandlerService the Flow Event Handler Service to
+ * de-register.
+ */
+ @Override
+ 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.flowEventHandlerService = null;
+ }
+
+ /**
+ * Get all Flows that are currently in the datagrid.
+ *
+ * @return all Flows that are currently in the datagrid.
+ */
+ @Override
+ public Collection<FlowPath> getAllFlows() {
+ Collection<FlowPath> allFlows = new LinkedList<FlowPath>();
+
+ //
+ // Get all current entries
+ //
+ Collection<byte[]> values = mapFlow.values();
+ Kryo kryo = kryoFactory.newKryo();
+ for (byte[] valueBytes : values) {
+ //
+ // Decode the value
+ //
+ Input input = new Input(valueBytes);
+ FlowPath flowPath = kryo.readObject(input, FlowPath.class);
+ allFlows.add(flowPath);
+ }
+ kryoFactory.deleteKryo(kryo);
+
+ return allFlows;
+ }
+
+ /**
+ * Send a notification that a Flow is added.
+ *
+ * @param flowPath the Flow that is added.
+ */
+ @Override
+ public void notificationSendFlowAdded(FlowPath flowPath) {
+ //
+ // Encode the value
+ //
+ byte[] buffer = new byte[MAX_BUFFER_SIZE];
+ Kryo kryo = kryoFactory.newKryo();
+ Output output = new Output(buffer, -1);
+ kryo.writeObject(output, flowPath);
+ byte[] valueBytes = output.toBytes();
+ kryoFactory.deleteKryo(kryo);
+
+ //
+ // Put the entry:
+ // - Key : Flow ID (Long)
+ // - Value : Serialized Flow (byte[])
+ //
+ mapFlow.putAsync(flowPath.flowId().value(), valueBytes);
+ }
+
+ /**
+ * Send a notification that a Flow is removed.
+ *
+ * @param flowId the Flow ID of the Flow that is removed.
+ */
+ @Override
+ public void notificationSendFlowRemoved(FlowId flowId) {
+ //
+ // Remove the entry:
+ // - Key : Flow ID (Long)
+ // - Value : Serialized Flow (byte[])
+ //
+ mapFlow.removeAsync(flowId.value());
+ }
+
+ /**
+ * Send a notification that a Flow is updated.
+ *
+ * @param flowPath the Flow that is updated.
+ */
+ @Override
+ public void notificationSendFlowUpdated(FlowPath flowPath) {
+ // NOTE: Adding an entry with an existing key automatically updates it
+ notificationSendFlowAdded(flowPath);
+ }
+
+ /**
+ * Send a notification that all Flows are removed.
+ */
+ @Override
+ public void notificationSendAllFlowsRemoved() {
+ //
+ // Remove all entries
+ // NOTE: We remove the entries one-by-one so the per-entry
+ // notifications will be delivered.
+ //
+ // mapFlow.clear();
+ Set<Long> keySet = mapFlow.keySet();
+ for (Long key : keySet) {
+ mapFlow.removeAsync(key);
+ }
+ }
+
+ /**
+ * 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.
+ */
+ @Override
+ public Collection<TopologyElement> getAllTopologyElements() {
+ Collection<TopologyElement> allTopologyElements =
+ new LinkedList<TopologyElement>();
+
+ //
+ // Get all current entries
+ //
+ Collection<byte[]> values = mapTopology.values();
+ Kryo kryo = kryoFactory.newKryo();
+ for (byte[] valueBytes : values) {
+ //
+ // Decode the value
+ //
+ Input input = new Input(valueBytes);
+ TopologyElement topologyElement =
+ kryo.readObject(input, TopologyElement.class);
+ allTopologyElements.add(topologyElement);
+ }
+ kryoFactory.deleteKryo(kryo);
+
+ return allTopologyElements;
+ }
+
+ /**
+ * Send a notification that a Topology Element is added.
+ *
+ * @param topologyElement the Topology Element that is added.
+ */
+ @Override
+ public void notificationSendTopologyElementAdded(TopologyElement topologyElement) {
+ //
+ // Encode the value
+ //
+ byte[] buffer = new byte[MAX_BUFFER_SIZE];
+ Kryo kryo = kryoFactory.newKryo();
+ Output output = new Output(buffer, -1);
+ kryo.writeObject(output, topologyElement);
+ byte[] valueBytes = output.toBytes();
+ kryoFactory.deleteKryo(kryo);
+
+ //
+ // Put the entry:
+ // - Key : TopologyElement ID (String)
+ // - Value : Serialized TopologyElement (byte[])
+ //
+ mapTopology.putAsync(topologyElement.elementId(), valueBytes);
+ }
+
+ /**
+ * Send a notification that a Topology Element is removed.
+ *
+ * @param topologyElement the Topology Element that is removed.
+ */
+ @Override
+ public void notificationSendTopologyElementRemoved(TopologyElement topologyElement) {
+ //
+ // Remove the entry:
+ // - Key : TopologyElement ID (String)
+ // - Value : Serialized TopologyElement (byte[])
+ //
+ mapTopology.removeAsync(topologyElement.elementId());
+ }
+
+ /**
+ * Send a notification that a Topology Element is updated.
+ *
+ * @param topologyElement the Topology Element that is updated.
+ */
+ @Override
+ public void notificationSendTopologyElementUpdated(TopologyElement topologyElement) {
+ // NOTE: Adding an entry with an existing key automatically updates it
+ notificationSendTopologyElementAdded(topologyElement);
+ }
+
+ /**
+ * Send a notification that all Topology Elements are removed.
+ */
+ @Override
+ public void notificationSendAllTopologyElementsRemoved() {
+ //
+ // Remove all entries
+ // NOTE: We remove the entries one-by-one so the per-entry
+ // notifications will be delivered.
+ //
+ // mapTopology.clear();
+ Set<String> keySet = mapTopology.keySet();
+ for (String key : keySet) {
+ mapTopology.removeAsync(key);
+ }
}
}
diff --git a/src/main/java/net/onrc/onos/datagrid/IDatagridService.java b/src/main/java/net/onrc/onos/datagrid/IDatagridService.java
index 1c7f3ab..1bcf601 100644
--- a/src/main/java/net/onrc/onos/datagrid/IDatagridService.java
+++ b/src/main/java/net/onrc/onos/datagrid/IDatagridService.java
@@ -1,10 +1,137 @@
package net.onrc.onos.datagrid;
+import java.util.Collection;
+
import net.floodlightcontroller.core.module.IFloodlightService;
+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;
+
/**
* Interface for providing Datagrid Service to other modules.
*/
public interface IDatagridService extends IFloodlightService {
- // TODO
+ /**
+ * Register Flow Event Handler Service for receiving Flow-related
+ * notifications.
+ *
+ * NOTE: Only a single Flow Event Handler Service can be registered.
+ *
+ * @param flowEventHandlerService the Flow Event Handler Service to register.
+ */
+ void registerFlowEventHandlerService(IFlowEventHandlerService flowEventHandlerService);
+
+ /**
+ * De-register Flow Event Handler Service for receiving Flow-related
+ * notifications.
+ *
+ * NOTE: Only a single Flow Event Handler Service can be registered.
+ *
+ * @param flowEventHandlerService the Flow Event Handler Service to
+ * de-register.
+ */
+ void deregisterFlowEventHandlerService(IFlowEventHandlerService flowEventHandlerService);
+
+ /**
+ * Get all Flows that are currently in the datagrid.
+ *
+ * @return all Flows that are currently in the datagrid.
+ */
+ Collection<FlowPath> getAllFlows();
+
+ /**
+ * Send a notification that a Flow 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.
+ */
+ void notificationSendFlowRemoved(FlowId flowId);
+
+ /**
+ * Send a notification that a Flow is updated.
+ *
+ * @param flowPath the Flow that is updated.
+ */
+ void notificationSendFlowUpdated(FlowPath flowPath);
+
+ /**
+ * Send a notification that all Flows are removed.
+ */
+ 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.
+ */
+ Collection<TopologyElement> getAllTopologyElements();
+
+ /**
+ * Send a notification that a Topology Element is added.
+ *
+ * @param topologyElement the Topology Element that is added.
+ */
+ void notificationSendTopologyElementAdded(TopologyElement topologyElement);
+
+ /**
+ * Send a notification that a Topology Element is removed.
+ *
+ * @param topologyElement the Topology Element that is removed.
+ */
+ void notificationSendTopologyElementRemoved(TopologyElement topologyElement);
+
+ /**
+ * Send a notification that a Topology Element is updated.
+ *
+ * @param topologyElement the Topology Element that is updated.
+ */
+ void notificationSendTopologyElementUpdated(TopologyElement topologyElement);
+
+ /**
+ * Send a notification that all Topology Elements are removed.
+ */
+ void notificationSendAllTopologyElementsRemoved();
}
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 7b38fef..6f13080 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/core/INetMapTopologyObjects.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/core/INetMapTopologyObjects.java
@@ -199,6 +199,20 @@
@Property("installer_id")
public void setInstallerId(String installerId);
+ @JsonProperty("flowPathType")
+ @Property("flow_path_type")
+ public String getFlowPathType();
+
+ @Property("flow_path_type")
+ public void setFlowPathType(String flowPathType);
+
+ @JsonProperty("flowPathUserState")
+ @Property("user_state")
+ public String getFlowPathUserState();
+
+ @Property("user_state")
+ public void setFlowPathUserState(String userState);
+
@JsonProperty("flowPathFlags")
@Property("flow_path_flags")
public Long getFlowPathFlags();
@@ -351,13 +365,6 @@
@JsonIgnore
@Property("state")
public String getState();
-
- @JsonIgnore
- @Property("user_state")
- public String getUserState();
-
- @Property("user_state")
- public void setUserState(String userState);
}
public interface IFlowEntry extends IBaseObject {
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 9eefba3..14cffd8 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/floodlightlistener/NetworkGraphPublisher.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/floodlightlistener/NetworkGraphPublisher.java
@@ -25,6 +25,8 @@
import net.floodlightcontroller.devicemanager.IDeviceService;
import net.floodlightcontroller.routing.Link;
import net.floodlightcontroller.threadpool.IThreadPoolService;
+
+import net.onrc.onos.datagrid.IDatagridService;
import net.onrc.onos.graph.GraphDBConnection;
import net.onrc.onos.graph.GraphDBOperation;
import net.onrc.onos.graph.IDBConnection;
@@ -42,6 +44,7 @@
import net.onrc.onos.ofcontroller.linkdiscovery.ILinkDiscoveryListener;
import net.onrc.onos.ofcontroller.linkdiscovery.ILinkDiscoveryService;
import net.onrc.onos.ofcontroller.linkdiscovery.LinkInfo;
+import net.onrc.onos.ofcontroller.topology.TopologyElement;
import net.onrc.onos.registry.controller.IControllerRegistryService;
import net.onrc.onos.registry.controller.IControllerRegistryService.ControlChangeCallback;
import net.onrc.onos.registry.controller.RegistryException;
@@ -69,6 +72,8 @@
protected final int CLEANUP_TASK_INTERVAL = 60; // 1 min
protected SingletonTask cleanupTask;
protected ILinkDiscoveryService linkDiscovery;
+
+ protected IDatagridService datagridService;
/**
* Cleanup and synch switch state from registry
@@ -98,6 +103,15 @@
registryService.releaseControl(dpid);
// TODO publish UPDATE_SWITCH event here
+ //
+ // NOTE: Here we explicitly send
+ // notification to remove the
+ // switch, because it is inactive
+ //
+ TopologyElement topologyElement =
+ new TopologyElement(dpid);
+ datagridService.notificationSendTopologyElementRemoved(topologyElement);
+
}
} catch (Exception e) {
log.error("Error in SwitchCleanup:controlChanged ", e);
@@ -136,7 +150,6 @@
@Override
public void linkDiscoveryUpdate(LDUpdate update) {
- // TODO Auto-generated method stub
Link lt = new Link(update.getSrc(),update.getSrcPort(),update.getDst(),update.getDstPort());
//log.debug("{}:LinkDicoveryUpdate(): Updating Link {}",this.getClass(), lt);
@@ -146,6 +159,12 @@
if (linkStore.deleteLink(lt)) {
// TODO publish DELETE_LINK event here
+ TopologyElement topologyElement =
+ new TopologyElement(update.getSrc(),
+ update.getSrcPort(),
+ update.getDst(),
+ update.getDstPort());
+ datagridService.notificationSendTopologyElementRemoved(topologyElement);
}
break;
case LINK_UPDATED:
@@ -155,6 +174,16 @@
// TODO update "linfo" using portState derived using "update"
if (linkStore.update(lt, linfo, DM_OPERATION.UPDATE)) {
// TODO publish UPDATE_LINK event here
+ //
+ // TODO NOTE: Here we assume that updated
+ // link is UP.
+ //
+ TopologyElement topologyElement =
+ new TopologyElement(update.getSrc(),
+ update.getSrcPort(),
+ update.getDst(),
+ update.getDstPort());
+ datagridService.notificationSendTopologyElementUpdated(topologyElement);
}
break;
case LINK_ADDED:
@@ -162,6 +191,12 @@
if (linkStore.addLink(lt)) {
// TODO publish ADD_LINK event here
+ TopologyElement topologyElement =
+ new TopologyElement(update.getSrc(),
+ update.getSrcPort(),
+ update.getDst(),
+ update.getDstPort());
+ datagridService.notificationSendTopologyElementAdded(topologyElement);
}
break;
default:
@@ -175,6 +210,13 @@
if (registryService.hasControl(sw.getId())) {
if (swStore.addSwitch(sw)) {
// TODO publish ADD_SWITCH event here
+ TopologyElement topologyElement =
+ new TopologyElement(sw.getId());
+ // TODO: Add only ports that are UP?
+ for (OFPhysicalPort port : sw.getPorts()) {
+ topologyElement.addSwitchPort(port.getPortNumber());
+ }
+ datagridService.notificationSendTopologyElementAdded(topologyElement);
}
}
}
@@ -184,13 +226,15 @@
if (registryService.hasControl(sw.getId())) {
if (swStore.deleteSwitch(sw.getStringId())) {
// TODO publish DELETE_SWITCH event here
+ TopologyElement topologyElement =
+ new TopologyElement(sw.getId());
+ datagridService.notificationSendTopologyElementRemoved(topologyElement);
}
}
}
@Override
public void switchPortChanged(Long switchId) {
- // TODO Auto-generated method stub
// NOTE: Event not needed here. This callback always coincide with add/remove callback.
}
@@ -199,6 +243,9 @@
public void switchPortAdded(Long switchId, OFPhysicalPort port) {
if (swStore.addPort(HexString.toHexString(switchId), port)) {
// TODO publish ADD_PORT event here
+ TopologyElement topologyElement =
+ new TopologyElement(switchId, port.getPortNumber());
+ datagridService.notificationSendTopologyElementAdded(topologyElement);
}
}
@@ -206,6 +253,9 @@
public void switchPortRemoved(Long switchId, OFPhysicalPort port) {
if (swStore.deletePort(HexString.toHexString(switchId), port.getPortNumber())) {
// TODO publish DELETE_PORT event here
+ TopologyElement topologyElement =
+ new TopologyElement(switchId, port.getPortNumber());
+ datagridService.notificationSendTopologyElementRemoved(topologyElement);
}
}
@@ -223,7 +273,6 @@
@Override
public void deviceRemoved(IDevice device) {
// TODO Auto-generated method stub
-
}
@Override
@@ -266,6 +315,7 @@
new ArrayList<Class<? extends IFloodlightService>>();
l.add(IFloodlightProviderService.class);
l.add(IDeviceService.class);
+ l.add(IDatagridService.class);
l.add(IThreadPoolService.class);
return l;
}
@@ -283,6 +333,7 @@
linkDiscovery = context.getServiceImpl(ILinkDiscoveryService.class);
threadPool = context.getServiceImpl(IThreadPoolService.class);
registryService = context.getServiceImpl(IControllerRegistryService.class);
+ datagridService = context.getServiceImpl(IDatagridService.class);
devStore = new DeviceStorageImpl();
devStore.init(conf);
@@ -315,6 +366,11 @@
cleanupTask = new SingletonTask(ses, new SwitchCleanup());
cleanupTask.reschedule(CLEANUP_TASK_INTERVAL, TimeUnit.SECONDS);
}
+
+ //
+ // NOTE: No need to register with the Datagrid Service,
+ // because we don't need to receive any notifications from it.
+ //
}
}
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 3178b60..d06c62c 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/FlowDatabaseOperation.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/FlowDatabaseOperation.java
@@ -35,14 +35,11 @@
* @param dbHandler the Graph Database handler to use.
* @param flowPath the Flow Path to install.
* @param flowId the return-by-reference Flow ID as assigned internally.
- * @param dataPathSummaryStr the data path summary string if the added
- * flow will be maintained internally, otherwise null.
* @return true on success, otherwise false.
*/
static boolean addFlow(FlowManager flowManager,
GraphDBOperation dbHandler,
- FlowPath flowPath, FlowId flowId,
- String dataPathSummaryStr) {
+ FlowPath flowPath, FlowId flowId) {
IFlowPath flowObj = null;
boolean found = false;
try {
@@ -80,6 +77,8 @@
//
// Set the Flow attributes:
// - flowPath.installerId()
+ // - flowPath.flowPathType()
+ // - flowPath.flowPathUserState()
// - flowPath.flowPathFlags()
// - flowPath.dataPath().srcPort()
// - flowPath.dataPath().dstPort()
@@ -97,6 +96,8 @@
// - flowPath.flowEntryActions()
//
flowObj.setInstallerId(flowPath.installerId().toString());
+ flowObj.setFlowPathType(flowPath.flowPathType().toString());
+ flowObj.setFlowPathUserState(flowPath.flowPathUserState().toString());
flowObj.setFlowPathFlags(flowPath.flowPathFlags().flags());
flowObj.setSrcSwitch(flowPath.dataPath().srcPort().dpid().toString());
flowObj.setSrcPort(flowPath.dataPath().srcPort().port().value());
@@ -138,17 +139,12 @@
if (! flowPath.flowEntryActions().actions().isEmpty()) {
flowObj.setActions(flowPath.flowEntryActions().toString());
}
-
- if (dataPathSummaryStr != null) {
- flowObj.setDataPathSummary(dataPathSummaryStr);
- } else {
- flowObj.setDataPathSummary("");
- }
+ flowObj.setDataPathSummary(flowPath.dataPath().dataPathSummary());
if (found)
- flowObj.setUserState("FE_USER_MODIFY");
+ flowObj.setFlowPathUserState("FP_USER_MODIFY");
else
- flowObj.setUserState("FE_USER_ADD");
+ flowObj.setFlowPathUserState("FP_USER_ADD");
// Flow edges:
// HeadFE
@@ -313,7 +309,7 @@
flowEntryObj.setUserState("FE_USER_MODIFY");
else
flowEntryObj.setUserState("FE_USER_ADD");
- flowEntryObj.setSwitchState("FE_SWITCH_NOT_UPDATED");
+ flowEntryObj.setSwitchState(flowEntry.flowEntrySwitchState().toString());
//
// TODO: Take care of the FlowEntryErrorState.
//
@@ -427,7 +423,7 @@
// Find and mark for deletion all Flow Entries,
// and the Flow itself.
//
- flowObj.setUserState("FE_USER_DELETE");
+ flowObj.setFlowPathUserState("FP_USER_DELETE");
Iterable<IFlowEntry> flowEntries = flowObj.getFlowEntries();
boolean empty = true; // TODO: an ugly hack
for (IFlowEntry flowEntryObj : flowEntries) {
@@ -751,6 +747,8 @@
//
String flowIdStr = flowObj.getFlowId();
String installerIdStr = flowObj.getInstallerId();
+ String flowPathType = flowObj.getFlowPathType();
+ String flowPathUserState = flowObj.getFlowPathUserState();
Long flowPathFlags = flowObj.getFlowPathFlags();
String srcSwitchStr = flowObj.getSrcSwitch();
Short srcPortShort = flowObj.getSrcPort();
@@ -759,6 +757,8 @@
if ((flowIdStr == null) ||
(installerIdStr == null) ||
+ (flowPathType == null) ||
+ (flowPathUserState == null) ||
(flowPathFlags == null) ||
(srcSwitchStr == null) ||
(srcPortShort == null) ||
@@ -771,6 +771,8 @@
FlowPath flowPath = new FlowPath();
flowPath.setFlowId(new FlowId(flowIdStr));
flowPath.setInstallerId(new CallerId(installerIdStr));
+ flowPath.setFlowPathType(FlowPathType.valueOf(flowPathType));
+ flowPath.setFlowPathUserState(FlowPathUserState.valueOf(flowPathUserState));
flowPath.setFlowPathFlags(new FlowPathFlags(flowPathFlags));
flowPath.dataPath().srcPort().setDpid(new Dpid(srcSwitchStr));
flowPath.dataPath().srcPort().setPort(new Port(srcPortShort));
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..29deb94
--- /dev/null
+++ b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/FlowEventHandler.java
@@ -0,0 +1,661 @@
+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.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;
+
+/**
+ * Class for implementing the Path Computation and Path Maintenance.
+ */
+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 List<FlowEntry> unmatchedFlowEntryUpdates =
+ new LinkedList<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>>();
+
+ /**
+ * 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();
+ }
+
+ /**
+ * 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);
+ }
+ //
+ // 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 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>
+ // - 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<FlowPath> newFlowPaths = new LinkedList<FlowPath>();
+ List<FlowPath> recomputeFlowPaths = new LinkedList<FlowPath>();
+ List<FlowPath> modifiedFlowPaths = new LinkedList<FlowPath>();
+
+ if (topologyEvents.isEmpty() && flowPathEvents.isEmpty() &&
+ flowEntryEvents.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);
+ }
+
+ //
+ // Process previously unmatched Flow Entry updates
+ //
+ if ((! flowPathEvents.isEmpty()) && (! unmatchedFlowEntryUpdates.isEmpty())) {
+ List<FlowEntry> remainingUpdates = new LinkedList<FlowEntry>();
+ for (FlowEntry flowEntry : unmatchedFlowEntryUpdates) {
+ if (! updateFlowEntry(flowEntry))
+ remainingUpdates.add(flowEntry);
+ }
+ unmatchedFlowEntryUpdates = remainingUpdates;
+ }
+
+ //
+ // Process the Flow Entry events
+ //
+ for (EventEntry<FlowEntry> eventEntry : flowEntryEvents) {
+ FlowEntry flowEntry = eventEntry.eventData();
+ switch (eventEntry.eventType()) {
+ case ENTRY_ADD:
+ //
+ // Find the corresponding Flow Entry and update it.
+ // If not found, then keep it in a local cache for
+ // later matching.
+ //
+ if (! updateFlowEntry(flowEntry))
+ unmatchedFlowEntryUpdates.add(flowEntry);
+ break;
+ case ENTRY_REMOVE:
+ //
+ // NOTE: For now we remove the Flow Entries based on
+ // local decisions, so no need to remove them because of an
+ // external event.
+ //
+ break;
+ }
+ }
+
+ //
+ // Push the Flow Entries that have been modified
+ //
+ flowManager.pushModifiedFlowEntries(modifiedFlowPaths);
+
+ // Cleanup
+ topologyEvents.clear();
+ flowPathEvents.clear();
+ flowEntryEvents.clear();
+ }
+
+ /**
+ * Update a Flow Entry because of an external event.
+ *
+ * @param flowEntry the FlowEntry with the new state.
+ * @return true if the Flow Entry was found and updated, otherwise false.
+ */
+ private boolean updateFlowEntry(FlowEntry flowEntry) {
+ if ((! flowEntry.isValidFlowId()) ||
+ (! flowEntry.isValidFlowEntryId())) {
+ //
+ // Ignore events for Flow Entries with invalid Flow ID or
+ // Flow Entry ID.
+ // This shouldn't happen.
+ //
+ return true;
+ }
+
+ FlowPath flowPath = allFlowPaths.get(flowEntry.flowId().value());
+ if (flowPath == null)
+ return false;
+
+ //
+ // Iterate over all Flow Entries and find a match based on the DPID
+ //
+ for (FlowEntry localFlowEntry : flowPath.flowEntries()) {
+ if (localFlowEntry.dpid().value() != flowEntry.dpid().value())
+ continue;
+ //
+ // TODO: We might want to check the FlowEntryMatch and
+ // FlowEntryActions to double-check it is the same Flow Entry
+ //
+
+ //
+ // 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. Ignore the event.
+ //
+ return true;
+ }
+ } else {
+ // Update the Flow Entry ID
+ FlowEntryId flowEntryId =
+ new FlowEntryId(flowEntry.flowEntryId().value());
+ localFlowEntry.setFlowEntryId(flowEntryId);
+ }
+
+ //
+ // Update the local Flow Entry.
+ // For now we update only the Flow Entry Switch State
+ //
+ localFlowEntry.setFlowEntrySwitchState(flowEntry.flowEntrySwitchState());
+ return true;
+ }
+
+ return false; // 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_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
+ //
+ //
+ // 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.
+ //
+ 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 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 cad5d9c..4465835 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/FlowManager.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/FlowManager.java
@@ -29,6 +29,7 @@
import net.onrc.onos.ofcontroller.flowmanager.web.FlowWebRoutable;
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.OFType;
@@ -47,6 +48,7 @@
protected volatile IDatagridService datagridService;
protected IRestApiService restApi;
protected FloodlightModuleContext context;
+ protected FlowEventHandler flowEventHandler;
protected OFMessageDamper messageDamper;
@@ -260,20 +262,20 @@
if (mySwitch == null)
continue; // Ignore: not my responsibility
- // Test the Data Path Summary string
- String dataPathSummaryStr = flowPathObj.getDataPathSummary();
- if (dataPathSummaryStr == null)
+ // Test whether we need to maintain this flow
+ String flowPathTypeStr = flowPathObj.getFlowPathType();
+ if (flowPathTypeStr == null)
continue; // Could be invalid entry?
- if (dataPathSummaryStr.isEmpty())
+ if (! flowPathTypeStr.equals("FP_TYPE_SHORTEST_PATH"))
continue; // No need to maintain this flow
//
// Test whether we need to complete the Flow cleanup,
// if the Flow has been deleted by the user.
//
- String flowUserState = flowPathObj.getUserState();
- if ((flowUserState != null)
- && flowUserState.equals("FE_USER_DELETE")) {
+ String flowPathUserStateStr = flowPathObj.getFlowPathUserState();
+ if ((flowPathUserStateStr != null)
+ && flowPathUserStateStr.equals("FP_USER_DELETE")) {
Iterable<IFlowEntry> flowEntries = flowPathObj.getFlowEntries();
final boolean empty = !flowEntries.iterator().hasNext();
if (empty)
@@ -281,11 +283,13 @@
}
// Fetch the fields needed to recompute the shortest path
+ String dataPathSummaryStr = flowPathObj.getDataPathSummary();
Short srcPortShort = flowPathObj.getSrcPort();
String dstDpidStr = flowPathObj.getDstSwitch();
Short dstPortShort = flowPathObj.getDstPort();
Long flowPathFlagsLong = flowPathObj.getFlowPathFlags();
- if ((srcPortShort == null) ||
+ if ((dataPathSummaryStr == null) ||
+ (srcPortShort == null) ||
(dstDpidStr == null) ||
(dstPortShort == null) ||
(flowPathFlagsLong == null)) {
@@ -297,6 +301,8 @@
Port dstPort = new Port(dstPortShort);
SwitchPort srcSwitchPort = new SwitchPort(srcDpid, srcPort);
SwitchPort dstSwitchPort = new SwitchPort(dstDpid, dstPort);
+ FlowPathType flowPathType = FlowPathType.valueOf(flowPathTypeStr);
+ FlowPathUserState flowPathUserState = FlowPathUserState.valueOf(flowPathUserStateStr);
FlowPathFlags flowPathFlags = new FlowPathFlags(flowPathFlagsLong);
counterMyFlowPaths++;
@@ -379,6 +385,7 @@
*/
@Override
public void close() {
+ datagridService.deregisterFlowEventHandlerService(flowEventHandler);
dbHandler.close();
}
@@ -487,6 +494,15 @@
// Initialize the Flow Entry ID generator
nextFlowEntryIdPrefix = randomGenerator.nextInt();
+ //
+ // Create the Flow Event Handler thread and register it with the
+ // Datagrid Service
+ //
+ flowEventHandler = new FlowEventHandler(this, datagridService);
+ datagridService.registerFlowEventHandlerService(flowEventHandler);
+
+ // Schedule the threads and periodic tasks
+ flowEventHandler.start();
mapReaderScheduler.scheduleAtFixedRate(
mapReader, 3, 3, TimeUnit.SECONDS);
shortestPathReconcileScheduler.scheduleAtFixedRate(
@@ -498,15 +514,28 @@
*
* @param flowPath the Flow Path to install.
* @param flowId the return-by-reference Flow ID as assigned internally.
- * @param dataPathSummaryStr the data path summary string if the added
- * flow will be maintained internally, otherwise null.
* @return true on success, otherwise false.
*/
@Override
- public boolean addFlow(FlowPath flowPath, FlowId flowId,
- String dataPathSummaryStr) {
- return FlowDatabaseOperation.addFlow(this, dbHandler, flowPath, flowId,
- dataPathSummaryStr);
+ public boolean addFlow(FlowPath flowPath, FlowId flowId) {
+ //
+ // NOTE: We need to explicitly initialize some of the state,
+ // in case the application didn't do it.
+ //
+ for (FlowEntry flowEntry : flowPath.flowEntries()) {
+ if (flowEntry.flowEntrySwitchState() ==
+ 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)) {
+ datagridService.notificationSendFlowAdded(flowPath);
+ return true;
+ }
+ return false;
}
/**
@@ -528,7 +557,11 @@
*/
@Override
public boolean deleteAllFlows() {
- return FlowDatabaseOperation.deleteAllFlows(dbHandler);
+ if (FlowDatabaseOperation.deleteAllFlows(dbHandler)) {
+ datagridService.notificationSendAllFlowsRemoved();
+ return true;
+ }
+ return false;
}
/**
@@ -539,7 +572,11 @@
*/
@Override
public boolean deleteFlow(FlowId flowId) {
- return FlowDatabaseOperation.deleteFlow(dbHandler, flowId);
+ if (FlowDatabaseOperation.deleteFlow(dbHandler, flowId)) {
+ datagridService.notificationSendFlowRemoved(flowId);
+ return true;
+ }
+ return false;
}
/**
@@ -549,7 +586,11 @@
*/
@Override
public boolean clearAllFlows() {
- return FlowDatabaseOperation.clearAllFlows(dbHandler);
+ if (FlowDatabaseOperation.clearAllFlows(dbHandler)) {
+ datagridService.notificationSendAllFlowsRemoved();
+ return true;
+ }
+ return false;
}
/**
@@ -560,7 +601,11 @@
*/
@Override
public boolean clearFlow(FlowId flowId) {
- return FlowDatabaseOperation.clearFlow(dbHandler, flowId);
+ if (FlowDatabaseOperation.clearFlow(dbHandler, flowId)) {
+ datagridService.notificationSendFlowRemoved(flowId);
+ return true;
+ }
+ return false;
}
/**
@@ -649,30 +694,11 @@
// Instead, let the Flow reconciliation thread take care of it.
//
- // We need the DataPath to populate the Network MAP
- DataPath dataPath = new DataPath();
- dataPath.setSrcPort(flowPath.dataPath().srcPort());
- dataPath.setDstPort(flowPath.dataPath().dstPort());
-
- //
- // Prepare the computed Flow Path
- //
- FlowPath computedFlowPath = new FlowPath();
- computedFlowPath.setFlowId(new FlowId(flowPath.flowId().value()));
- computedFlowPath.setInstallerId(new CallerId(flowPath.installerId().value()));
- computedFlowPath.setFlowPathFlags(new FlowPathFlags(flowPath.flowPathFlags().flags()));
- computedFlowPath.setDataPath(dataPath);
- computedFlowPath.setFlowEntryMatch(new FlowEntryMatch(flowPath.flowEntryMatch()));
- computedFlowPath.setFlowEntryActions(new FlowEntryActions(flowPath.flowEntryActions()));
-
FlowId flowId = new FlowId();
- String dataPathSummaryStr = dataPath.dataPathSummary();
- if (! addFlow(computedFlowPath, flowId, dataPathSummaryStr))
+ if (! addFlow(flowPath, flowId))
return null;
- // TODO: Mark the flow for maintenance purpose
-
- return (computedFlowPath);
+ return (flowPath);
}
/**
@@ -682,7 +708,8 @@
* @param newDataPath the new data path to use.
* @return true on success, otherwise false.
*/
- public boolean reconcileFlow(IFlowPath flowObj, DataPath newDataPath) {
+ private boolean reconcileFlow(IFlowPath flowObj, DataPath newDataPath) {
+ String flowIdStr = flowObj.getFlowId();
//
// Set the incoming port matching and the outgoing port output
@@ -690,6 +717,10 @@
//
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
FlowEntryMatch flowEntryMatch = new FlowEntryMatch();
flowEntry.setFlowEntryMatch(flowEntryMatch);
@@ -745,7 +776,7 @@
*
* @param flowObjSet the set of flows that need to be reconciliated.
*/
- public void reconcileFlows(Iterable<IFlowPath> flowObjSet) {
+ private void reconcileFlows(Iterable<IFlowPath> flowObjSet) {
if (! flowObjSet.iterator().hasNext())
return;
// TODO: Not implemented/used yet.
@@ -759,7 +790,7 @@
* @param flowEntryObj the flow entry object to install.
* @return true on success, otherwise false.
*/
- public boolean installFlowEntry(IOFSwitch mySwitch, IFlowPath flowObj,
+ private boolean installFlowEntry(IOFSwitch mySwitch, IFlowPath flowObj,
IFlowEntry flowEntryObj) {
return FlowSwitchOperation.installFlowEntry(
floodlightProvider.getOFMessageFactory(),
@@ -774,7 +805,7 @@
* @param flowEntry the flow entry to install.
* @return true on success, otherwise false.
*/
- public boolean installFlowEntry(IOFSwitch mySwitch, FlowPath flowPath,
+ private boolean installFlowEntry(IOFSwitch mySwitch, FlowPath flowPath,
FlowEntry flowEntry) {
return FlowSwitchOperation.installFlowEntry(
floodlightProvider.getOFMessageFactory(),
@@ -789,7 +820,7 @@
* @param flowEntry the flow entry to remove.
* @return true on success, otherwise false.
*/
- public boolean removeFlowEntry(IOFSwitch mySwitch, FlowPath flowPath,
+ private boolean removeFlowEntry(IOFSwitch mySwitch, FlowPath flowPath,
FlowEntry flowEntry) {
//
// The installFlowEntry() method implements both installation
@@ -797,4 +828,105 @@
//
return (installFlowEntry(mySwitch, flowPath, flowEntry));
}
+
+ /**
+ * Push the modified Flow Entries of a collection of Flow Paths.
+ * 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.
+ */
+ public void pushModifiedFlowEntries(Collection<FlowPath> modifiedFlowPaths) {
+
+ // TODO: For now, the pushing of Flow Entries is disabled
+ if (true)
+ return;
+
+ Map<Long, IOFSwitch> mySwitches = floodlightProvider.getSwitches();
+
+ 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();
+ log.error(logMsg);
+ continue;
+ }
+
+ for (FlowEntry flowEntry : flowPath.flowEntries()) {
+ if (flowEntry.flowEntrySwitchState() !=
+ FlowEntrySwitchState.FE_SWITCH_NOT_UPDATED) {
+ continue; // No need to update the entry
+ }
+
+ IOFSwitch mySwitch = mySwitches.get(flowEntry.dpid().value());
+ if (mySwitch == null)
+ continue; // Ignore the entry: not my switch
+
+ //
+ // Assign the FlowEntry ID if needed
+ //
+ if (! flowEntry.isValidFlowEntryId()) {
+ long id = getNextFlowEntryId();
+ flowEntry.setFlowEntryId(new FlowEntryId(id));
+ }
+
+ //
+ // 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;
+ }
+
+ //
+ // NOTE: Here we assume that the switch has been successfully
+ // updated.
+ //
+ flowEntry.setFlowEntrySwitchState(FlowEntrySwitchState.FE_SWITCH_UPDATED);
+ //
+ // Write the Flow Entry to the Datagrid
+ //
+ switch (flowEntry.flowEntryUserState()) {
+ case FE_USER_ADD:
+ datagridService.notificationSendFlowEntryAdded(flowEntry);
+ break;
+ case FE_USER_MODIFY:
+ datagridService.notificationSendFlowEntryUpdated(flowEntry);
+ break;
+ case FE_USER_DELETE:
+ datagridService.notificationSendFlowEntryRemoved(flowEntry.flowEntryId());
+ break;
+ }
+
+ //
+ // Write the Flow Entry to the Network Map
+ //
+ 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();
+ log.error(logMsg);
+ continue;
+ }
+ } catch (Exception e) {
+ String logMsg = "Exception writing Flow Entry to Network MAP";
+ log.debug(logMsg);
+ dbHandler.rollback();
+ continue;
+ }
+ }
+ }
+
+ dbHandler.commit();
+ }
}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/FlowSwitchOperation.java b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/FlowSwitchOperation.java
index 3b64096..9741b04 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/FlowSwitchOperation.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/FlowSwitchOperation.java
@@ -202,6 +202,8 @@
String actionsStr = flowEntryObj.getActions();
if (actionsStr != null)
flowEntryActions = new FlowEntryActions(actionsStr);
+ else
+ flowEntryActions = new FlowEntryActions();
for (FlowEntryAction action : flowEntryActions.actions()) {
ActionOutput actionOutput = action.actionOutput();
ActionSetVlanId actionSetVlanId = action.actionSetVlanId();
@@ -655,6 +657,14 @@
//
// Write the message to the switch
//
+ log.debug("MEASUREMENT: Installing flow entry " +
+ flowEntry.flowEntryUserState() +
+ " into switch DPID: " +
+ mySwitch.getStringId() +
+ " flowEntryId: " + flowEntry.flowEntryId().toString() +
+ " srcMac: " + matchSrcMac + " dstMac: " + matchDstMac +
+ " inPort: " + matchInPort + " outPort: " + actionOutputPort
+ );
try {
messageDamper.write(mySwitch, fm, null);
mySwitch.flush();
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 73f86b6..1f8cd5b 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/IFlowService.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/IFlowService.java
@@ -21,12 +21,9 @@
*
* @param flowPath the Flow Path to install.
* @param flowId the return-by-reference Flow ID as assigned internally.
- * @param dataPathSummaryStr the data path summary string if the added
- * flow will be maintained internally, otherwise null.
* @return true on success, otherwise false.
*/
- boolean addFlow(FlowPath flowPath, FlowId flowId,
- String dataPathSummaryStr);
+ boolean addFlow(FlowPath flowPath, FlowId flowId);
/**
* Delete all previously added flows.
@@ -114,5 +111,5 @@
* conditions to install.
* @return the added shortest-path flow on success, otherwise null.
*/
- public FlowPath addAndMaintainShortestPathFlow(FlowPath flowPath);
+ FlowPath addAndMaintainShortestPathFlow(FlowPath flowPath);
}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/web/AddFlowResource.java b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/web/AddFlowResource.java
index d891374..0926f91 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/web/AddFlowResource.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/web/AddFlowResource.java
@@ -64,7 +64,7 @@
// Process the request
if (flowPath != null) {
- if (flowService.addFlow(flowPath, result, null) != true) {
+ if (flowService.addFlow(flowPath, result) != true) {
result = new FlowId(); // Error: Return empty Flow Id
}
}
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 1133d3d..dabe916 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/topology/ShortestPath.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/topology/ShortestPath.java
@@ -123,7 +123,7 @@
Port outPort;
for (Node.Link link: resultPath) {
// Setup the outgoing port, and add the Flow Entry
- outPort = new Port(link.myPort);
+ outPort = new Port((short)link.myPort);
FlowEntry flowEntry = new FlowEntry();
flowEntry.setDpid(new Dpid(link.me.nodeId));
@@ -132,7 +132,7 @@
result_data_path.flowEntries().add(flowEntry);
// Setup the next incoming port
- inPort = new Port(link.neighborPort);
+ inPort = new Port((short)link.neighborPort);
}
if (resultPath.size() > 0) {
// Add the last Flow Entry
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 a2f2c21..612b72a 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/topology/Topology.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/topology/Topology.java
@@ -24,18 +24,18 @@
class Link {
public Node me; // The node this link originates from
public Node neighbor; // The neighbor node on the other side
- public short myPort; // Local port number for the link
- public short neighborPort; // Neighbor port number for the link
+ public int myPort; // Local port ID for the link
+ public int neighborPort; // Neighbor port ID for the link
/**
* Link constructor.
*
* @param me the node this link originates from.
* @param the neighbor node on the other side of the link.
- * @param myPort local port number for the link.
- * @param neighborPort neighrobr port number for the link.
+ * @param myPort local port ID for the link.
+ * @param neighborPort neighbor port ID for the link.
*/
- public Link(Node me, Node neighbor, short myPort, short neighborPort) {
+ public Link(Node me, Node neighbor, int myPort, int neighborPort) {
this.me = me;
this.neighbor = neighbor;
this.myPort = myPort;
@@ -43,8 +43,10 @@
}
};
- public long nodeId; // The node ID
- public HashMap<Short, Link> links; // The links originating from this node
+ 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
/**
* Node constructor.
@@ -53,21 +55,124 @@
*/
public Node(long nodeId) {
this.nodeId = nodeId;
- links = new HashMap<Short, Link>();
+ links = new HashMap<Integer, Link>();
+ reverseLinksMap = new HashMap<Integer, Link>();
+ portsMap = new HashMap<Integer, Integer>();
}
/**
- * Add a neighbor.
+ * Get all ports.
*
- * A new link to the neighbor will be created.
- *
- * @param neighbor the neighbor to add.
- * @param myPort the local port number for the link to the neighbor.
- * @param neighborPort the neighbor port number for the link.
+ * @return all ports.
*/
- public void addNeighbor(Node neighbor, short myPort, short neighborPort) {
- Link link = new Link(this, neighbor, myPort, neighborPort);
- links.put(myPort, link);
+ public Map<Integer, Integer> ports() {
+ return portsMap;
+ }
+
+ /**
+ * Get the port for a given Port ID.
+ *
+ * Note: For now the port itself is just the Port ID. In the future
+ * it might contain more information.
+ *
+ * @return the port if found, otherwise null.
+ */
+ public Integer getPort(int portId) {
+ return portsMap.get(nodeId);
+ }
+
+ /**
+ * Add a port for a given Port ID.
+ *
+ * Note: For now the port itself is just the Port ID. In the future
+ * it might contain more information.
+ *
+ * @param portId the Port ID of the port to add.
+ * @return the added Port.
+ */
+ Integer addPort(int portId) {
+ Integer port = new Integer(portId);
+ portsMap.put(portId, port);
+ return port;
+ }
+
+ /**
+ * Remove a port for a given Port ID.
+ *
+ * NOTE: The outgoing and incoming links using this port are removed as
+ * well.
+ */
+ void removePort(int portId) {
+ // Remove the outgoing link
+ Link link = getLink(portId);
+ if (link != null) {
+ link.neighbor.removeReverseLink(link);
+ removeLink(portId);
+ }
+
+ // Remove the incoming link
+ Link reverseLink = reverseLinksMap.get(portId);
+ if (reverseLink != null) {
+ // NOTE: reverseLink.myPort is the neighbor's outgoing port
+ reverseLink.neighbor.removeLink(reverseLink.myPort);
+ removeReverseLink(reverseLink);
+ }
+
+ portsMap.remove(portId);
+ }
+
+ /**
+ * Get a link on a port to a neighbor.
+ *
+ * @param myPortId the local port ID for the link to the neighbor.
+ * @return the link if found, otherwise null.
+ */
+ public Link getLink(int myPortId) {
+ return links.get(myPortId);
+ }
+
+ /**
+ * Add a link to a neighbor.
+ *
+ * @param myPortId the local port ID for the link to the neighbor.
+ * @param neighbor the neighbor for the link.
+ * @param neighborPortId the neighbor port ID for the link.
+ * @return the added Link.
+ */
+ public Link addLink(int myPortId, Node neighbor, int neighborPortId) {
+ Link link = new Link(this, neighbor, myPortId, neighborPortId);
+ links.put(myPortId, link);
+ neighbor.addReverseLink(link);
+ return link;
+ }
+
+ /**
+ * Add a reverse link from a neighbor.
+ *
+ * @param link the reverse link from a neighbor to add.
+ */
+ private void addReverseLink(Link link) {
+ // NOTE: link.neghborPort is my port
+ reverseLinksMap.put(link.neighborPort, link);
+ }
+
+ /**
+ * Remove a link to a neighbor.
+ *
+ * @param myPortId the local port ID for the link to the neighbor.
+ */
+ public void removeLink(int myPortId) {
+ links.remove(myPortId);
+ }
+
+ /**
+ * Remove a reverse link from a neighbor.
+ *
+ * @param link the reverse link from a neighbor to remove.
+ */
+ private void removeReverseLink(Link link) {
+ // NOTE: link.neghborPort is my port
+ reverseLinksMap.remove(link.neighborPort);
}
};
@@ -77,12 +182,147 @@
public class Topology {
private Map<Long, Node> nodesMap; // The dpid->Node mapping
+ /**
+ * Default constructor.
+ */
public Topology() {
nodesMap = new HashMap<Long, Node>();
}
/**
- * Get a node for a give Node ID.
+ * Add a topology element to the topology.
+ *
+ * @param topologyElement the topology element to add.
+ * @return true if the topology was modified, otherwise false.
+ */
+ public boolean addTopologyElement(TopologyElement topologyElement) {
+ boolean isModified = false;
+
+ switch (topologyElement.getType()) {
+ case ELEMENT_SWITCH: {
+ // Add the switch
+ Node node = getNode(topologyElement.getSwitch());
+ if (node == null) {
+ 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: {
+ // Add the switch
+ Node node = getNode(topologyElement.getSwitch());
+ if (node == null) {
+ node = addNode(topologyElement.getSwitch());
+ isModified = true;
+ }
+ // Add the port for the switch
+ Integer port = node.getPort(topologyElement.getSwitchPort());
+ if (port == null) {
+ node.addPort(topologyElement.getSwitchPort());
+ isModified = true;
+ }
+ break;
+ }
+ case ELEMENT_LINK: {
+ // Add the "from" switch
+ Node fromNode = getNode(topologyElement.getFromSwitch());
+ if (fromNode == null) {
+ fromNode = addNode(topologyElement.getFromSwitch());
+ isModified = true;
+ }
+ // Add the "to" switch
+ Node toNode = getNode(topologyElement.getToSwitch());
+ if (toNode == null) {
+ toNode = addNode(topologyElement.getToSwitch());
+ isModified = true;
+ }
+ // Add the "from" port
+ Integer fromPort = fromNode.getPort(topologyElement.getFromPort());
+ if (fromPort == null) {
+ fromNode.addPort(topologyElement.getFromPort());
+ isModified = true;
+ }
+ // Add the "to" port
+ Integer toPort = fromNode.getPort(topologyElement.getToPort());
+ if (toPort == null) {
+ toNode.addPort(topologyElement.getToPort());
+ isModified = true;
+ }
+ Node.Link link = fromNode.getLink(topologyElement.getFromPort());
+ if (link == null) {
+ fromNode.addLink(topologyElement.getFromPort(),
+ toNode,
+ topologyElement.getToPort());
+ isModified = true;
+ }
+
+ break;
+ }
+ }
+
+ return isModified;
+ }
+
+ /**
+ * Remove a topology element from the topology.
+ *
+ * @param topologyElement the topology element to remove.
+ * @return true if the topology was modified, otherwise false.
+ */
+ public boolean removeTopologyElement(TopologyElement topologyElement) {
+ boolean isModified = false;
+
+ switch (topologyElement.getType()) {
+ case ELEMENT_SWITCH: {
+ // Remove the switch
+ Node node = getNode(topologyElement.getSwitch());
+ if (node != null) {
+ removeNode(node);
+ isModified = true;
+ }
+ break;
+ }
+ case ELEMENT_PORT: {
+ // Find the switch
+ Node node = getNode(topologyElement.getSwitch());
+ if (node == null)
+ break;
+ // Remove the port for the switch
+ Integer port = node.getPort(topologyElement.getSwitchPort());
+ if (port != null) {
+ node.removePort(topologyElement.getSwitchPort());
+ isModified = true;
+ }
+ break;
+ }
+ case ELEMENT_LINK: {
+ // Find the "from" switch
+ Node fromNode = getNode(topologyElement.getFromSwitch());
+ if (fromNode == null)
+ break;
+ // Remove the link originating from the "from" port
+ Node.Link link = fromNode.getLink(topologyElement.getFromPort());
+ if (link != null) {
+ fromNode.removeLink(topologyElement.getFromPort());
+ isModified = true;
+ }
+ break;
+ }
+ }
+
+ return isModified;
+ }
+
+ /**
+ * Get a node for a given Node ID.
*
* @param nodeId the Node ID to use.
* @return the corresponding Node if found, otherwise null.
@@ -92,6 +332,34 @@
}
/**
+ * Add a node for a given Node ID.
+ *
+ * @param nodeId the Node ID to use.
+ * @return the added Node.
+ */
+ Node addNode(long nodeId) {
+ Node node = new Node(nodeId);
+ nodesMap.put(nodeId, node);
+ return node;
+ }
+
+ /**
+ * Remove an existing node.
+ *
+ * @param node the Node to remove.
+ */
+ void removeNode(Node node) {
+ //
+ // Remove all ports one-by-one. This operation will also remove the
+ // incoming links originating from the neighbors.
+ //
+ for (Integer portId : node.ports().keySet())
+ node.removePort(portId);
+
+ nodesMap.remove(node.nodeId);
+ }
+
+ /**
* Read topology state from the database.
*
* @param dbHandler the Graph Database handler to use.
@@ -110,10 +378,8 @@
String nodeDpid = nodeVertex.getProperty("dpid").toString();
long nodeId = HexString.toLong(nodeDpid);
Node me = nodesMap.get(nodeId);
- if (me == null) {
- me = new Node(nodeId);
- nodesMap.put(nodeId, me);
- }
+ if (me == null)
+ me = addNode(nodeId);
//
// The local Port info
@@ -123,13 +389,12 @@
if (! myPortVertex.getProperty("state").toString().equals("ACTIVE"))
continue;
- short myPort = 0;
+ int myPort = 0;
Object obj = myPortVertex.getProperty("number");
if (obj instanceof Short) {
myPort = (Short)obj;
} else if (obj instanceof Integer) {
- Integer int_nodeId = (Integer)obj;
- myPort = int_nodeId.shortValue();
+ myPort = (Integer)obj;
}
//
@@ -140,13 +405,12 @@
if (! neighborPortVertex.getProperty("state").toString().equals("ACTIVE"))
continue;
- short neighborPort = 0;
+ int neighborPort = 0;
obj = neighborPortVertex.getProperty("number");
if (obj instanceof Short) {
neighborPort = (Short)obj;
} else if (obj instanceof Integer) {
- Integer int_nodeId = (Integer)obj;
- neighborPort = int_nodeId.shortValue();
+ neighborPort = (Integer)obj;
}
//
// The neighbor Switch info
@@ -160,11 +424,9 @@
String neighborDpid = neighborVertex.getProperty("dpid").toString();
long neighborId = HexString.toLong(neighborDpid);
Node neighbor = nodesMap.get(neighborId);
- if (neighbor == null) {
- neighbor = new Node(neighborId);
- nodesMap.put(neighborId, neighbor);
- }
- me.addNeighbor(neighbor, myPort, neighborPort);
+ if (neighbor == null)
+ neighbor = addNode(neighborId);
+ me.addLink(myPort, neighbor, neighborPort);
}
}
}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/topology/TopologyElement.java b/src/main/java/net/onrc/onos/ofcontroller/topology/TopologyElement.java
new file mode 100644
index 0000000..fe84654
--- /dev/null
+++ b/src/main/java/net/onrc/onos/ofcontroller/topology/TopologyElement.java
@@ -0,0 +1,198 @@
+package net.onrc.onos.ofcontroller.topology;
+
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * Class for storing information about a Topology Element: Switch, Port or
+ * Link.
+ */
+public class TopologyElement {
+ /**
+ * The Element Type.
+ */
+ public enum Type {
+ ELEMENT_UNKNOWN, // Unknown element
+ ELEMENT_SWITCH, // Network Switch
+ ELEMENT_PORT, // Switch Port
+ ELEMENT_LINK // Unidirectional Link between Switch Ports
+ }
+
+ private Type elementType; // The element type
+ private long fromSwitchDpid = 0; // The Switch DPID
+ private int fromSwitchPort = 0; // The Switch Port
+ 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.
+ */
+ public TopologyElement() {
+ elementType = Type.ELEMENT_UNKNOWN;
+ }
+
+ /**
+ * Constructor to create a Topology Element for a Switch.
+ *
+ * @param switchDpid the Switch DPID.
+ */
+ public TopologyElement(long switchDpid) {
+ this.elementType = Type.ELEMENT_SWITCH;
+ this.fromSwitchDpid = switchDpid;
+ }
+
+ /**
+ * Constructor to create a Topology Element for a Switch Port.
+ *
+ * @param switchDpid the Switch DPID.
+ * @param switchPort the Switch Port.
+ */
+ public TopologyElement(long switchDpid, int switchPort) {
+ this.elementType = Type.ELEMENT_PORT;
+ this.fromSwitchDpid = switchDpid;
+ this.fromSwitchPort = switchPort;
+ }
+
+ /**
+ * Constructor to create a Topology Element for an unidirectional Link
+ * between Switch Ports.
+ *
+ * @param fromSwitchDpid the Switch DPID the Link begins from.
+ * @param fromSwitchPort the Switch Port the Link begins from.
+ * @param toSwitchDpid the Switch DPID the Link ends to.
+ * @param toSwitchPort the Switch Port the Link ends to.
+ */
+ public TopologyElement(long fromSwitchDpid, int fromSwitchPort,
+ long toSwitchDpid, int toSwitchPort) {
+ this.elementType = Type.ELEMENT_LINK;
+ this.fromSwitchDpid = fromSwitchDpid;
+ this.fromSwitchPort = fromSwitchPort;
+ this.toSwitchDpid = toSwitchDpid;
+ this.toSwitchPort = toSwitchPort;
+ }
+
+ /**
+ * Get the Element type.
+ *
+ * @return the Element type.
+ */
+ public TopologyElement.Type getType() {
+ return elementType;
+ }
+
+ /**
+ * Get the Switch DPID.
+ *
+ * NOTE: Applies for Type.ELEMENT_SWITCH and Type.ELEMENT_PORT
+ *
+ * @return the Switch DPID.
+ */
+ public long getSwitch() {
+ return fromSwitchDpid;
+ }
+
+ /**
+ * 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
+ *
+ * @return the Switch Port.
+ */
+ public int getSwitchPort() {
+ return fromSwitchPort;
+ }
+
+ /**
+ * Get the Switch DPID the Link begins from.
+ *
+ * NOTE: Applies for Type.ELEMENT_LINK
+ */
+ public long getFromSwitch() {
+ return fromSwitchDpid;
+ }
+
+ /**
+ * Get the Switch Port the Link begins from.
+ *
+ * NOTE: Applies for Type.ELEMENT_LINK
+ */
+ public int getFromPort() {
+ return fromSwitchPort;
+ }
+
+ /**
+ * Get the Switch DPID the Link ends to.
+ *
+ * NOTE: Applies for Type.ELEMENT_LINK
+ */
+ public long getToSwitch() {
+ return toSwitchDpid;
+ }
+
+ /**
+ * Get the Switch Port the Link ends to.
+ *
+ * NOTE: Applies for Type.ELEMENT_LINK
+ */
+ public int getToPort() {
+ return toSwitchPort;
+ }
+
+ /**
+ * Get the Topology Element ID.
+ *
+ * The Topology Element ID has the following format:
+ * - Switch: "Switch=<HexLongDpid>"
+ * Example: "Switch=101"
+ * - Switch Port: "Port=<HexLongDpid>/<IntPortId>"
+ * Example: "Port=102/1"
+ * - Link: "Link=<FromHexLongDpid>/<FromIntPortId>/<ToHexLongDpid>/<ToIntPortId>"
+ * Example: "Link=101/2/103/4"
+ *
+ * NOTE: The Topology Element ID has no syntax meaning. It is used only to
+ * uniquely identify a topology element.
+ *
+ * @return the Topology Element ID.
+ */
+ public String elementId() {
+ switch (elementType) {
+ case ELEMENT_SWITCH:
+ return "Switch=" + Long.toHexString(fromSwitchDpid);
+ case ELEMENT_PORT:
+ return "Port=" +
+ Long.toHexString(fromSwitchDpid) + "/" + fromSwitchPort;
+ case ELEMENT_LINK:
+ return "Link=" +
+ Long.toHexString(fromSwitchDpid) + "/" + fromSwitchPort + "/" +
+ Long.toHexString(toSwitchDpid) + "/" + toSwitchPort;
+ }
+
+ assert(false);
+ return null;
+ }
+}
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 ccb64f8..ffe806a 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/topology/TopologyManager.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/topology/TopologyManager.java
@@ -15,6 +15,9 @@
import net.onrc.onos.graph.GraphDBOperation;
import net.onrc.onos.ofcontroller.floodlightlistener.INetworkGraphService;
import net.onrc.onos.ofcontroller.util.DataPath;
+import net.onrc.onos.ofcontroller.util.FlowEntry;
+import net.onrc.onos.ofcontroller.util.FlowPath;
+import net.onrc.onos.ofcontroller.util.Port;
import net.onrc.onos.ofcontroller.util.SwitchPort;
import org.slf4j.Logger;
@@ -202,6 +205,80 @@
}
/**
+ * Compute the network path for a Flow.
+ *
+ * @param topology the topology handler to use.
+ * @param flowPath the Flow to compute the network path for.
+ * @return the data path with the computed path if found, otherwise null.
+ */
+ public static DataPath computeNetworkPath(Topology topology,
+ FlowPath flowPath) {
+ //
+ // Compute the network path based on the desired Flow Path type
+ //
+ switch (flowPath.flowPathType()) {
+ case FP_TYPE_SHORTEST_PATH: {
+ SwitchPort src = flowPath.dataPath().srcPort();
+ SwitchPort dest = flowPath.dataPath().dstPort();
+ return ShortestPath.getTopologyShortestPath(topology, src, dest);
+ }
+ case FP_TYPE_EXPLICIT_PATH:
+ return flowPath.dataPath();
+ }
+
+ return null;
+ }
+
+ /**
+ * Test whether two Flow Entries represent same points in a data path.
+ *
+ * NOTE: Two Flow Entries represent same points in a data path if
+ * the Switch DPID, incoming port and outgoing port are same.
+ *
+ * NOTE: This method is specialized for shortest-path unicast paths,
+ * and probably should be moved somewhere else.
+ *
+ * @param oldFlowEntry the first Flow Entry to compare.
+ * @param newFlowEntry the second Flow Entry to compare.
+ * @return true if the two Flow Entries represent same points in a
+ * data path, otherwise false.
+ */
+ public static boolean isSameFlowEntryDataPath(FlowEntry oldFlowEntry,
+ FlowEntry newFlowEntry) {
+ // Test the DPID
+ if (oldFlowEntry.dpid().value() != newFlowEntry.dpid().value())
+ return false;
+
+ // Test the inPort
+ do {
+ Port oldPort = oldFlowEntry.inPort();
+ Port newPort = newFlowEntry.inPort();
+ if ((oldPort != null) && (newPort != null) &&
+ (oldPort.value() == newPort.value())) {
+ break;
+ }
+ if ((oldPort == null) && (newPort == null))
+ break;
+ return false; // inPort is different
+ } while (false);
+
+ // Test the outPort
+ do {
+ Port oldPort = oldFlowEntry.outPort();
+ Port newPort = newFlowEntry.outPort();
+ if ((oldPort != null) && (newPort != null) &&
+ (oldPort.value() == newPort.value())) {
+ break;
+ }
+ if ((oldPort == null) && (newPort == null))
+ break;
+ return false; // outPort is different
+ } while (false);
+
+ return true;
+ }
+
+ /**
* Get the shortest path from a source to a destination by
* using the pre-populated local topology state prepared
* by method @ref newDatabaseTopology().
diff --git a/src/main/java/net/onrc/onos/ofcontroller/util/EventEntry.java b/src/main/java/net/onrc/onos/ofcontroller/util/EventEntry.java
new file mode 100644
index 0000000..5b296e0
--- /dev/null
+++ b/src/main/java/net/onrc/onos/ofcontroller/util/EventEntry.java
@@ -0,0 +1,64 @@
+package net.onrc.onos.ofcontroller.util;
+
+/**
+ * Class for encapsulating events with event-related data entry.
+ */
+public class EventEntry<T> {
+ /**
+ * The event types.
+ */
+ public enum Type {
+ ENTRY_ADD, // Add or update an entry
+ ENTRY_REMOVE // Remove an entry
+ }
+
+ private Type eventType; // The event type
+ private T eventData; // The relevant event data entry
+
+ /**
+ * Constructor for a given event type and event-related data entry.
+ *
+ * @param eventType the event type.
+ * @param eventData the event data entry.
+ */
+ public EventEntry(EventEntry.Type eventType, T eventData) {
+ this.eventType = eventType;
+ this.eventData = eventData;
+ }
+
+ /**
+ * Test whether the event type is ENTRY_ADD.
+ *
+ * @return true if the event type is ENTRY_ADD, otherwise false.
+ */
+ public boolean isAdd() {
+ return (this.eventType == Type.ENTRY_ADD);
+ }
+
+ /**
+ * Test whether the event type is ENTRY_REMOVE.
+ *
+ * @return true if the event type is ENTRY_REMOVE, otherwise false.
+ */
+ public boolean isRemove() {
+ return (this.eventType == Type.ENTRY_REMOVE);
+ }
+
+ /**
+ * Get the event type.
+ *
+ * @return the event type.
+ */
+ public EventEntry.Type eventType() {
+ return this.eventType;
+ }
+
+ /**
+ * Get the event-related data entry.
+ *
+ * @return the event-related data entry.
+ */
+ public T eventData() {
+ return this.eventData;
+ }
+}
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());
}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/util/FlowEntrySwitchState.java b/src/main/java/net/onrc/onos/ofcontroller/util/FlowEntrySwitchState.java
index a69fdac..44439f2 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/util/FlowEntrySwitchState.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/util/FlowEntrySwitchState.java
@@ -4,9 +4,9 @@
* The Flow Entry state as set by the controller.
*/
public enum FlowEntrySwitchState {
- FE_SWITCH_UNKNOWN, // Initialization value: state unknown
- FE_SWITCH_NOT_UPDATED, // Switch not updated with this entry
- FE_SWITCH_UPDATE_IN_PROGRESS, // Switch update in progress
- FE_SWITCH_UPDATED, // Switch updated with this entry
- FE_SWITCH_UPDATE_FAILED // Error updating the switch with this entry
+ FE_SWITCH_UNKNOWN, // Initialization value: state unknown
+ FE_SWITCH_NOT_UPDATED, // Switch not updated with this entry
+ FE_SWITCH_UPDATE_IN_PROGRESS, // Switch update in progress
+ FE_SWITCH_UPDATED, // Switch updated with this entry
+ FE_SWITCH_UPDATE_FAILED // Error updating the switch with this entry
}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/util/FlowEntryUserState.java b/src/main/java/net/onrc/onos/ofcontroller/util/FlowEntryUserState.java
index e3b64f0..5ed8865 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/util/FlowEntryUserState.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/util/FlowEntryUserState.java
@@ -4,8 +4,8 @@
* The Flow Entry state as set by the user (via the ONOS API).
*/
public enum FlowEntryUserState {
- FE_USER_UNKNOWN, // Initialization value: state unknown
- FE_USER_ADD, // Flow entry that is added
- FE_USER_MODIFY, // Flow entry that is modified
- FE_USER_DELETE // Flow entry that is deleted
+ FE_USER_UNKNOWN, // Initialization value: state unknown
+ FE_USER_ADD, // Flow entry that is added
+ FE_USER_MODIFY, // Flow entry that is modified
+ FE_USER_DELETE // Flow entry that is deleted
}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/util/FlowPath.java b/src/main/java/net/onrc/onos/ofcontroller/util/FlowPath.java
index a56dbff..a720fc6 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/util/FlowPath.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/util/FlowPath.java
@@ -1,9 +1,10 @@
package net.onrc.onos.ofcontroller.util;
+import java.util.ArrayList;
+
import net.floodlightcontroller.util.MACAddress;
import net.onrc.onos.ofcontroller.core.INetMapTopologyObjects.IFlowEntry;
import net.onrc.onos.ofcontroller.core.INetMapTopologyObjects.IFlowPath;
-import net.onrc.onos.ofcontroller.util.FlowPathFlags;
import org.codehaus.jackson.annotate.JsonProperty;
@@ -13,6 +14,8 @@
public class FlowPath implements Comparable<FlowPath> {
private FlowId flowId; // The Flow ID
private CallerId installerId; // The Caller ID of the path installer
+ private FlowPathType flowPathType; // The Flow Path type
+ private FlowPathUserState flowPathUserState; // The Flow Path User state
private FlowPathFlags flowPathFlags; // The Flow Path flags
private DataPath dataPath; // The data path
private FlowEntryMatch flowEntryMatch; // Common Flow Entry Match for all
@@ -24,6 +27,8 @@
* Default constructor.
*/
public FlowPath() {
+ flowPathType = FlowPathType.FP_TYPE_UNKNOWN;
+ flowPathUserState = FlowPathUserState.FP_USER_UNKNOWN;
flowPathFlags = new FlowPathFlags();
dataPath = new DataPath();
flowEntryActions = new FlowEntryActions();
@@ -36,6 +41,8 @@
dataPath = new DataPath();
this.setFlowId(new FlowId(flowObj.getFlowId()));
this.setInstallerId(new CallerId(flowObj.getInstallerId()));
+ this.setFlowPathType(FlowPathType.valueOf(flowObj.getFlowPathType()));
+ this.setFlowPathUserState(FlowPathUserState.valueOf(flowObj.getFlowPathUserState()));
this.setFlowPathFlags(new FlowPathFlags(flowObj.getFlowPathFlags()));
this.dataPath().srcPort().setDpid(new Dpid(flowObj.getSrcSwitch()));
this.dataPath().srcPort().setPort(new Port(flowObj.getSrcPort()));
@@ -221,6 +228,42 @@
}
/**
+ * Get the flow path type.
+ *
+ * @return the flow path type.
+ */
+ @JsonProperty("flowPathType")
+ public FlowPathType flowPathType() { return flowPathType; }
+
+ /**
+ * Set the flow path type.
+ *
+ * @param flowPathType the flow path type to set.
+ */
+ @JsonProperty("flowPathType")
+ public void setFlowPathType(FlowPathType flowPathType) {
+ this.flowPathType = flowPathType;
+ }
+
+ /**
+ * Get the flow path user state.
+ *
+ * @return the flow path user state.
+ */
+ @JsonProperty("flowPathUserState")
+ public FlowPathUserState flowPathUserState() { return flowPathUserState; }
+
+ /**
+ * Set the flow path user state.
+ *
+ * @param flowPathUserState the flow path user state to set.
+ */
+ @JsonProperty("flowPathUserState")
+ public void setFlowPathUserState(FlowPathUserState flowPathUserState) {
+ this.flowPathUserState = flowPathUserState;
+ }
+
+ /**
* Get the flow path flags.
*
* @return the flow path flags.
@@ -257,6 +300,15 @@
}
/**
+ * Get the data path flow entries.
+ *
+ * @return the data path flow entries.
+ */
+ public ArrayList<FlowEntry> flowEntries() {
+ return this.dataPath.flowEntries();
+ }
+
+ /**
* Get the flow path's match conditions common for all Flow Entries.
*
* @return the flow path's match conditions common for all Flow Entries.
@@ -300,8 +352,9 @@
* Convert the flow path to a string.
*
* The string has the following form:
- * [flowId=XXX installerId=XXX flowPathFlags=XXX dataPath=XXX
- * flowEntryMatch=XXX flowEntryActions=XXX]
+ * [flowId=XXX installerId=XXX flowPathType = XXX flowPathUserState = XXX
+ * flowPathFlags=XXX dataPath=XXX flowEntryMatch=XXX
+ * flowEntryActions=XXX]
*
* @return the flow path as a string.
*/
@@ -309,6 +362,8 @@
public String toString() {
String ret = "[flowId=" + this.flowId.toString();
ret += " installerId=" + this.installerId.toString();
+ ret += " flowPathType=" + this.flowPathType;
+ ret += " flowPathUserState=" + this.flowPathUserState;
ret += " flowPathFlags=" + this.flowPathFlags.toString();
if (dataPath != null)
ret += " dataPath=" + this.dataPath.toString();
diff --git a/src/main/java/net/onrc/onos/ofcontroller/util/FlowPathType.java b/src/main/java/net/onrc/onos/ofcontroller/util/FlowPathType.java
new file mode 100644
index 0000000..87f2d98
--- /dev/null
+++ b/src/main/java/net/onrc/onos/ofcontroller/util/FlowPathType.java
@@ -0,0 +1,10 @@
+package net.onrc.onos.ofcontroller.util;
+
+/**
+ * The Flow Path types.
+ */
+public enum FlowPathType {
+ FP_TYPE_UNKNOWN, // Initialization value: state unknown
+ FP_TYPE_SHORTEST_PATH, // Shortest path flow
+ FP_TYPE_EXPLICIT_PATH // Flow path with explicit flow entries
+}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/util/FlowPathUserState.java b/src/main/java/net/onrc/onos/ofcontroller/util/FlowPathUserState.java
new file mode 100644
index 0000000..96b6345
--- /dev/null
+++ b/src/main/java/net/onrc/onos/ofcontroller/util/FlowPathUserState.java
@@ -0,0 +1,11 @@
+package net.onrc.onos.ofcontroller.util;
+
+/**
+ * The Flow Path state as set by the user (via the ONOS API).
+ */
+public enum FlowPathUserState {
+ FP_USER_UNKNOWN, // Initialization value: state unknown
+ FP_USER_ADD, // Flow path that is added
+ FP_USER_MODIFY, // Flow path that is modified
+ FP_USER_DELETE // Flow path that is deleted
+}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/util/Switch.java b/src/main/java/net/onrc/onos/ofcontroller/util/Switch.java
index c989732..f7df223 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/util/Switch.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/util/Switch.java
@@ -7,6 +7,9 @@
* NOTE: Currently this class is (almost) not used.
*/
public class Switch {
+ /**
+ * The Switch state.
+ */
public enum SwitchState {
INACTIVE,
ACTIVE,
diff --git a/src/main/java/net/onrc/onos/ofcontroller/util/serializers/KryoFactory.java b/src/main/java/net/onrc/onos/ofcontroller/util/serializers/KryoFactory.java
new file mode 100644
index 0000000..eeb307f
--- /dev/null
+++ b/src/main/java/net/onrc/onos/ofcontroller/util/serializers/KryoFactory.java
@@ -0,0 +1,135 @@
+package net.onrc.onos.ofcontroller.util.serializers;
+
+import java.util.ArrayList;
+import java.util.TreeMap;
+
+import com.esotericsoftware.kryo2.Kryo;
+
+import net.floodlightcontroller.util.MACAddress;
+
+import net.onrc.onos.ofcontroller.util.*;
+import net.onrc.onos.ofcontroller.topology.TopologyElement;
+
+/**
+ * Class factory for allocating Kryo instances for
+ * serialization/deserialization of classes.
+ */
+public class KryoFactory {
+ private ArrayList<Kryo> kryoList = new ArrayList<Kryo>();
+
+ /**
+ * Default constructor.
+ */
+ public KryoFactory() {
+ Kryo kryo;
+ // Preallocate
+ for (int i = 0; i < 100; i++) {
+ kryo = newKryoImpl();
+ kryoList.add(kryo);
+ }
+ }
+
+ /**
+ * Create and initialize a new Kryo object.
+ *
+ * @return the created Kryo object.
+ */
+ public Kryo newKryo() {
+ return newDeleteKryo(null);
+ }
+
+ /**
+ * Delete an existing Kryo object.
+ *
+ * @param deleteKryo the object to delete.
+ */
+ public void deleteKryo(Kryo deleteKryo) {
+ newDeleteKryo(deleteKryo);
+ }
+
+ /**
+ * Create or delete a Kryo object.
+ *
+ * @param deleteKryo if null, then allocate and return a new object,
+ * otherwise delete the provided object.
+ * @return a new Kryo object if needed, otherwise null.
+ */
+ synchronized private Kryo newDeleteKryo(Kryo deleteKryo) {
+ if (deleteKryo != null) {
+ // Delete an entry by moving it back to the buffer
+ kryoList.add(deleteKryo);
+ return null;
+ } else {
+ Kryo kryo = null;
+ if (kryoList.isEmpty()) {
+ // Preallocate
+ for (int i = 0; i < 100; i++) {
+ kryo = newKryoImpl();
+ kryoList.add(kryo);
+ }
+ }
+
+ kryo = kryoList.remove(kryoList.size() - 1);
+ return kryo;
+ }
+ }
+
+ /**
+ * Create and initialize a new Kryo object.
+ *
+ * @return the created Kryo object.
+ */
+ private Kryo newKryoImpl() {
+ Kryo kryo = new Kryo();
+ kryo.setRegistrationRequired(true);
+ // kryo.setReferences(false);
+ //
+ kryo.register(ArrayList.class);
+
+ // FlowPath and related classes
+ kryo.register(CallerId.class);
+ kryo.register(DataPath.class);
+ kryo.register(DataPathEndpoints.class);
+ kryo.register(Dpid.class);
+ kryo.register(FlowEntryAction.class);
+ kryo.register(FlowEntryAction.ActionEnqueue.class);
+ kryo.register(FlowEntryAction.ActionOutput.class);
+ kryo.register(FlowEntryAction.ActionSetEthernetAddr.class);
+ kryo.register(FlowEntryAction.ActionSetIpToS.class);
+ kryo.register(FlowEntryAction.ActionSetIPv4Addr.class);
+ kryo.register(FlowEntryAction.ActionSetTcpUdpPort.class);
+ kryo.register(FlowEntryAction.ActionSetVlanId.class);
+ kryo.register(FlowEntryAction.ActionSetVlanPriority.class);
+ kryo.register(FlowEntryAction.ActionStripVlan.class);
+ kryo.register(FlowEntryAction.ActionValues.class);
+ kryo.register(FlowEntryActions.class);
+ kryo.register(FlowEntryErrorState.class);
+ kryo.register(FlowEntryId.class);
+ kryo.register(FlowEntry.class);
+ kryo.register(FlowEntryMatch.class);
+ kryo.register(FlowEntryMatch.Field.class);
+ kryo.register(FlowEntrySwitchState.class);
+ kryo.register(FlowEntryUserState.class);
+ kryo.register(FlowId.class);
+ kryo.register(FlowPath.class);
+ kryo.register(FlowPathFlags.class);
+ kryo.register(FlowPathType.class);
+ kryo.register(FlowPathUserState.class);
+ kryo.register(IPv4.class);
+ kryo.register(IPv4Net.class);
+ kryo.register(IPv6.class);
+ kryo.register(IPv6Net.class);
+ kryo.register(byte[].class);
+ kryo.register(MACAddress.class);
+ kryo.register(Port.class);
+ kryo.register(Switch.class);
+ kryo.register(SwitchPort.class);
+
+ // Topology-related classes
+ kryo.register(TopologyElement.class);
+ kryo.register(TopologyElement.Type.class);
+ kryo.register(TreeMap.class);
+
+ return kryo;
+ }
+}
diff --git a/src/test/java/net/onrc/onos/ofcontroller/core/INetMapTopologyObjectsIFlowPathTest.java b/src/test/java/net/onrc/onos/ofcontroller/core/INetMapTopologyObjectsIFlowPathTest.java
index bb0dbbf..9a1e34a 100644
--- a/src/test/java/net/onrc/onos/ofcontroller/core/INetMapTopologyObjectsIFlowPathTest.java
+++ b/src/test/java/net/onrc/onos/ofcontroller/core/INetMapTopologyObjectsIFlowPathTest.java
@@ -104,6 +104,42 @@
/**
* Desc:
+ * Test method for get and set FlowPathType method.
+ * Condition:
+ * N/A
+ * Expect:
+ * 1. Should set the Flow Path Type.
+ * 2. Should get the Flow Path Type.
+ */
+ @Test
+ public void testSetGetFlowPathType() {
+ String flowId = "xx";
+ String flowPathType = "FP_TYPE_SHORTEST_PATH";
+ flowPath.setFlowId(flowId);
+ flowPath.setFlowPathType(flowPathType);
+ assertEquals(flowPath.getFlowPathType(), flowPathType);
+ }
+
+ /**
+ * Desc:
+ * Test method for get and set FlowPathUserState method.
+ * Condition:
+ * N/A
+ * Expect:
+ * 1. Should set the Flow Path User State.
+ * 2. Should get the Flow Path User State.
+ */
+ @Test
+ public void testSetGetFlowPathUserState() {
+ String flowId = "xx";
+ String flowPathUserState = "FP_USER_ADD";
+ flowPath.setFlowId(flowId);
+ flowPath.setFlowPathUserState(flowPathUserState);
+ assertEquals(flowPath.getFlowPathUserState(), flowPathUserState);
+ }
+
+ /**
+ * Desc:
* Test method for get and set FlowPathFlags method.
* Condition:
* N/A
@@ -457,24 +493,6 @@
/**
* Desc:
- * Test method for set and get UserState.
- * Condition:
- * N/A
- * Expect:
- * 1. Should set UserState.
- * 2. Should get UserState.
- */
- @Test
- public void testSetGetUserState() {
- String flowId = "xx";
- String userStatus = "Good";
- flowPath.setFlowId(flowId);
- flowPath.setUserState(userStatus);
- assertEquals(flowPath.getUserState(), userStatus);
- }
-
- /**
- * Desc:
* Test method for get Switches.
* Condition:
* N/A
diff --git a/src/test/java/net/onrc/onos/ofcontroller/core/internal/TestableGraphDBOperation.java b/src/test/java/net/onrc/onos/ofcontroller/core/internal/TestableGraphDBOperation.java
index 385b49d..dfe6ccf 100644
--- a/src/test/java/net/onrc/onos/ofcontroller/core/internal/TestableGraphDBOperation.java
+++ b/src/test/java/net/onrc/onos/ofcontroller/core/internal/TestableGraphDBOperation.java
@@ -415,8 +415,10 @@
public static class TestFlowPath implements IFlowPath {
private String state,type,flowId,installerId,srcSw,dstSw;
+ private String flowPathType;
+ private String flowPathUserState;
private Long flowPathFlags;
- private String dataPathSummary,userState;
+ private String dataPathSummary;
private Short srcPort,dstPort;
private String matchSrcMac,matchDstMac;
private Short matchEthernetFrameType;
@@ -431,8 +433,10 @@
private List<ISwitchObject> switches;
private String stateToUpdate,typeToUpdate,flowIdToUpdate,installerIdToUpdate,srcSwToUpdate,dstSwToUpdate;
+ private String flowPathTypeToUpdate;
+ private String flowPathUserStateToUpdate;
private Long flowPathFlagsToUpdate;
- private String dataPathSummaryToUpdate,userStateToUpdate;
+ private String dataPathSummaryToUpdate;
private Short srcPortToUpdate,dstPortToUpdate;
private String matchSrcMacToUpdate,matchDstMacToUpdate;
private Short matchEthernetFrameTypeToUpdate;
@@ -469,11 +473,12 @@
if(typeToUpdate != null) { type = typeToUpdate; }
if(flowIdToUpdate != null) { flowId = flowIdToUpdate; }
if(installerIdToUpdate != null) { installerId = installerIdToUpdate; }
+ if(flowPathTypeToUpdate != null) { flowPathType = flowPathTypeToUpdate; }
+ if(flowPathUserStateToUpdate != null) { flowPathUserState = flowPathUserStateToUpdate; }
if(flowPathFlagsToUpdate != null) { flowPathFlags = flowPathFlagsToUpdate; }
if(srcSwToUpdate != null) { srcSw = srcSwToUpdate; }
if(dstSwToUpdate != null) { dstSw = dstSwToUpdate; }
if(dataPathSummaryToUpdate != null) { dataPathSummary = dataPathSummaryToUpdate; }
- if(userStateToUpdate != null) { userState = userStateToUpdate; }
if(srcPortToUpdate != null) { srcPort = srcPortToUpdate; }
if(dstPortToUpdate != null) { dstPort = dstPortToUpdate; }
if(matchSrcMacToUpdate != null) { matchSrcMac = matchSrcMacToUpdate; }
@@ -499,8 +504,10 @@
flowsToRemove.clear();
stateToUpdate = typeToUpdate = flowIdToUpdate = installerIdToUpdate = null;
+ flowPathTypeToUpdate = null;
+ flowPathUserStateToUpdate = null;
flowPathFlagsToUpdate = null;
- srcSwToUpdate = dstSwToUpdate = dataPathSummaryToUpdate = userStateToUpdate = null;
+ srcSwToUpdate = dstSwToUpdate = dataPathSummaryToUpdate = null;
srcPortToUpdate = dstPortToUpdate = null;
matchSrcMacToUpdate = matchDstMacToUpdate = null;
matchEthernetFrameTypeToUpdate = null;
@@ -517,11 +524,12 @@
public void setTypeForTest(String type) { this.type = type; }
public void setFlowIdForTest(String flowId) { this.flowId = flowId; }
public void setInstallerIdForTest(String installerId) { this.installerId = installerId; }
+ public void setFlowPathTypeForTest(String flowPathType) { this.flowPathType = flowPathType; }
+ public void setFlowPathUserStateForTest(String flowPathUserState) { this.flowPathUserState = flowPathUserState; }
public void setFlowPathFlagsForTest(Long flowPathFlags) { this.flowPathFlags = flowPathFlags; }
public void setSrcSwForTest(String srcSw) { this.srcSw = srcSw; }
public void setDstSwForTest(String dstSw) { this.dstSw = dstSw; }
public void setDataPathSummaryForTest(String dataPathSummary) { this.dataPathSummary = dataPathSummary; }
- public void setUserStateForTest(String userState) { this.userState = userState; }
public void setSrcPortForTest(Short srcPort) { this.srcPort = srcPort; }
public void setDstPortForTest(Short dstPort) { this.dstPort = dstPort; }
public void setMatchSrcMacForTest(String matchSrcMac) { this.matchSrcMac = matchSrcMac; }
@@ -570,6 +578,18 @@
public void setInstallerId(String installerId) { installerIdToUpdate = installerId; }
@Override
+ public String getFlowPathType() { return flowPathType; }
+
+ @Override
+ public void setFlowPathType(String flowPathType) { flowPathTypeToUpdate = flowPathType; }
+
+ @Override
+ public String getFlowPathUserState() { return flowPathUserState; }
+
+ @Override
+ public void setFlowPathUserState(String flowPathUserState) { flowPathUserStateToUpdate = flowPathUserState; }
+
+ @Override
public Long getFlowPathFlags() { return flowPathFlags; }
@Override
@@ -706,12 +726,6 @@
@Override
public Iterable<ISwitchObject> getSwitches() { return switches; }
-
- @Override
- public String getUserState() { return userState; }
-
- @Override
- public void setUserState(String userState) { userStateToUpdate = userState; }
}
public static class TestFlowEntry implements IFlowEntry {
diff --git a/src/test/java/net/onrc/onos/ofcontroller/flowmanager/FlowManagerTest.java b/src/test/java/net/onrc/onos/ofcontroller/flowmanager/FlowManagerTest.java
index 8a8779b..7fd0f67 100644
--- a/src/test/java/net/onrc/onos/ofcontroller/flowmanager/FlowManagerTest.java
+++ b/src/test/java/net/onrc/onos/ofcontroller/flowmanager/FlowManagerTest.java
@@ -5,6 +5,7 @@
import static org.easymock.EasyMock.cmpEq;
import static org.powermock.api.easymock.PowerMock.*;
+import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
@@ -87,11 +88,14 @@
}
private IFlowPath createIFlowPathMock(long flowId, String installerID,
- long flowPathFlags,
- long srcDpid, int srcPort, long dstDpid, int dstPort) {
+ String flowPathType, String flowPathUserState,
+ long flowPathFlags, long srcDpid, int srcPort,
+ long dstDpid, int dstPort) {
IFlowPath iFlowPath = createNiceMock(IFlowPath.class);
expect(iFlowPath.getFlowId()).andReturn(new FlowId(flowId).toString()).anyTimes();
expect(iFlowPath.getInstallerId()).andReturn(installerID).anyTimes();
+ expect(iFlowPath.getFlowPathType()).andReturn(flowPathType).anyTimes();
+ expect(iFlowPath.getFlowPathUserState()).andReturn(flowPathUserState).anyTimes();
expect(iFlowPath.getFlowPathFlags()).andReturn(new Long(flowPathFlags)).anyTimes();
expect(iFlowPath.getSrcSwitch()).andReturn(new Dpid(srcDpid).toString()).anyTimes();
expect(iFlowPath.getSrcPort()).andReturn(new Short((short)srcPort)).anyTimes();
@@ -101,13 +105,16 @@
}
private FlowPath createTestFlowPath(long flowId, String installerId,
+ String flowPathType, String flowPathUserState,
final long flowPathFlags,
final long srcDpid, final int srcPort,
- final long dstDpid, final int dstPort
+ final long dstDpid, final int dstPort
) {
FlowPath flowPath = new FlowPath();
flowPath.setFlowId(new FlowId(flowId));
flowPath.setInstallerId(new CallerId(installerId));
+ flowPath.setFlowPathType(FlowPathType.valueOf(flowPathType));
+ flowPath.setFlowPathUserState(FlowPathUserState.valueOf(flowPathUserState));
flowPath.setFlowPathFlags(new FlowPathFlags(flowPathFlags));
flowPath.setDataPath(new DataPath() {{
setSrcPort(new SwitchPort(new Dpid(srcDpid), new Port((short)srcPort)));
@@ -118,9 +125,9 @@
}
private ArrayList<FlowPath> createTestFlowPaths() {
- FlowPath flowPath1 = createTestFlowPath(1, "foo caller id", 0, 1, 1, 2, 2);
- FlowPath flowPath2 = createTestFlowPath(2, "caller id", 0, 1, 1, 2, 2);
- FlowPath flowPath3 = createTestFlowPath(3, "caller id", 0, 1, 5, 2, 2);
+ FlowPath flowPath1 = createTestFlowPath(1, "foo caller id", "FP_TYPE_SHORTEST_PATH", "FP_USER_ADD", 0, 1, 1, 2, 2);
+ FlowPath flowPath2 = createTestFlowPath(2, "caller id", "FP_TYPE_SHORTEST_PATH", "FP_USER_ADD", 0, 1, 1, 2, 2);
+ FlowPath flowPath3 = createTestFlowPath(3, "caller id", "FP_TYPE_SHORTEST_PATH", "FP_USER_ADD", 0, 1, 5, 2, 2);
ArrayList<FlowPath> flowPaths = new ArrayList<FlowPath>();
flowPaths.add(flowPath1);
@@ -156,7 +163,7 @@
replayAll();
fm.init(context);
- Boolean result = fm.addFlow(flowPath, flowId, "");
+ Boolean result = fm.addFlow(flowPath, flowId);
// verify the test
verifyAll();
@@ -164,7 +171,7 @@
}
/**
- * Test method for {@link FlowManager#addFlow(FlowPath, FlowId, String)}.
+ * Test method for {@link FlowManager#addFlow(FlowPath, FlowId)}.
* @throws Exception
*/
@Test
@@ -193,6 +200,8 @@
FlowPath flowPath = new FlowPath();
flowPath.setFlowId(new FlowId(0x100));
flowPath.setInstallerId(new CallerId("installer id"));
+ flowPath.setFlowPathType(FlowPathType.valueOf("FP_TYPE_SHORTEST_PATH"));
+ flowPath.setFlowPathUserState(FlowPathUserState.valueOf("FP_USER_ADD"));
flowPath.setFlowPathFlags(new FlowPathFlags(0));
flowPath.setDataPath(dataPath);
flowPath.setFlowEntryMatch(match);
@@ -204,13 +213,14 @@
createdFlowPath.setFlowId("0x100");
createdFlowPath.setType("flow");
createdFlowPath.setInstallerId("installer id");
+ createdFlowPath.setFlowPathType("FP_TYPE_SHORTEST_PATH");
+ createdFlowPath.setFlowPathUserState("FP_USER_ADD");
createdFlowPath.setFlowPathFlags(new Long((long)0));
createdFlowPath.setSrcSwitch("00:00:00:00:00:00:12:34");
createdFlowPath.setSrcPort(new Short((short)1));
createdFlowPath.setDstSwitch("00:00:00:00:00:00:56:78");
createdFlowPath.setDstPort(new Short((short)2));
createdFlowPath.setDataPathSummary("data path summary");
- createdFlowPath.setUserState("FE_USER_ADD");
expectPrivate(fm, addFlowEntry, createdFlowPath, flowEntry1)
.andReturn(createdFlowEntry1);
@@ -223,7 +233,7 @@
replayAll();
fm.init(context);
- Boolean result = fm.addFlow(flowPath, new FlowId(0x100), "data path summary");
+ Boolean result = fm.addFlow(flowPath, new FlowId(0x100));
// verify the test
verifyAll();
@@ -352,7 +362,7 @@
public final void testGetFlowSuccessNormally() throws Exception {
// instantiate required objects
FlowManager fm = new FlowManager();
- IFlowPath iFlowPath = createIFlowPathMock(1, "caller id", 0, 1, 1, 2, 2);
+ IFlowPath iFlowPath = createIFlowPathMock(1, "caller id", "FP_TYPE_SHORTEST_PATH", "FP_USER_ADD", 0, 1, 1, 2, 2);
// setup expectations
expectInitWithContext();
@@ -366,11 +376,15 @@
fm.init(context);
FlowPath flowPath = fm.getFlow(new FlowId(1));
String installerId = flowPath.installerId().toString();
+ String flowPathType = flowPath.flowPathType().toString();
+ String flowPathUserState = flowPath.flowPathUserState().toString();
long flowPathFlags = flowPath.flowPathFlags().flags();
//verify the test
verifyAll();
assertEquals("caller id", installerId);
+ assertEquals("FP_TYPE_SHORTEST_PATH", flowPathType);
+ assertEquals("FP_USER_ADD", flowPathUserState);
assertEquals(0L, flowPathFlags);
}
@@ -453,9 +467,9 @@
final String getAllFlowsWithoutFlowEntries = "getAllFlowsWithoutFlowEntries";
// create mock objects
FlowManager fm = createPartialMockAndInvokeDefaultConstructor(FlowManager.class, getAllFlowsWithoutFlowEntries);
- IFlowPath flowPath1 = createIFlowPathMock(1, "", 0, 1, 2, 3, 4);
- IFlowPath flowPath2 = createIFlowPathMock(5, "", 0, 2, 3, 4, 5);
- IFlowPath flowPath3 = createIFlowPathMock(10, "", 0, 3, 4, 5, 6);
+ IFlowPath flowPath1 = createIFlowPathMock(1, "", "FP_TYPE_SHORTEST_PATH", "FP_USER_ADD", 0, 1, 2, 3, 4);
+ IFlowPath flowPath2 = createIFlowPathMock(5, "", "FP_TYPE_SHORTEST_PATH", "FP_USER_ADD", 0, 2, 3, 4, 5);
+ IFlowPath flowPath3 = createIFlowPathMock(10, "", "FP_TYPE_SHORTEST_PATH", "FP_USER_ADD", 0, 3, 4, 5, 6);
// instantiate required objects
ArrayList<IFlowPath> flows = new ArrayList<IFlowPath>();
@@ -488,8 +502,8 @@
@Test
public final void testGetAllFlowsSuccessNormally() throws Exception {
// create mock objects
- IFlowPath iFlowPath1 = createIFlowPathMock(1, "caller id", 0, 1, 1, 2, 2);
- IFlowPath iFlowPath2 = createIFlowPathMock(2, "caller id", 0, 2, 5, 3, 5);
+ IFlowPath iFlowPath1 = createIFlowPathMock(1, "caller id", "FP_TYPE_SHORTEST_PATH", "FP_USER_ADD", 0, 1, 1, 2, 2);
+ IFlowPath iFlowPath2 = createIFlowPathMock(2, "caller id", "FP_TYPE_SHORTEST_PATH", "FP_USER_ADD", 0, 2, 5, 3, 5);
// instantiate required objects
ArrayList<IFlowPath> flowPaths = new ArrayList<IFlowPath>();
@@ -540,6 +554,8 @@
FlowPath paramFlow = new FlowPath();
paramFlow.setFlowId(new FlowId(100));
paramFlow.setInstallerId(new CallerId("installer id"));
+ paramFlow.setFlowPathType(FlowPathType.valueOf("FP_TYPE_SHORTEST_PATH"));
+ paramFlow.setFlowPathUserState(FlowPathUserState.valueOf("FP_USER_ADD"));
paramFlow.setFlowPathFlags(new FlowPathFlags(0));
paramFlow.setDataPath(dataPath);
paramFlow.setFlowEntryMatch(match);
@@ -555,6 +571,8 @@
FlowPath flowPath = (FlowPath)EasyMock.getCurrentArguments()[0];
assertEquals(flowPath.flowId().value(), 100);
assertEquals(flowPath.installerId().toString(), "installer id");
+ assertEquals(flowPath.flowPathType().toString(), "PF_TYPE_SHORTEST_PATH");
+ assertEquals(flowPath.flowPathUserState().toString(), "PF_USER_STATE");
assertEquals(flowPath.flowPathFlags().flags(), 0);
assertEquals(flowPath.dataPath().srcPort().toString(),
new SwitchPort(new Dpid(1), new Port((short)3)).toString());
@@ -576,6 +594,8 @@
verifyAll();
assertEquals(paramFlow.flowId().value(), resultFlow.flowId().value());
assertEquals(paramFlow.installerId().toString(), resultFlow.installerId().toString());
+ assertEquals(paramFlow.flowPathType().toString(), resultFlow.flowPathType().toString());
+ assertEquals(paramFlow.flowPathUserState().toString(), resultFlow.flowPathUserState().toString());
assertEquals(paramFlow.flowPathFlags().flags(), resultFlow.flowPathFlags().flags());
assertEquals(paramFlow.dataPath().toString(), resultFlow.dataPath().toString());
assertEquals(paramFlow.flowEntryMatch().toString(), resultFlow.flowEntryMatch().toString());
@@ -774,7 +794,7 @@
@Test
public final void testClearFlowSuccessNormally() throws Exception {
// create mock objects
- IFlowPath flowPath = createIFlowPathMock(123, "id", 0, 1, 2, 3, 4);
+ IFlowPath flowPath = createIFlowPathMock(123, "id", "FP_TYPE_SHORTEST_PATH", "FP_USER_ADD", 0, 1, 2, 3, 4);
IFlowEntry flowEntry1 = createMock(IFlowEntry.class);
IFlowEntry flowEntry2 = createMock(IFlowEntry.class);
IFlowEntry flowEntry3 = createMock(IFlowEntry.class);
@@ -817,8 +837,8 @@
@Test
public final void testGetAllFlowsWithoutFlowEntriesSuccessNormally() throws Exception {
// create mock objects
- IFlowPath iFlowPath1 = createIFlowPathMock(1, "caller id", 0, 1, 1, 2, 2);
- IFlowPath iFlowPath2 = createIFlowPathMock(2, "caller id", 0, 2, 5, 3, 5);
+ IFlowPath iFlowPath1 = createIFlowPathMock(1, "caller id", "FP_TYPE_SHORTEST_PATH", "FP_USER_ADD", 0, 1, 1, 2, 2);
+ IFlowPath iFlowPath2 = createIFlowPathMock(2, "caller id", "FP_TYPE_SHORTEST_PATH", "FP_USER_ADD", 0, 2, 5, 3, 5);
// instantiate required objects
ArrayList<IFlowPath> flowPaths = new ArrayList<IFlowPath>();
@@ -854,7 +874,7 @@
final String addFlowEntry = "addFlowEntry";
// create mock objects
- IFlowPath iFlowPath1 = createIFlowPathMock(1, "caller id", 0, 1, 1, 2, 2);
+ IFlowPath iFlowPath1 = createIFlowPathMock(1, "caller id", "FP_TYPE_SHORTEST_PATH", "FP_USER_ADD", 0, 1, 1, 2, 2);
IFlowEntry iFlowEntry1 = createMock(IFlowEntry.class);
IFlowEntry iFlowEntry2 = createMock(IFlowEntry.class);
FlowManager fm = createPartialMockAndInvokeDefaultConstructor(FlowManager.class, addFlowEntry);
@@ -904,7 +924,15 @@
replayAll();
fm.init(context);
- Boolean result = fm.reconcileFlow(iFlowPath1, dataPath);
+ // Use reflection to test the private method
+ // Boolean result = fm.reconcileFlow(iFlowPath1, dataPath);
+ Class fmClass = FlowManager.class;
+ Method method = fmClass.getDeclaredMethod(
+ "reconcileFlow",
+ new Class[] { IFlowPath.class, DataPath.class });
+ method.setAccessible(true);
+ Boolean result = (Boolean)method.invoke(fm,
+ new Object[] { iFlowPath1, dataPath });
// verify the test
verifyAll();
@@ -920,7 +948,7 @@
public final void testInstallFlowEntryWithIFlowPathSuccessNormally() throws Exception {
// create mock object
IOFSwitch iofSwitch = createNiceMock(IOFSwitch.class);
- IFlowPath iFlowPath = createIFlowPathMock(1, "id", 0, 1, 2, 3, 4);
+ IFlowPath iFlowPath = createIFlowPathMock(1, "id", "FP_TYPE_SHORTEST_PATH", "FP_USER_ADD", 0, 1, 2, 3, 4);
IFlowEntry iFlowEntry = createMock(IFlowEntry.class);
BasicFactory basicFactory = createMock(BasicFactory.class);
@@ -958,7 +986,16 @@
replayAll();
fm.init(context);
- Boolean result = fm.installFlowEntry(iofSwitch, iFlowPath, iFlowEntry);
+ // Use reflection to test the private method
+ // Boolean result = fm.installFlowEntry(iofSwitch, iFlowPath, iFlowEntry);
+ Class fmClass = FlowManager.class;
+ Method method = fmClass.getDeclaredMethod(
+ "installFlowEntry",
+ new Class[] { IOFSwitch.class, IFlowPath.class, IFlowEntry.class });
+ method.setAccessible(true);
+ Boolean result = (Boolean)method.invoke(fm,
+ new Object[] { iofSwitch, iFlowPath, iFlowEntry });
+
// verify the test
verifyAll();
diff --git a/src/test/java/net/onrc/onos/ofcontroller/util/FlowEntryTest.java b/src/test/java/net/onrc/onos/ofcontroller/util/FlowEntryTest.java
index 1d193c4..fc17178 100644
--- a/src/test/java/net/onrc/onos/ofcontroller/util/FlowEntryTest.java
+++ b/src/test/java/net/onrc/onos/ofcontroller/util/FlowEntryTest.java
@@ -144,8 +144,25 @@
}
@Test
- public void testGetFlowId(){
- assertEquals("flowId", flowId, entry.getFlowId() );
+ public void testFlowId(){
+ assertEquals("flowId", flowId, entry.flowId() );
+ }
+
+ @Test
+ public void testIsValidFlowId(){
+ FlowEntry e = new FlowEntry();
+
+ // Test a Flow Entry with empty Flow ID
+ assertEquals("isValidFlowId", false, e.isValidFlowId() );
+
+ // Test a Flow Entry with invalid Flow ID
+ e.setFlowId(new FlowId());
+ assertEquals("isValidFlowId", false, e.isValidFlowId() );
+
+ // Test a Flow Entry with valid Flow ID
+ e.setFlowId(new FlowId(0x1));
+ assertEquals("isValidFlowId", true, e.isValidFlowId() );
+ assertEquals("isValidFlowId", true, entry.isValidFlowId() );
}
@Test
@@ -154,6 +171,23 @@
}
@Test
+ public void testIsValidFlowEntryId(){
+ FlowEntry e = new FlowEntry();
+
+ // Test a Flow Entry with empty Flow Entry ID
+ assertEquals("isValidFlowEntryId", false, e.isValidFlowEntryId() );
+
+ // Test a Flow Entry with invalid Flow Entry ID
+ e.setFlowEntryId(new FlowEntryId());
+ assertEquals("isValidFlowEntryId", false, e.isValidFlowEntryId() );
+
+ // Test a Flow Entry with valid Flow Entry ID
+ e.setFlowEntryId(new FlowEntryId(0x1));
+ assertEquals("isValidFlowEntryId", true, e.isValidFlowEntryId() );
+ assertEquals("isValidFlowEntryId", true, entry.isValidFlowEntryId() );
+ }
+
+ @Test
public void testFlowEntryMatch(){
assertEquals("flowEntryMatch", match, entry.flowEntryMatch() );
}
@@ -203,8 +237,8 @@
@Test
public void testToString(){
FlowEntry def = new FlowEntry();
- assertEquals( def.toString(), "[ flowEntryActions=[] flowEntryUserState=FE_USER_UNKNOWN flowEntrySwitchState=FE_SWITCH_UNKNOWN]" );
- assertEquals( entry.toString(), "[flowEntryId=0x5678 flowEntryMatch=[inPort=1 srcMac=01:02:03:04:05:06 dstMac=06:05:04:03:02:01 ethernetFrameType=2 vlanId=3 vlanPriority=4 srcIPv4Net=127.0.0.1/32 dstIPv4Net=127.0.0.2/32 ipProto=5 ipToS=6 srcTcpUdpPort=7 dstTcpUdpPort=8] flowEntryActions=[[type=ACTION_OUTPUT action=[port=9 maxLen=0]];[type=ACTION_OUTPUT action=[port=-3 maxLen=0]];[type=ACTION_SET_VLAN_VID action=[vlanId=3]];[type=ACTION_SET_VLAN_PCP action=[vlanPriority=4]];[type=ACTION_STRIP_VLAN action=[stripVlan=true]];[type=ACTION_SET_DL_SRC action=[addr=01:02:03:04:05:06]];[type=ACTION_SET_DL_DST action=[addr=06:05:04:03:02:01]];[type=ACTION_SET_NW_SRC action=[addr=127.0.0.3]];[type=ACTION_SET_NW_DST action=[addr=127.0.0.4]];[type=ACTION_SET_NW_TOS action=[ipToS=6]];[type=ACTION_SET_TP_SRC action=[port=7]];[type=ACTION_SET_TP_DST action=[port=8]];[type=ACTION_ENQUEUE action=[port=10 queueId=11]];] dpid=00:00:00:00:00:00:ca:fe inPort=1 outPort=9 flowEntryUserState=FE_USER_ADD flowEntrySwitchState=FE_SWITCH_UPDATED flowEntryErrorState=[type=12 code=13]]" );
+ assertEquals("toString", def.toString(), "[ flowEntryActions=[] flowEntryUserState=FE_USER_UNKNOWN flowEntrySwitchState=FE_SWITCH_UNKNOWN]" );
+ assertEquals("toString", entry.toString(), "[flowEntryId=0x5678 flowId=0x1234 flowEntryMatch=[inPort=1 srcMac=01:02:03:04:05:06 dstMac=06:05:04:03:02:01 ethernetFrameType=2 vlanId=3 vlanPriority=4 srcIPv4Net=127.0.0.1/32 dstIPv4Net=127.0.0.2/32 ipProto=5 ipToS=6 srcTcpUdpPort=7 dstTcpUdpPort=8] flowEntryActions=[[type=ACTION_OUTPUT action=[port=9 maxLen=0]];[type=ACTION_OUTPUT action=[port=-3 maxLen=0]];[type=ACTION_SET_VLAN_VID action=[vlanId=3]];[type=ACTION_SET_VLAN_PCP action=[vlanPriority=4]];[type=ACTION_STRIP_VLAN action=[stripVlan=true]];[type=ACTION_SET_DL_SRC action=[addr=01:02:03:04:05:06]];[type=ACTION_SET_DL_DST action=[addr=06:05:04:03:02:01]];[type=ACTION_SET_NW_SRC action=[addr=127.0.0.3]];[type=ACTION_SET_NW_DST action=[addr=127.0.0.4]];[type=ACTION_SET_NW_TOS action=[ipToS=6]];[type=ACTION_SET_TP_SRC action=[port=7]];[type=ACTION_SET_TP_DST action=[port=8]];[type=ACTION_ENQUEUE action=[port=10 queueId=11]];] dpid=00:00:00:00:00:00:ca:fe inPort=1 outPort=9 flowEntryUserState=FE_USER_ADD flowEntrySwitchState=FE_SWITCH_UPDATED flowEntryErrorState=[type=12 code=13]]" );
}
}
diff --git a/src/test/java/net/onrc/onos/ofcontroller/util/FlowPathTest.java b/src/test/java/net/onrc/onos/ofcontroller/util/FlowPathTest.java
index 89a12e5..bd42ac8 100644
--- a/src/test/java/net/onrc/onos/ofcontroller/util/FlowPathTest.java
+++ b/src/test/java/net/onrc/onos/ofcontroller/util/FlowPathTest.java
@@ -16,6 +16,8 @@
TestFlowPath iFlowPath = new TestFlowPath();
iFlowPath.setFlowIdForTest("0x1234");
iFlowPath.setInstallerIdForTest("installerId");
+ iFlowPath.setFlowPathTypeForTest("FP_TYPE_SHORTEST_PATH");
+ iFlowPath.setFlowPathUserStateForTest("FP_USER_ADD");
iFlowPath.setFlowPathFlagsForTest(0L);
iFlowPath.setSrcSwForTest("CA:FE");
iFlowPath.setSrcPortForTest((short)1);
@@ -38,6 +40,8 @@
@Test
public void testFlowPath(){
FlowPath flowPath = new FlowPath();
+ assertTrue ( flowPath.flowPathType() == FlowPathType.FP_TYPE_UNKNOWN);
+ assertTrue ( flowPath.flowPathUserState() == FlowPathUserState.FP_USER_UNKNOWN);
assertFalse( flowPath.flowPathFlags().isDiscardFirstHopEntry() );
assertFalse( flowPath.flowPathFlags().isKeepOnlyFirstHopEntry() );
assertTrue( flowPath.flowEntryActions().isEmpty() );
@@ -48,6 +52,8 @@
TestFlowPath iFlowPath = new TestFlowPath();
iFlowPath.setFlowIdForTest("0x1234");
iFlowPath.setInstallerIdForTest("installerId");
+ iFlowPath.setFlowPathTypeForTest("FP_TYPE_SHORTEST_PATH");
+ iFlowPath.setFlowPathUserStateForTest("FP_USER_ADD");
iFlowPath.setFlowPathFlagsForTest(0L);
iFlowPath.setSrcSwForTest("CA:FE");
iFlowPath.setSrcPortForTest((short)1);
@@ -91,6 +97,8 @@
FlowPath flowPath = new FlowPath(iFlowPath);
assertEquals(flowPath.flowId().value(), 0x1234);
assertEquals(flowPath.installerId().value(), "installerId");
+ assertEquals(flowPath.flowPathType(), FlowPathType.FP_TYPE_SHORTEST_PATH);
+ assertEquals(flowPath.flowPathUserState(), FlowPathUserState.FP_USER_ADD);
assertEquals(flowPath.flowPathFlags().flags(), 0);
assertEquals(flowPath.dataPath().srcPort().dpid().value(), 0xCAFE);
assertEquals(flowPath.dataPath().srcPort().port().value(), 1);
@@ -134,6 +142,21 @@
assertEquals("FE_SWITCH_UPDATE_IN_PROGRESS", flowPath.dataPath().flowEntries().get(0).flowEntrySwitchState().toString());
}
+ @Test
+ public void testSetFlowPathType(){
+ FlowPath flowPath = new FlowPath();
+ FlowPathType type = FlowPathType.FP_TYPE_SHORTEST_PATH;
+ flowPath.setFlowPathType( type );
+ assertTrue( flowPath.flowPathType() == FlowPathType.FP_TYPE_SHORTEST_PATH );
+ }
+
+ @Test
+ public void testSetFlowPathUserState(){
+ FlowPath flowPath = new FlowPath();
+ FlowPathUserState state = FlowPathUserState.FP_USER_ADD;
+ flowPath.setFlowPathUserState( state );
+ assertTrue( flowPath.flowPathUserState() == FlowPathUserState.FP_USER_ADD );
+ }
@Test
public void testFlowPathFlags(){
@@ -166,7 +189,7 @@
@Test
public void testToString(){
- assertEquals("[flowId=0x1234 installerId=installerId flowPathFlags=[flags=] dataPath=[src=00:00:00:00:00:00:ca:fe/1 flowEntry=[flowEntryId=0x14 flowEntryMatch=[] flowEntryActions=[[type=ACTION_OUTPUT action=[port=23 maxLen=24]];[type=ACTION_OUTPUT action=[port=25 maxLen=26]];] dpid=00:00:00:00:00:00:be:ef flowEntryUserState=FE_USER_MODIFY flowEntrySwitchState=FE_SWITCH_UPDATE_IN_PROGRESS] dst=00:00:00:00:00:00:ba:be/2] flowEntryMatch=[] flowEntryActions=[[type=ACTION_OUTPUT action=[port=10 maxLen=11]];[type=ACTION_OUTPUT action=[port=12 maxLen=13]];]]", flowPath.toString());
+ assertEquals("[flowId=0x1234 installerId=installerId flowPathType=FP_TYPE_SHORTEST_PATH flowPathUserState=FP_USER_ADD flowPathFlags=[flags=] dataPath=[src=00:00:00:00:00:00:ca:fe/1 flowEntry=[flowEntryId=0x14 flowEntryMatch=[] flowEntryActions=[[type=ACTION_OUTPUT action=[port=23 maxLen=24]];[type=ACTION_OUTPUT action=[port=25 maxLen=26]];] dpid=00:00:00:00:00:00:be:ef flowEntryUserState=FE_USER_MODIFY flowEntrySwitchState=FE_SWITCH_UPDATE_IN_PROGRESS] dst=00:00:00:00:00:00:ba:be/2] flowEntryMatch=[] flowEntryActions=[[type=ACTION_OUTPUT action=[port=10 maxLen=11]];[type=ACTION_OUTPUT action=[port=12 maxLen=13]];]]", flowPath.toString());
}
@Test
diff --git a/web/add_flow.py b/web/add_flow.py
index 8100f22..6b9d5d0 100755
--- a/web/add_flow.py
+++ b/web/add_flow.py
@@ -352,6 +352,9 @@
flow_path = {}
flow_path['flowId'] = flow_id
flow_path['installerId'] = installer_id
+ # NOTE: The 'flowPathType' might be rewritten later
+ flow_path['flowPathType'] = 'FP_TYPE_EXPLICIT_PATH'
+ flow_path['flowPathUserState'] = 'FP_USER_ADD'
flow_path['flowPathFlags'] = flowPathFlags
if (len(match) > 0):
@@ -440,6 +443,8 @@
parsed_args[idx]['actionOutputEnabled'] = False
flow_path = compute_flow_path(parsed_args[idx], data_path)
+ flow_path['flowPathType'] = 'FP_TYPE_SHORTEST_PATH'
+
add_shortest_path_flow(flow_path)
idx = idx + 1
diff --git a/web/get_flow.py b/web/get_flow.py
index 9ab55da..c45d853 100755
--- a/web/get_flow.py
+++ b/web/get_flow.py
@@ -161,6 +161,8 @@
def print_flow_path(parsedResult):
flowId = parsedResult['flowId']['value']
installerId = parsedResult['installerId']['value']
+ flowPathType = parsedResult['flowPathType']
+ flowPathUserState = parsedResult['flowPathUserState']
flowPathFlags = parsedResult['flowPathFlags']['flags']
srcSwitch = parsedResult['dataPath']['srcPort']['dpid']['value']
srcPort = parsedResult['dataPath']['srcPort']['port']['value']
@@ -179,7 +181,7 @@
flowPathFlagsStr += ","
flowPathFlagsStr += "KEEP_ONLY_FIRST_HOP_ENTRY"
- print "FlowPath: (flowId = %s installerId = %s flowPathFlags = 0x%x(%s) src = %s/%s dst = %s/%s)" % (flowId, installerId, flowPathFlags, flowPathFlagsStr, srcSwitch, srcPort, dstSwitch, dstPort)
+ print "FlowPath: (flowId = %s installerId = %s flowPathType = %s flowPathUserState = %s flowPathFlags = 0x%x(%s) src = %s/%s dst = %s/%s)" % (flowId, installerId, flowPathType, flowPathUserState, flowPathFlags, flowPathFlagsStr, srcSwitch, srcPort, dstSwitch, dstPort)
#
# Print the common match conditions