Renamed the intent package
net.onrc.onos.intent.* => net.onrc.onos.core.intent.*
Change-Id: Id61f79ed52acf3b91af4ebad2515ac5b7d6dc5e1
diff --git a/src/main/java/net/onrc/onos/core/intent/runtime/PathCalcRuntimeModule.java b/src/main/java/net/onrc/onos/core/intent/runtime/PathCalcRuntimeModule.java
new file mode 100755
index 0000000..9f6353e
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/intent/runtime/PathCalcRuntimeModule.java
@@ -0,0 +1,407 @@
+package net.onrc.onos.core.intent.runtime;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.concurrent.locks.ReentrantLock;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import net.floodlightcontroller.core.module.FloodlightModuleContext;
+import net.floodlightcontroller.core.module.FloodlightModuleException;
+import net.floodlightcontroller.core.module.IFloodlightModule;
+import net.floodlightcontroller.core.module.IFloodlightService;
+import net.onrc.onos.core.datagrid.IDatagridService;
+import net.onrc.onos.core.datagrid.IEventChannel;
+import net.onrc.onos.core.datagrid.IEventChannelListener;
+import net.onrc.onos.core.intent.Intent;
+import net.onrc.onos.core.intent.IntentMap;
+import net.onrc.onos.core.intent.IntentOperation;
+import net.onrc.onos.core.intent.IntentOperationList;
+import net.onrc.onos.core.intent.PathIntent;
+import net.onrc.onos.core.intent.PathIntentMap;
+import net.onrc.onos.core.intent.ShortestPathIntent;
+import net.onrc.onos.core.intent.Intent.IntentState;
+import net.onrc.onos.core.intent.IntentOperation.Operator;
+import net.onrc.onos.ofcontroller.networkgraph.DeviceEvent;
+import net.onrc.onos.ofcontroller.networkgraph.INetworkGraphListener;
+import net.onrc.onos.ofcontroller.networkgraph.INetworkGraphService;
+import net.onrc.onos.ofcontroller.networkgraph.LinkEvent;
+import net.onrc.onos.ofcontroller.networkgraph.PortEvent;
+import net.onrc.onos.ofcontroller.networkgraph.SwitchEvent;
+import net.onrc.onos.registry.controller.IControllerRegistryService;
+
+/**
+ * @author Toshio Koide (t-koide@onlab.us)
+ */
+public class PathCalcRuntimeModule implements IFloodlightModule, IPathCalcRuntimeService, INetworkGraphListener, IEventChannelListener<Long, IntentStateList> {
+ class PerfLog {
+ private String step;
+ private long time;
+
+ public PerfLog(String step) {
+ this.step = step;
+ this.time = System.nanoTime();
+ }
+
+ public void logThis() {
+ log.error("Time:{}, Step:{}", time, step);
+ }
+ }
+ class PerfLogger {
+ private LinkedList<PerfLog> logData = new LinkedList<>();
+
+ public PerfLogger(String logPhase) {
+ log("start_" + logPhase);
+ }
+
+ public void log(String step) {
+ logData.add(new PerfLog(step));
+ }
+
+ public void flushLog() {
+ log("finish");
+ for (PerfLog log: logData) {
+ log.logThis();
+ }
+ logData.clear();
+ }
+ }
+ private PathCalcRuntime runtime;
+ private IDatagridService datagridService;
+ private INetworkGraphService networkGraphService;
+ private IntentMap highLevelIntents;
+ private PathIntentMap pathIntents;
+ private IControllerRegistryService controllerRegistry;
+ private PersistIntent persistIntent;
+
+ private IEventChannel<Long, IntentOperationList> opEventChannel;
+ private final ReentrantLock lock = new ReentrantLock();
+ private HashSet<LinkEvent> unmatchedLinkEvents = new HashSet<>();
+ private static final String INTENT_OP_EVENT_CHANNEL_NAME = "onos.pathintent";
+ private static final String INTENT_STATE_EVENT_CHANNEL_NAME = "onos.pathintent_state";
+ private static final Logger log = LoggerFactory.getLogger(PathCalcRuntimeModule.class);
+
+ // ================================================================================
+ // private methods
+ // ================================================================================
+
+ private void reroutePaths(Collection<Intent> oldPaths) {
+ if (oldPaths == null || oldPaths.isEmpty())
+ return;
+
+ IntentOperationList reroutingOperation = new IntentOperationList();
+ for (Intent intent : oldPaths) {
+ PathIntent pathIntent = (PathIntent) intent;
+ if (pathIntent.isPathFrozen())
+ continue;
+ if (pathIntent.getState().equals(IntentState.INST_ACK) && // XXX: path intents in flight
+ !reroutingOperation.contains(pathIntent.getParentIntent())) {
+ reroutingOperation.add(Operator.ADD, pathIntent.getParentIntent());
+ }
+ }
+ executeIntentOperations(reroutingOperation);
+ }
+
+
+ // ================================================================================
+ // IFloodlightModule implementations
+ // ================================================================================
+
+ @Override
+ public Collection<Class<? extends IFloodlightService>> getModuleServices() {
+ Collection<Class<? extends IFloodlightService>> l = new ArrayList<>(1);
+ l.add(IPathCalcRuntimeService.class);
+ return l;
+ }
+
+ @Override
+ public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() {
+ Map<Class<? extends IFloodlightService>, IFloodlightService> m = new HashMap<>();
+ m.put(IPathCalcRuntimeService.class, this);
+ return m;
+ }
+
+ @Override
+ public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
+ Collection<Class<? extends IFloodlightService>> l = new ArrayList<>(2);
+ l.add(IDatagridService.class);
+ l.add(INetworkGraphService.class);
+ return l;
+ }
+
+ @Override
+ public void init(FloodlightModuleContext context) throws FloodlightModuleException {
+ datagridService = context.getServiceImpl(IDatagridService.class);
+ networkGraphService = context.getServiceImpl(INetworkGraphService.class);
+ controllerRegistry = context.getServiceImpl(IControllerRegistryService.class);
+ }
+
+ @Override
+ public void startUp(FloodlightModuleContext context) {
+ highLevelIntents = new IntentMap();
+ runtime = new PathCalcRuntime(networkGraphService.getNetworkGraph());
+ pathIntents = new PathIntentMap();
+ opEventChannel = datagridService.createChannel(INTENT_OP_EVENT_CHANNEL_NAME, Long.class, IntentOperationList.class);
+ datagridService.addListener(INTENT_STATE_EVENT_CHANNEL_NAME, this, Long.class, IntentStateList.class);
+ networkGraphService.registerNetworkGraphListener(this);
+ persistIntent = new PersistIntent(controllerRegistry, networkGraphService);
+ }
+
+ // ================================================================================
+ // IPathCalcRuntimeService implementations
+ // ================================================================================
+
+ @Override
+ public IntentOperationList executeIntentOperations(IntentOperationList list) {
+ if (list == null || list.size() == 0)
+ return null;
+ PerfLogger p = new PerfLogger("executeIntentOperations_" + list.get(0).operator);
+
+ lock.lock(); // TODO optimize locking using smaller steps
+ try {
+ // update the map of high-level intents
+ p.log("begin_updateInMemoryIntents");
+ highLevelIntents.executeOperations(list);
+
+ // change states of high-level intents
+ IntentStateList states = new IntentStateList();
+ for (IntentOperation op : list) {
+ switch (op.operator) {
+ case ADD:
+ switch (op.intent.getState()) {
+ case CREATED:
+ states.put(op.intent.getId(), IntentState.INST_REQ);
+ break;
+ case INST_ACK:
+ states.put(op.intent.getId(), IntentState.REROUTE_REQ);
+ break;
+ default:
+ break;
+ }
+ break;
+ case REMOVE:
+ switch (op.intent.getState()) {
+ case CREATED:
+ states.put(op.intent.getId(), IntentState.DEL_REQ);
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ highLevelIntents.changeStates(states);
+ p.log("end_updateInMemoryIntents");
+
+ // calculate path-intents (low-level operations)
+ p.log("begin_calcPathIntents");
+ IntentOperationList pathIntentOperations = runtime.calcPathIntents(list, highLevelIntents, pathIntents);
+ p.log("end_calcPathIntents");
+
+ // persist calculated low-level operations into data store
+ p.log("begin_persistPathIntents");
+ long key = persistIntent.getKey();
+ persistIntent.persistIfLeader(key, pathIntentOperations);
+ p.log("end_persistPathIntents");
+
+ // remove error-intents and reflect them to high-level intents
+ p.log("begin_removeErrorIntents");
+ states.clear();
+ Iterator<IntentOperation> i = pathIntentOperations.iterator();
+ while (i.hasNext()) {
+ IntentOperation op = i.next();
+ if (op.operator.equals(Operator.ERROR)) {
+ states.put(op.intent.getId(), IntentState.INST_NACK);
+ i.remove();
+ }
+ }
+ highLevelIntents.changeStates(states);
+ p.log("end_removeErrorIntents");
+
+ // update the map of path intents and publish the path operations
+ p.log("begin_updateInMemoryPathIntents");
+ pathIntents.executeOperations(pathIntentOperations);
+ p.log("end_updateInMemoryPathIntents");
+
+ // XXX Demo special: add a complete path to remove operation
+ p.log("begin_addPathToRemoveOperation");
+ for (IntentOperation op: pathIntentOperations) {
+ if(op.operator.equals(Operator.REMOVE)) {
+ op.intent = pathIntents.getIntent(op.intent.getId());
+ }
+ if (op.intent instanceof PathIntent) {
+ log.debug("operation: {}, intent:{}", op.operator, op.intent);
+ }
+ }
+ p.log("end_addPathToRemoveOperation");
+
+ // send notification
+ p.log("begin_sendNotification");
+ // XXX: Send notifications using the same key every time
+ // and receive them by entryAdded() and entryUpdated()
+ opEventChannel.addEntry(0L, pathIntentOperations);
+ p.log("end_sendNotification");
+ //opEventChannel.removeEntry(key);
+ return pathIntentOperations;
+ }
+ finally {
+ p.flushLog();
+ lock.unlock();
+ }
+ }
+
+ @Override
+ public IntentMap getHighLevelIntents() {
+ return highLevelIntents;
+ }
+
+ @Override
+ public IntentMap getPathIntents() {
+ return pathIntents;
+ }
+
+ @Override
+ public void purgeIntents() {
+ highLevelIntents.purge();
+ pathIntents.purge();
+ }
+
+ // ================================================================================
+ // INetworkGraphListener implementations
+ // ================================================================================
+
+ @Override
+ public void networkGraphEvents(Collection<SwitchEvent> addedSwitchEvents,
+ Collection<SwitchEvent> removedSwitchEvents,
+ Collection<PortEvent> addedPortEvents,
+ Collection<PortEvent> removedPortEvents,
+ Collection<LinkEvent> addedLinkEvents,
+ Collection<LinkEvent> removedLinkEvents,
+ Collection<DeviceEvent> addedDeviceEvents,
+ Collection<DeviceEvent> removedDeviceEvents) {
+
+ PerfLogger p = new PerfLogger("networkGraphEvents");
+ HashSet<Intent> affectedPaths = new HashSet<>();
+
+ boolean rerouteAll = false;
+ for(LinkEvent le : addedLinkEvents) {
+ LinkEvent rev = new LinkEvent(le.getDst().getDpid(), le.getDst().getNumber(), le.getSrc().getDpid(), le.getSrc().getNumber());
+ if(unmatchedLinkEvents.contains(rev)) {
+ rerouteAll = true;
+ unmatchedLinkEvents.remove(rev);
+ log.debug("Found matched LinkEvent: {} {}", rev, le);
+ }
+ else {
+ unmatchedLinkEvents.add(le);
+ log.debug("Adding unmatched LinkEvent: {}", le);
+ }
+ }
+ for(LinkEvent le : removedLinkEvents) {
+ if (unmatchedLinkEvents.contains(le)) {
+ unmatchedLinkEvents.remove(le);
+ log.debug("Removing LinkEvent: {}", le);
+ }
+ }
+ if(unmatchedLinkEvents.size() > 0) {
+ log.debug("Unmatched link events: {} events", unmatchedLinkEvents.size());
+ }
+
+ if ( rerouteAll ) {//addedLinkEvents.size() > 0) { // ||
+// addedPortEvents.size() > 0 ||
+// addedSwitchEvents.size() > 0) {
+ p.log("begin_getAllIntents");
+ affectedPaths.addAll(getPathIntents().getAllIntents());
+ p.log("end_getAllIntents");
+ }
+ else if (removedSwitchEvents.size() > 0 ||
+ removedLinkEvents.size() > 0 ||
+ removedPortEvents.size() > 0) {
+ p.log("begin_getIntentsByLink");
+ for (LinkEvent linkEvent: removedLinkEvents)
+ affectedPaths.addAll(pathIntents.getIntentsByLink(linkEvent));
+ p.log("end_getIntentsByLink");
+
+ p.log("begin_getIntentsByPort");
+ for (PortEvent portEvent: removedPortEvents)
+ affectedPaths.addAll(pathIntents.getIntentsByPort(portEvent.getDpid(), portEvent.getNumber()));
+ p.log("end_getIntentsByPort");
+
+ p.log("begin_getIntentsByDpid");
+ for (SwitchEvent switchEvent: removedSwitchEvents)
+ affectedPaths.addAll(pathIntents.getIntentsByDpid(switchEvent.getDpid()));
+ p.log("end_getIntentsByDpid");
+ }
+ p.log("begin_reroutePaths");
+ reroutePaths(affectedPaths);
+ p.log("end_reroutePaths");
+ p.flushLog();
+ }
+
+ // ================================================================================
+ // IEventChannelListener implementations
+ // ================================================================================
+
+ @Override
+ public void entryAdded(IntentStateList value) {
+ entryUpdated(value);
+ }
+
+ @Override
+ public void entryRemoved(IntentStateList value) {
+ // do nothing
+ }
+
+ @Override
+ public void entryUpdated(IntentStateList value) {
+ // TODO draw state transition diagram in multiple ONOS instances and update this method
+ PerfLogger p = new PerfLogger("entryUpdated");
+ lock.lock(); // TODO optimize locking using smaller steps
+ try {
+ // reflect state changes of path-level intent into application-level intents
+ p.log("begin_changeStateByNotification");
+ IntentStateList highLevelIntentStates = new IntentStateList();
+ IntentStateList pathIntentStates = new IntentStateList();
+ for (Entry<String, IntentState> entry: value.entrySet()) {
+ PathIntent pathIntent = (PathIntent) pathIntents.getIntent(entry.getKey());
+ if (pathIntent == null) continue;
+
+ Intent parentIntent = pathIntent.getParentIntent();
+ if (parentIntent == null ||
+ !(parentIntent instanceof ShortestPathIntent) ||
+ !((ShortestPathIntent) parentIntent).getPathIntentId().equals(pathIntent.getId()))
+ continue;
+
+ IntentState state = entry.getValue();
+ switch (state) {
+ //case INST_REQ:
+ case INST_ACK:
+ case INST_NACK:
+ //case DEL_REQ:
+ case DEL_ACK:
+ case DEL_PENDING:
+ highLevelIntentStates.put(parentIntent.getId(), state);
+ pathIntentStates.put(entry.getKey(), entry.getValue());
+ break;
+ default:
+ break;
+ }
+ }
+ highLevelIntents.changeStates(highLevelIntentStates);
+ pathIntents.changeStates(pathIntentStates);
+ p.log("end_changeStateByNotification");
+ }
+ finally {
+ p.flushLog();
+ lock.unlock();
+ }
+ }
+}