Marc De Leenheer
Committed by Gerrit Code Review

First round of cleanups in optical path provisioner. No more user input for pack…

…et/optical mininet script.

Change-Id: Ibbfa6a17a97432da8dee63e9cd15fa6b1c2c1e46
......@@ -18,6 +18,7 @@ package org.onosproject.optical;
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.onosproject.net.topology.TopologyEdge;
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 @@ public class OpticalPathProvisioner {
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 @@ public class OpticalPathProvisioner {
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 @@ public class OpticalPathProvisioner {
}
}
protected void deactivate() {
intentService.removeListener(pathProvisioner);
}
public class InternalOpticalPathProvisioner implements IntentListener {
@Override
public void event(IntentEvent event) {
......@@ -132,11 +139,11 @@ public class OpticalPathProvisioner {
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 @@ public class OpticalPathProvisioner {
/**
* 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 @@ public class OpticalPathProvisioner {
}
}
private List<Intent> getOpticalPath(ConnectPoint ingress, ConnectPoint egress) {
Set<Path> paths = pathService.getPaths(ingress.deviceId(),
egress.deviceId(),
new OpticalLinkWeight());
/**
* 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>();
if (paths.isEmpty()) {
return Lists.newArrayList();
for (Link link : path.links()) {
if (!isCrossConnectLink(link)) {
continue;
}
List<Intent> connectionList = Lists.newArrayList();
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;
if (scanning) {
connectPoints.add(checkNotNull(link.src()));
scanning = false;
} else {
srcWdmPoint = link1.dst();
dstWdmPoint = srcWdmPoint;
connectPoints.add(checkNotNull(link.dst()));
scanning = true;
}
}
while (itrLink.hasNext()) {
Link link2 = itrLink.next();
if (isOpticalLink(link2)) {
dstWdmPoint = link2.src();
} else {
break;
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;
}
}
if (inStatusTportMap.get(srcWdmPoint) != null ||
outStatusTportMap.get(dstWdmPoint) != null) {
usedTportFound = true;
// log.info("used ConnectPoint {} to {} were found", srcWdmPoint, dstWdmPoint);
break;
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(srcWdmPoint)
.dst(dstWdmPoint)
.src(src)
.dst(dst)
.build();
Intent opticalIntent2 = OpticalConnectivityIntent.builder()
Intent opticalIntentRev = OpticalConnectivityIntent.builder()
.appId(appId)
.src(dstWdmPoint)
.dst(srcWdmPoint)
.src(dst)
.dst(src)
.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);
intents.add(opticalIntent);
intents.add(opticalIntentRev);
}
break;
return intents;
}
if (!usedTportFound) {
break;
} else {
// reset the connection list
connectionList = Lists.newArrayList();
private List<Intent> getOpticalPath(ConnectPoint ingress, ConnectPoint egress) {
Set<Path> paths = pathService.getPaths(ingress.deviceId(),
egress.deviceId(),
new OpticalLinkWeight());
if (paths.isEmpty()) {
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()) {
Path nextPath = itrPath.next();
List<ConnectPoint> crossConnectPoints = getCrossConnectPoints(nextPath);
// Skip to next path if not all connect points are available
if (!checkCrossConnectPoints(crossConnectPoints)) {
continue;
}
return getIntents(crossConnectPoints);
}
return connectionList;
return Collections.emptyList();
}
private void teardownLightpath(Intent intent) {
......@@ -330,26 +384,45 @@ public class OpticalPathProvisioner {
}
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;
}
checkNotNull(link.annotations());
checkNotNull(link.annotations().value("optical.type"));
if (link.annotations().value("optical.type").equals("cross-connect")) {
return true;
}
return isOptical;
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;
}
}
......
......@@ -56,6 +56,7 @@ import re
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.log import setLogLevel, info, error, warn
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 @@ class LINCSwitch(OpticalSwitch):
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 @@ class LINCSwitch(OpticalSwitch):
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():
......