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; ...@@ -18,6 +18,7 @@ package org.onosproject.optical;
18 import com.google.common.collect.Lists; 18 import com.google.common.collect.Lists;
19 import org.apache.felix.scr.annotations.Activate; 19 import org.apache.felix.scr.annotations.Activate;
20 import org.apache.felix.scr.annotations.Component; 20 import org.apache.felix.scr.annotations.Component;
21 +import org.apache.felix.scr.annotations.Deactivate;
21 import org.apache.felix.scr.annotations.Reference; 22 import org.apache.felix.scr.annotations.Reference;
22 import org.apache.felix.scr.annotations.ReferenceCardinality; 23 import org.apache.felix.scr.annotations.ReferenceCardinality;
23 import org.onosproject.cluster.ClusterService; 24 import org.onosproject.cluster.ClusterService;
...@@ -44,19 +45,23 @@ import org.onosproject.net.topology.TopologyEdge; ...@@ -44,19 +45,23 @@ import org.onosproject.net.topology.TopologyEdge;
44 import org.slf4j.Logger; 45 import org.slf4j.Logger;
45 import org.slf4j.LoggerFactory; 46 import org.slf4j.LoggerFactory;
46 47
48 +import java.util.Collections;
47 import java.util.Iterator; 49 import java.util.Iterator;
50 +import java.util.LinkedList;
48 import java.util.List; 51 import java.util.List;
49 import java.util.Map; 52 import java.util.Map;
50 import java.util.Set; 53 import java.util.Set;
51 import java.util.concurrent.ConcurrentHashMap; 54 import java.util.concurrent.ConcurrentHashMap;
52 55
56 +import static com.google.common.base.Preconditions.checkArgument;
53 import static org.onosproject.net.intent.IntentState.INSTALLED; 57 import static org.onosproject.net.intent.IntentState.INSTALLED;
54 58
59 +import static com.google.common.base.Preconditions.checkNotNull;
60 +
55 /** 61 /**
56 - * OpticalPathProvisioner listens event notifications from the Intent F/W. 62 + * OpticalPathProvisioner listens for event notifications from the Intent F/W.
57 * It generates one or more opticalConnectivityIntent(s) and submits (or withdraws) to Intent F/W 63 * It generates one or more opticalConnectivityIntent(s) and submits (or withdraws) to Intent F/W
58 * for adding/releasing capacity at the packet layer. 64 * for adding/releasing capacity at the packet layer.
59 - *
60 */ 65 */
61 66
62 @Component(immediate = true) 67 @Component(immediate = true)
...@@ -86,12 +91,12 @@ public class OpticalPathProvisioner { ...@@ -86,12 +91,12 @@ public class OpticalPathProvisioner {
86 private ApplicationId appId; 91 private ApplicationId appId;
87 92
88 // TODO use a shared map for distributed operation 93 // TODO use a shared map for distributed operation
89 - protected final Map<ConnectPoint, OpticalConnectivityIntent> inStatusTportMap = 94 + private final Map<ConnectPoint, OpticalConnectivityIntent> inStatusTportMap =
90 new ConcurrentHashMap<>(); 95 new ConcurrentHashMap<>();
91 - protected final Map<ConnectPoint, OpticalConnectivityIntent> outStatusTportMap = 96 + private final Map<ConnectPoint, OpticalConnectivityIntent> outStatusTportMap =
92 new ConcurrentHashMap<>(); 97 new ConcurrentHashMap<>();
93 98
94 - protected final Map<ConnectPoint, Map<ConnectPoint, Intent>> intentMap = 99 + private final Map<ConnectPoint, Map<ConnectPoint, Intent>> intentMap =
95 new ConcurrentHashMap<>(); 100 new ConcurrentHashMap<>();
96 101
97 private final InternalOpticalPathProvisioner pathProvisioner = new InternalOpticalPathProvisioner(); 102 private final InternalOpticalPathProvisioner pathProvisioner = new InternalOpticalPathProvisioner();
...@@ -101,7 +106,13 @@ public class OpticalPathProvisioner { ...@@ -101,7 +106,13 @@ public class OpticalPathProvisioner {
101 intentService.addListener(pathProvisioner); 106 intentService.addListener(pathProvisioner);
102 appId = coreService.registerApplication("org.onosproject.optical"); 107 appId = coreService.registerApplication("org.onosproject.optical");
103 initTport(); 108 initTport();
104 - log.info("Starting optical path provisioning..."); 109 + log.info("Started");
110 + }
111 +
112 + @Deactivate
113 + protected void deactivate() {
114 + intentService.removeListener(pathProvisioner);
115 + log.info("Stopped");
105 } 116 }
106 117
107 protected void initTport() { 118 protected void initTport() {
...@@ -119,10 +130,6 @@ public class OpticalPathProvisioner { ...@@ -119,10 +130,6 @@ public class OpticalPathProvisioner {
119 } 130 }
120 } 131 }
121 132
122 - protected void deactivate() {
123 - intentService.removeListener(pathProvisioner);
124 - }
125 -
126 public class InternalOpticalPathProvisioner implements IntentListener { 133 public class InternalOpticalPathProvisioner implements IntentListener {
127 @Override 134 @Override
128 public void event(IntentEvent event) { 135 public void event(IntentEvent event) {
...@@ -132,11 +139,11 @@ public class OpticalPathProvisioner { ...@@ -132,11 +139,11 @@ public class OpticalPathProvisioner {
132 case INSTALLED: 139 case INSTALLED:
133 break; 140 break;
134 case FAILED: 141 case FAILED:
135 - log.info("packet intent {} failed, calling optical path provisioning APP.", event.subject()); 142 + log.info("Intent {} failed, calling optical path provisioning app.", event.subject());
136 setupLightpath(event.subject()); 143 setupLightpath(event.subject());
137 break; 144 break;
138 case WITHDRAWN: 145 case WITHDRAWN:
139 - log.info("intent {} withdrawn.", event.subject()); 146 + log.info("Intent {} withdrawn.", event.subject());
140 //FIXME 147 //FIXME
141 //teardownLightpath(event.subject()); 148 //teardownLightpath(event.subject());
142 break; 149 break;
...@@ -162,6 +169,7 @@ public class OpticalPathProvisioner { ...@@ -162,6 +169,7 @@ public class OpticalPathProvisioner {
162 169
163 /** 170 /**
164 * Registers an intent from src to dst. 171 * Registers an intent from src to dst.
172 + *
165 * @param src source point 173 * @param src source point
166 * @param dst destination point 174 * @param dst destination point
167 * @param intent intent to be registered 175 * @param intent intent to be registered
...@@ -242,78 +250,124 @@ public class OpticalPathProvisioner { ...@@ -242,78 +250,124 @@ public class OpticalPathProvisioner {
242 } 250 }
243 } 251 }
244 252
253 + /**
254 + * Returns list of cross connection points of missing optical path sections.
255 + *
256 + * Scans the given multi-layer path and looks for sections that use cross connect links.
257 + * The ingress and egress points in the optical layer are returned in a list.
258 + *
259 + * @param path the multi-layer path
260 + * @return list of cross connection points on the optical layer
261 + */
262 + private List<ConnectPoint> getCrossConnectPoints(Path path) {
263 + boolean scanning = false;
264 + List<ConnectPoint> connectPoints = new LinkedList<ConnectPoint>();
265 +
266 + for (Link link : path.links()) {
267 + if (!isCrossConnectLink(link)) {
268 + continue;
269 + }
270 +
271 + if (scanning) {
272 + connectPoints.add(checkNotNull(link.src()));
273 + scanning = false;
274 + } else {
275 + connectPoints.add(checkNotNull(link.dst()));
276 + scanning = true;
277 + }
278 + }
279 +
280 + return connectPoints;
281 + }
282 +
283 + /**
284 + * Checks availability of cross connect points by verifying T port status.
285 + * TODO: refactor after rewriting OpticalConnectivityIntentCompiler
286 + *
287 + * @param crossConnectPoints list of cross connection points
288 + * @return true if all cross connect points are available, false otherwise
289 + */
290 + private boolean checkCrossConnectPoints(List<ConnectPoint> crossConnectPoints) {
291 + checkArgument(crossConnectPoints.size() % 2 == 0);
292 +
293 + Iterator<ConnectPoint> itr = crossConnectPoints.iterator();
294 +
295 + while (itr.hasNext()) {
296 + // checkArgument at start ensures we'll always have pairs of connect points
297 + ConnectPoint src = itr.next();
298 + ConnectPoint dst = itr.next();
299 +
300 + if (inStatusTportMap.get(src) != null || outStatusTportMap.get(dst) != null) {
301 + return false;
302 + }
303 + }
304 +
305 + return true;
306 + }
307 +
308 + /**
309 + * Scans the list of cross connection points and returns a list of optical connectivity intents
310 + * in both directions.
311 + *
312 + * @param crossConnectPoints list of cross connection points
313 + * @return list of optical connectivity intents
314 + */
315 + private List<Intent> getIntents(List<ConnectPoint> crossConnectPoints) {
316 + checkArgument(crossConnectPoints.size() % 2 == 0);
317 +
318 + List<Intent> intents = new LinkedList<Intent>();
319 + Iterator<ConnectPoint> itr = crossConnectPoints.iterator();
320 +
321 + while (itr.hasNext()) {
322 + // checkArgument at start ensures we'll always have pairs of connect points
323 + ConnectPoint src = itr.next();
324 + ConnectPoint dst = itr.next();
325 +
326 + // TODO: should have option for bidirectional OpticalConnectivityIntent
327 + Intent opticalIntent = OpticalConnectivityIntent.builder()
328 + .appId(appId)
329 + .src(src)
330 + .dst(dst)
331 + .build();
332 + Intent opticalIntentRev = OpticalConnectivityIntent.builder()
333 + .appId(appId)
334 + .src(dst)
335 + .dst(src)
336 + .build();
337 + intents.add(opticalIntent);
338 + intents.add(opticalIntentRev);
339 + }
340 +
341 + return intents;
342 + }
343 +
245 private List<Intent> getOpticalPath(ConnectPoint ingress, ConnectPoint egress) { 344 private List<Intent> getOpticalPath(ConnectPoint ingress, ConnectPoint egress) {
246 Set<Path> paths = pathService.getPaths(ingress.deviceId(), 345 Set<Path> paths = pathService.getPaths(ingress.deviceId(),
247 - egress.deviceId(), 346 + egress.deviceId(),
248 - new OpticalLinkWeight()); 347 + new OpticalLinkWeight());
249 348
250 if (paths.isEmpty()) { 349 if (paths.isEmpty()) {
251 - return Lists.newArrayList(); 350 + return Collections.emptyList();
252 } 351 }
253 352
254 List<Intent> connectionList = Lists.newArrayList(); 353 List<Intent> connectionList = Lists.newArrayList();
255 354
355 + // Iterate over all paths until a suitable one has been found
256 Iterator<Path> itrPath = paths.iterator(); 356 Iterator<Path> itrPath = paths.iterator();
257 while (itrPath.hasNext()) { 357 while (itrPath.hasNext()) {
258 - boolean usedTportFound = false;
259 Path nextPath = itrPath.next(); 358 Path nextPath = itrPath.next();
260 - log.info(nextPath.links().toString()); // TODO drop log level
261 -
262 - Iterator<Link> itrLink = nextPath.links().iterator();
263 - while (itrLink.hasNext()) {
264 - ConnectPoint srcWdmPoint, dstWdmPoint;
265 - Link link1 = itrLink.next();
266 - if (!isOpticalLink(link1)) {
267 - continue;
268 - } else {
269 - srcWdmPoint = link1.dst();
270 - dstWdmPoint = srcWdmPoint;
271 - }
272 359
273 - while (itrLink.hasNext()) { 360 + List<ConnectPoint> crossConnectPoints = getCrossConnectPoints(nextPath);
274 - Link link2 = itrLink.next();
275 - if (isOpticalLink(link2)) {
276 - dstWdmPoint = link2.src();
277 - } else {
278 - break;
279 - }
280 - }
281 -
282 - if (inStatusTportMap.get(srcWdmPoint) != null ||
283 - outStatusTportMap.get(dstWdmPoint) != null) {
284 - usedTportFound = true;
285 - // log.info("used ConnectPoint {} to {} were found", srcWdmPoint, dstWdmPoint);
286 - break;
287 - }
288 361
289 - Intent opticalIntent = OpticalConnectivityIntent.builder() 362 + // Skip to next path if not all connect points are available
290 - .appId(appId) 363 + if (!checkCrossConnectPoints(crossConnectPoints)) {
291 - .src(srcWdmPoint) 364 + continue;
292 - .dst(dstWdmPoint)
293 - .build();
294 - Intent opticalIntent2 = OpticalConnectivityIntent.builder()
295 - .appId(appId)
296 - .src(dstWdmPoint)
297 - .dst(srcWdmPoint)
298 - .build();
299 - log.info("Creating optical intent from {} to {}", srcWdmPoint, dstWdmPoint);
300 - log.info("Creating optical intent from {} to {}", dstWdmPoint, srcWdmPoint);
301 - connectionList.add(opticalIntent);
302 - connectionList.add(opticalIntent2);
303 -
304 - break;
305 - }
306 -
307 - if (!usedTportFound) {
308 - break;
309 - } else {
310 - // reset the connection list
311 - connectionList = Lists.newArrayList();
312 } 365 }
313 366
367 + return getIntents(crossConnectPoints);
314 } 368 }
315 369
316 - return connectionList; 370 + return Collections.emptyList();
317 } 371 }
318 372
319 private void teardownLightpath(Intent intent) { 373 private void teardownLightpath(Intent intent) {
...@@ -330,26 +384,45 @@ public class OpticalPathProvisioner { ...@@ -330,26 +384,45 @@ public class OpticalPathProvisioner {
330 384
331 } 385 }
332 386
333 - private static boolean isOpticalLink(Link link) { 387 + /**
334 - boolean isOptical = false; 388 + * Verifies if given link is cross-connect between packet and optical layer.
335 - Link.Type lt = link.type(); 389 + *
336 - if (lt == Link.Type.OPTICAL) { 390 + * @param link the link
337 - isOptical = true; 391 + * @return true if the link is a cross-connect link
392 + */
393 + public static boolean isCrossConnectLink(Link link) {
394 + if (link.type() != Link.Type.OPTICAL) {
395 + return false;
338 } 396 }
339 - return isOptical; 397 +
398 + checkNotNull(link.annotations());
399 + checkNotNull(link.annotations().value("optical.type"));
400 +
401 + if (link.annotations().value("optical.type").equals("cross-connect")) {
402 + return true;
403 + }
404 +
405 + return false;
340 } 406 }
341 407
408 + /**
409 + * Link weight function that emphasizes re-use of packet links.
410 + */
342 private static class OpticalLinkWeight implements LinkWeight { 411 private static class OpticalLinkWeight implements LinkWeight {
343 @Override 412 @Override
344 public double weight(TopologyEdge edge) { 413 public double weight(TopologyEdge edge) {
414 + // Ignore inactive links
345 if (edge.link().state() == Link.State.INACTIVE) { 415 if (edge.link().state() == Link.State.INACTIVE) {
346 - return -1; // ignore inactive links 416 + return -1;
347 } 417 }
348 - if (isOpticalLink(edge.link())) { 418 +
349 - return 1000; // optical links 419 + // Transport links have highest weight
350 - } else { 420 + if (edge.link().type() == Link.Type.OPTICAL) {
351 - return 1; // packet links 421 + return 1000;
352 } 422 }
423 +
424 + // Packet links
425 + return 1;
353 } 426 }
354 } 427 }
355 428
......
...@@ -56,6 +56,7 @@ import re ...@@ -56,6 +56,7 @@ import re
56 import json 56 import json
57 import os 57 import os
58 from time import sleep 58 from time import sleep
59 +import urllib2
59 60
60 from mininet.node import Switch, RemoteController 61 from mininet.node import Switch, RemoteController
61 from mininet.topo import Topo 62 from mininet.topo import Topo
...@@ -65,6 +66,10 @@ from mininet.log import setLogLevel, info, error, warn ...@@ -65,6 +66,10 @@ from mininet.log import setLogLevel, info, error, warn
65 from mininet.link import Link, Intf 66 from mininet.link import Link, Intf
66 from mininet.cli import CLI 67 from mininet.cli import CLI
67 68
69 +# Sleep time and timeout values in seconds
70 +SLEEP_TIME = .5
71 +TIMEOUT = 60
72 +
68 class OpticalSwitch(Switch): 73 class OpticalSwitch(Switch):
69 """ 74 """
70 For now, same as Switch class. 75 For now, same as Switch class.
...@@ -414,17 +419,37 @@ class LINCSwitch(OpticalSwitch): ...@@ -414,17 +419,37 @@ class LINCSwitch(OpticalSwitch):
414 intf2 = intfList[ 0 ] 419 intf2 = intfList[ 0 ]
415 intf.node.attach(LINCSwitch.findTap(intf2.node, intf2.node.ports[ intf2 ])) 420 intf.node.attach(LINCSwitch.findTap(intf2.node, intf2.node.ports[ intf2 ]))
416 421
417 - info('*** Press ENTER to push Topology.json to onos...\n') 422 + info('*** Waiting for all devices to be available in ONOS...\n')
418 - raw_input() # FIXME... we should eventually remove this 423 + url = 'http://%s:8181/onos/v1/devices' % LINCSwitch.controllers[0].ip
424 + time = 0
425 + while True:
426 + response = json.load(urllib2.urlopen(url))
427 + devs = response.get('devices')
428 +
429 + # Wait for all devices to be registered & available
430 + if (len(devices) == len(devs)):
431 + for d in devs:
432 + if not d['available']:
433 + continue
434 + break
435 +
436 + if (time >= TIMEOUT):
437 + error('***ERROR: ONOS did not register devices within %s seconds\n' % TIMEOUT)
438 + break
439 +
440 + time += SLEEP_TIME
441 + sleep(SLEEP_TIME)
442 +
419 info('*** Pushing Topology.json to ONOS\n') 443 info('*** Pushing Topology.json to ONOS\n')
420 output = quietRun('%s/tools/test/bin/onos-topo-cfg %s Topology.json' % (LINCSwitch.onosDir, LINCSwitch.controllers[ 0 ].ip), shell=True) 444 output = quietRun('%s/tools/test/bin/onos-topo-cfg %s Topology.json' % (LINCSwitch.onosDir, LINCSwitch.controllers[ 0 ].ip), shell=True)
445 +
421 # successful output contains the two characters '{}' 446 # successful output contains the two characters '{}'
422 # if there is more output than this, there is an issue 447 # if there is more output than this, there is an issue
423 if output.strip('{}'): 448 if output.strip('{}'):
424 - warn('***WARNING: Could not push topology file to ONOS: %s' % output) 449 + warn('***WARNING: Could not push topology file to ONOS: %s\n' % output)
425 450
426 @staticmethod 451 @staticmethod
427 - def waitStarted(net, timeout=None): 452 + def waitStarted(net, timeout=TIMEOUT):
428 "wait until all tap interfaces are available" 453 "wait until all tap interfaces are available"
429 tapCount = 0 454 tapCount = 0
430 time = 0 455 time = 0
...@@ -437,11 +462,11 @@ class LINCSwitch(OpticalSwitch): ...@@ -437,11 +462,11 @@ class LINCSwitch(OpticalSwitch):
437 if str(tapCount) == quietRun('ip addr | grep tap | wc -l', shell=True).strip('\n'): 462 if str(tapCount) == quietRun('ip addr | grep tap | wc -l', shell=True).strip('\n'):
438 return True 463 return True
439 if timeout: 464 if timeout:
440 - if time >= timeout: 465 + if time >= TIMEOUT:
441 - error('***ERROR: Linc OE did not start within %s seconds' % timeout) 466 + error('***ERROR: LINC OE did not start within %s seconds\n' % TIMEOUT)
442 return False 467 return False
443 - time += .5 468 + time += SLEEP_TIME
444 - sleep(.5) 469 + sleep(SLEEP_TIME)
445 470
446 @staticmethod 471 @staticmethod
447 def shutdownOE(): 472 def shutdownOE():
......