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
245 - private List<Intent> getOpticalPath(ConnectPoint ingress, ConnectPoint egress) { 253 + /**
246 - Set<Path> paths = pathService.getPaths(ingress.deviceId(), 254 + * Returns list of cross connection points of missing optical path sections.
247 - egress.deviceId(), 255 + *
248 - new OpticalLinkWeight()); 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>();
249 265
250 - if (paths.isEmpty()) { 266 + for (Link link : path.links()) {
251 - return Lists.newArrayList(); 267 + if (!isCrossConnectLink(link)) {
268 + continue;
252 } 269 }
253 270
254 - List<Intent> connectionList = Lists.newArrayList(); 271 + if (scanning) {
255 - 272 + connectPoints.add(checkNotNull(link.src()));
256 - Iterator<Path> itrPath = paths.iterator(); 273 + scanning = false;
257 - while (itrPath.hasNext()) {
258 - boolean usedTportFound = false;
259 - 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 { 274 } else {
269 - srcWdmPoint = link1.dst(); 275 + connectPoints.add(checkNotNull(link.dst()));
270 - dstWdmPoint = srcWdmPoint; 276 + scanning = true;
277 + }
271 } 278 }
272 279
273 - while (itrLink.hasNext()) { 280 + return connectPoints;
274 - Link link2 = itrLink.next(); 281 + }
275 - if (isOpticalLink(link2)) { 282 +
276 - dstWdmPoint = link2.src(); 283 + /**
277 - } else { 284 + * Checks availability of cross connect points by verifying T port status.
278 - break; 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;
279 } 302 }
280 } 303 }
281 304
282 - if (inStatusTportMap.get(srcWdmPoint) != null || 305 + return true;
283 - outStatusTportMap.get(dstWdmPoint) != null) {
284 - usedTportFound = true;
285 - // log.info("used ConnectPoint {} to {} were found", srcWdmPoint, dstWdmPoint);
286 - break;
287 } 306 }
288 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
289 Intent opticalIntent = OpticalConnectivityIntent.builder() 327 Intent opticalIntent = OpticalConnectivityIntent.builder()
290 .appId(appId) 328 .appId(appId)
291 - .src(srcWdmPoint) 329 + .src(src)
292 - .dst(dstWdmPoint) 330 + .dst(dst)
293 .build(); 331 .build();
294 - Intent opticalIntent2 = OpticalConnectivityIntent.builder() 332 + Intent opticalIntentRev = OpticalConnectivityIntent.builder()
295 .appId(appId) 333 .appId(appId)
296 - .src(dstWdmPoint) 334 + .src(dst)
297 - .dst(srcWdmPoint) 335 + .dst(src)
298 .build(); 336 .build();
299 - log.info("Creating optical intent from {} to {}", srcWdmPoint, dstWdmPoint); 337 + intents.add(opticalIntent);
300 - log.info("Creating optical intent from {} to {}", dstWdmPoint, srcWdmPoint); 338 + intents.add(opticalIntentRev);
301 - connectionList.add(opticalIntent); 339 + }
302 - connectionList.add(opticalIntent2);
303 340
304 - break; 341 + return intents;
305 } 342 }
306 343
307 - if (!usedTportFound) { 344 + private List<Intent> getOpticalPath(ConnectPoint ingress, ConnectPoint egress) {
308 - break; 345 + Set<Path> paths = pathService.getPaths(ingress.deviceId(),
309 - } else { 346 + egress.deviceId(),
310 - // reset the connection list 347 + new OpticalLinkWeight());
311 - connectionList = Lists.newArrayList(); 348 +
349 + if (paths.isEmpty()) {
350 + return Collections.emptyList();
312 } 351 }
313 352
353 + List<Intent> connectionList = Lists.newArrayList();
354 +
355 + // Iterate over all paths until a suitable one has been found
356 + Iterator<Path> itrPath = paths.iterator();
357 + while (itrPath.hasNext()) {
358 + Path nextPath = itrPath.next();
359 +
360 + List<ConnectPoint> crossConnectPoints = getCrossConnectPoints(nextPath);
361 +
362 + // Skip to next path if not all connect points are available
363 + if (!checkCrossConnectPoints(crossConnectPoints)) {
364 + continue;
365 + }
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;
396 + }
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;
338 } 403 }
339 - return isOptical; 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():
......