First round of cleanups in optical path provisioner. No more user input for packet/optical mininet script.
Change-Id: Ibbfa6a17a97432da8dee63e9cd15fa6b1c2c1e46
diff --git a/apps/optical/src/main/java/org/onosproject/optical/OpticalPathProvisioner.java b/apps/optical/src/main/java/org/onosproject/optical/OpticalPathProvisioner.java
index c5ec560..6b95d63 100644
--- a/apps/optical/src/main/java/org/onosproject/optical/OpticalPathProvisioner.java
+++ b/apps/optical/src/main/java/org/onosproject/optical/OpticalPathProvisioner.java
@@ -18,6 +18,7 @@
import com.google.common.collect.Lists;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.onosproject.cluster.ClusterService;
@@ -44,19 +45,23 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.util.Collections;
import java.util.Iterator;
+import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
+import static com.google.common.base.Preconditions.checkArgument;
import static org.onosproject.net.intent.IntentState.INSTALLED;
+import static com.google.common.base.Preconditions.checkNotNull;
+
/**
- * OpticalPathProvisioner listens event notifications from the Intent F/W.
+ * OpticalPathProvisioner listens for event notifications from the Intent F/W.
* It generates one or more opticalConnectivityIntent(s) and submits (or withdraws) to Intent F/W
* for adding/releasing capacity at the packet layer.
- *
*/
@Component(immediate = true)
@@ -86,12 +91,12 @@
private ApplicationId appId;
// TODO use a shared map for distributed operation
- protected final Map<ConnectPoint, OpticalConnectivityIntent> inStatusTportMap =
+ private final Map<ConnectPoint, OpticalConnectivityIntent> inStatusTportMap =
new ConcurrentHashMap<>();
- protected final Map<ConnectPoint, OpticalConnectivityIntent> outStatusTportMap =
+ private final Map<ConnectPoint, OpticalConnectivityIntent> outStatusTportMap =
new ConcurrentHashMap<>();
- protected final Map<ConnectPoint, Map<ConnectPoint, Intent>> intentMap =
+ private final Map<ConnectPoint, Map<ConnectPoint, Intent>> intentMap =
new ConcurrentHashMap<>();
private final InternalOpticalPathProvisioner pathProvisioner = new InternalOpticalPathProvisioner();
@@ -101,7 +106,13 @@
intentService.addListener(pathProvisioner);
appId = coreService.registerApplication("org.onosproject.optical");
initTport();
- log.info("Starting optical path provisioning...");
+ log.info("Started");
+ }
+
+ @Deactivate
+ protected void deactivate() {
+ intentService.removeListener(pathProvisioner);
+ log.info("Stopped");
}
protected void initTport() {
@@ -119,10 +130,6 @@
}
}
- protected void deactivate() {
- intentService.removeListener(pathProvisioner);
- }
-
public class InternalOpticalPathProvisioner implements IntentListener {
@Override
public void event(IntentEvent event) {
@@ -132,11 +139,11 @@
case INSTALLED:
break;
case FAILED:
- log.info("packet intent {} failed, calling optical path provisioning APP.", event.subject());
+ log.info("Intent {} failed, calling optical path provisioning app.", event.subject());
setupLightpath(event.subject());
break;
case WITHDRAWN:
- log.info("intent {} withdrawn.", event.subject());
+ log.info("Intent {} withdrawn.", event.subject());
//FIXME
//teardownLightpath(event.subject());
break;
@@ -162,6 +169,7 @@
/**
* Registers an intent from src to dst.
+ *
* @param src source point
* @param dst destination point
* @param intent intent to be registered
@@ -242,78 +250,124 @@
}
}
+ /**
+ * Returns list of cross connection points of missing optical path sections.
+ *
+ * Scans the given multi-layer path and looks for sections that use cross connect links.
+ * The ingress and egress points in the optical layer are returned in a list.
+ *
+ * @param path the multi-layer path
+ * @return list of cross connection points on the optical layer
+ */
+ private List<ConnectPoint> getCrossConnectPoints(Path path) {
+ boolean scanning = false;
+ List<ConnectPoint> connectPoints = new LinkedList<ConnectPoint>();
+
+ for (Link link : path.links()) {
+ if (!isCrossConnectLink(link)) {
+ continue;
+ }
+
+ if (scanning) {
+ connectPoints.add(checkNotNull(link.src()));
+ scanning = false;
+ } else {
+ connectPoints.add(checkNotNull(link.dst()));
+ scanning = true;
+ }
+ }
+
+ return connectPoints;
+ }
+
+ /**
+ * Checks availability of cross connect points by verifying T port status.
+ * TODO: refactor after rewriting OpticalConnectivityIntentCompiler
+ *
+ * @param crossConnectPoints list of cross connection points
+ * @return true if all cross connect points are available, false otherwise
+ */
+ private boolean checkCrossConnectPoints(List<ConnectPoint> crossConnectPoints) {
+ checkArgument(crossConnectPoints.size() % 2 == 0);
+
+ Iterator<ConnectPoint> itr = crossConnectPoints.iterator();
+
+ while (itr.hasNext()) {
+ // checkArgument at start ensures we'll always have pairs of connect points
+ ConnectPoint src = itr.next();
+ ConnectPoint dst = itr.next();
+
+ if (inStatusTportMap.get(src) != null || outStatusTportMap.get(dst) != null) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Scans the list of cross connection points and returns a list of optical connectivity intents
+ * in both directions.
+ *
+ * @param crossConnectPoints list of cross connection points
+ * @return list of optical connectivity intents
+ */
+ private List<Intent> getIntents(List<ConnectPoint> crossConnectPoints) {
+ checkArgument(crossConnectPoints.size() % 2 == 0);
+
+ List<Intent> intents = new LinkedList<Intent>();
+ Iterator<ConnectPoint> itr = crossConnectPoints.iterator();
+
+ while (itr.hasNext()) {
+ // checkArgument at start ensures we'll always have pairs of connect points
+ ConnectPoint src = itr.next();
+ ConnectPoint dst = itr.next();
+
+ // TODO: should have option for bidirectional OpticalConnectivityIntent
+ Intent opticalIntent = OpticalConnectivityIntent.builder()
+ .appId(appId)
+ .src(src)
+ .dst(dst)
+ .build();
+ Intent opticalIntentRev = OpticalConnectivityIntent.builder()
+ .appId(appId)
+ .src(dst)
+ .dst(src)
+ .build();
+ intents.add(opticalIntent);
+ intents.add(opticalIntentRev);
+ }
+
+ return intents;
+ }
+
private List<Intent> getOpticalPath(ConnectPoint ingress, ConnectPoint egress) {
Set<Path> paths = pathService.getPaths(ingress.deviceId(),
- egress.deviceId(),
- new OpticalLinkWeight());
+ egress.deviceId(),
+ new OpticalLinkWeight());
if (paths.isEmpty()) {
- return Lists.newArrayList();
+ return Collections.emptyList();
}
List<Intent> connectionList = Lists.newArrayList();
+ // Iterate over all paths until a suitable one has been found
Iterator<Path> itrPath = paths.iterator();
while (itrPath.hasNext()) {
- boolean usedTportFound = false;
Path nextPath = itrPath.next();
- log.info(nextPath.links().toString()); // TODO drop log level
- Iterator<Link> itrLink = nextPath.links().iterator();
- while (itrLink.hasNext()) {
- ConnectPoint srcWdmPoint, dstWdmPoint;
- Link link1 = itrLink.next();
- if (!isOpticalLink(link1)) {
- continue;
- } else {
- srcWdmPoint = link1.dst();
- dstWdmPoint = srcWdmPoint;
- }
+ List<ConnectPoint> crossConnectPoints = getCrossConnectPoints(nextPath);
- while (itrLink.hasNext()) {
- Link link2 = itrLink.next();
- if (isOpticalLink(link2)) {
- dstWdmPoint = link2.src();
- } else {
- break;
- }
- }
-
- if (inStatusTportMap.get(srcWdmPoint) != null ||
- outStatusTportMap.get(dstWdmPoint) != null) {
- usedTportFound = true;
- // log.info("used ConnectPoint {} to {} were found", srcWdmPoint, dstWdmPoint);
- break;
- }
-
- Intent opticalIntent = OpticalConnectivityIntent.builder()
- .appId(appId)
- .src(srcWdmPoint)
- .dst(dstWdmPoint)
- .build();
- Intent opticalIntent2 = OpticalConnectivityIntent.builder()
- .appId(appId)
- .src(dstWdmPoint)
- .dst(srcWdmPoint)
- .build();
- log.info("Creating optical intent from {} to {}", srcWdmPoint, dstWdmPoint);
- log.info("Creating optical intent from {} to {}", dstWdmPoint, srcWdmPoint);
- connectionList.add(opticalIntent);
- connectionList.add(opticalIntent2);
-
- break;
+ // Skip to next path if not all connect points are available
+ if (!checkCrossConnectPoints(crossConnectPoints)) {
+ continue;
}
- if (!usedTportFound) {
- break;
- } else {
- // reset the connection list
- connectionList = Lists.newArrayList();
- }
-
+ return getIntents(crossConnectPoints);
}
- return connectionList;
+ return Collections.emptyList();
}
private void teardownLightpath(Intent intent) {
@@ -330,26 +384,45 @@
}
- private static boolean isOpticalLink(Link link) {
- boolean isOptical = false;
- Link.Type lt = link.type();
- if (lt == Link.Type.OPTICAL) {
- isOptical = true;
+ /**
+ * Verifies if given link is cross-connect between packet and optical layer.
+ *
+ * @param link the link
+ * @return true if the link is a cross-connect link
+ */
+ public static boolean isCrossConnectLink(Link link) {
+ if (link.type() != Link.Type.OPTICAL) {
+ return false;
}
- return isOptical;
+
+ checkNotNull(link.annotations());
+ checkNotNull(link.annotations().value("optical.type"));
+
+ if (link.annotations().value("optical.type").equals("cross-connect")) {
+ return true;
+ }
+
+ return false;
}
+ /**
+ * Link weight function that emphasizes re-use of packet links.
+ */
private static class OpticalLinkWeight implements LinkWeight {
@Override
public double weight(TopologyEdge edge) {
+ // Ignore inactive links
if (edge.link().state() == Link.State.INACTIVE) {
- return -1; // ignore inactive links
+ return -1;
}
- if (isOpticalLink(edge.link())) {
- return 1000; // optical links
- } else {
- return 1; // packet links
+
+ // Transport links have highest weight
+ if (edge.link().type() == Link.Type.OPTICAL) {
+ return 1000;
}
+
+ // Packet links
+ return 1;
}
}
diff --git a/tools/test/topos/opticalUtils.py b/tools/test/topos/opticalUtils.py
index 21dd1f2..116c0cc 100644
--- a/tools/test/topos/opticalUtils.py
+++ b/tools/test/topos/opticalUtils.py
@@ -56,6 +56,7 @@
import json
import os
from time import sleep
+import urllib2
from mininet.node import Switch, RemoteController
from mininet.topo import Topo
@@ -65,6 +66,10 @@
from mininet.link import Link, Intf
from mininet.cli import CLI
+# Sleep time and timeout values in seconds
+SLEEP_TIME = .5
+TIMEOUT = 60
+
class OpticalSwitch(Switch):
"""
For now, same as Switch class.
@@ -414,17 +419,37 @@
intf2 = intfList[ 0 ]
intf.node.attach(LINCSwitch.findTap(intf2.node, intf2.node.ports[ intf2 ]))
- info('*** Press ENTER to push Topology.json to onos...\n')
- raw_input() # FIXME... we should eventually remove this
+ info('*** Waiting for all devices to be available in ONOS...\n')
+ url = 'http://%s:8181/onos/v1/devices' % LINCSwitch.controllers[0].ip
+ time = 0
+ while True:
+ response = json.load(urllib2.urlopen(url))
+ devs = response.get('devices')
+
+ # Wait for all devices to be registered & available
+ if (len(devices) == len(devs)):
+ for d in devs:
+ if not d['available']:
+ continue
+ break
+
+ if (time >= TIMEOUT):
+ error('***ERROR: ONOS did not register devices within %s seconds\n' % TIMEOUT)
+ break
+
+ time += SLEEP_TIME
+ sleep(SLEEP_TIME)
+
info('*** Pushing Topology.json to ONOS\n')
output = quietRun('%s/tools/test/bin/onos-topo-cfg %s Topology.json' % (LINCSwitch.onosDir, LINCSwitch.controllers[ 0 ].ip), shell=True)
+
# successful output contains the two characters '{}'
# if there is more output than this, there is an issue
if output.strip('{}'):
- warn('***WARNING: Could not push topology file to ONOS: %s' % output)
+ warn('***WARNING: Could not push topology file to ONOS: %s\n' % output)
@staticmethod
- def waitStarted(net, timeout=None):
+ def waitStarted(net, timeout=TIMEOUT):
"wait until all tap interfaces are available"
tapCount = 0
time = 0
@@ -437,11 +462,11 @@
if str(tapCount) == quietRun('ip addr | grep tap | wc -l', shell=True).strip('\n'):
return True
if timeout:
- if time >= timeout:
- error('***ERROR: Linc OE did not start within %s seconds' % timeout)
+ if time >= TIMEOUT:
+ error('***ERROR: LINC OE did not start within %s seconds\n' % TIMEOUT)
return False
- time += .5
- sleep(.5)
+ time += SLEEP_TIME
+ sleep(SLEEP_TIME)
@staticmethod
def shutdownOE():