blob: dc41ee9cac4d8d66c2e5ae4b0e8d9d5d52fa2bdb [file] [log] [blame]
Thomas Vachuska781d18b2014-10-27 10:31:25 -07001/*
Ray Milkey34c95902015-04-15 09:47:53 -07002 * Copyright 2014-2015 Open Networking Laboratory
Thomas Vachuska781d18b2014-10-27 10:31:25 -07003 *
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07004 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
Thomas Vachuska781d18b2014-10-27 10:31:25 -07007 *
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07008 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
Thomas Vachuska781d18b2014-10-27 10:31:25 -070015 */
Brian O'Connorabafb502014-12-02 22:26:20 -080016package org.onosproject.optical;
weibitf32383b2014-10-22 10:17:31 -070017
weibitf32383b2014-10-22 10:17:31 -070018import org.apache.felix.scr.annotations.Activate;
19import org.apache.felix.scr.annotations.Component;
Marc De Leenheer16f857b2015-05-05 20:50:24 -070020import org.apache.felix.scr.annotations.Deactivate;
weibitf32383b2014-10-22 10:17:31 -070021import org.apache.felix.scr.annotations.Reference;
22import org.apache.felix.scr.annotations.ReferenceCardinality;
Marc De Leenheer8b3e80b2015-03-06 14:27:03 -080023import org.onosproject.cluster.ClusterService;
24import org.onosproject.cluster.NodeId;
Brian O'Connorabafb502014-12-02 22:26:20 -080025import org.onosproject.core.ApplicationId;
26import org.onosproject.core.CoreService;
Marc De Leenheer8b3e80b2015-03-06 14:27:03 -080027import org.onosproject.mastership.MastershipService;
Brian O'Connorabafb502014-12-02 22:26:20 -080028import org.onosproject.net.ConnectPoint;
Marc De Leenheer1afa2a02015-05-13 09:18:07 -070029import org.onosproject.net.Device;
Brian O'Connorabafb502014-12-02 22:26:20 -080030import org.onosproject.net.Host;
31import org.onosproject.net.Link;
Marc De Leenheer8c2caac2015-05-28 16:37:33 -070032import org.onosproject.net.OchPort;
Marc De Leenheer1afa2a02015-05-13 09:18:07 -070033import org.onosproject.net.OduCltPort;
Marc De Leenheer8c2caac2015-05-28 16:37:33 -070034import org.onosproject.net.OduSignalType;
Brian O'Connorabafb502014-12-02 22:26:20 -080035import org.onosproject.net.Path;
Marc De Leenheer1afa2a02015-05-13 09:18:07 -070036import org.onosproject.net.Port;
37import org.onosproject.net.device.DeviceService;
Brian O'Connorabafb502014-12-02 22:26:20 -080038import org.onosproject.net.host.HostService;
39import org.onosproject.net.intent.HostToHostIntent;
40import org.onosproject.net.intent.Intent;
41import org.onosproject.net.intent.IntentEvent;
42import org.onosproject.net.intent.IntentListener;
Brian O'Connorabafb502014-12-02 22:26:20 -080043import org.onosproject.net.intent.IntentService;
44import org.onosproject.net.intent.IntentState;
Marc De Leenheer8c2caac2015-05-28 16:37:33 -070045import org.onosproject.net.intent.OpticalCircuitIntent;
Brian O'Connorabafb502014-12-02 22:26:20 -080046import org.onosproject.net.intent.OpticalConnectivityIntent;
47import org.onosproject.net.intent.PointToPointIntent;
Brian O'Connor6de2e202015-05-21 14:30:41 -070048import org.onosproject.net.resource.device.DeviceResourceService;
49import org.onosproject.net.resource.link.LinkResourceService;
Brian O'Connorabafb502014-12-02 22:26:20 -080050import org.onosproject.net.topology.LinkWeight;
51import org.onosproject.net.topology.PathService;
52import org.onosproject.net.topology.TopologyEdge;
weibitf32383b2014-10-22 10:17:31 -070053import org.slf4j.Logger;
54import org.slf4j.LoggerFactory;
55
Marc De Leenheer16f857b2015-05-05 20:50:24 -070056import java.util.Collections;
Brian O'Connor772852a2014-11-17 15:51:19 -080057import java.util.Iterator;
Marc De Leenheer16f857b2015-05-05 20:50:24 -070058import java.util.LinkedList;
Brian O'Connor772852a2014-11-17 15:51:19 -080059import java.util.List;
Brian O'Connorc7bdd8c2014-12-08 01:29:53 -080060import java.util.Map;
Brian O'Connor772852a2014-11-17 15:51:19 -080061import java.util.Set;
Brian O'Connorc7bdd8c2014-12-08 01:29:53 -080062import java.util.concurrent.ConcurrentHashMap;
63
Marc De Leenheer16f857b2015-05-05 20:50:24 -070064import static com.google.common.base.Preconditions.checkArgument;
Praseed Balakrishnanc0029652014-11-14 13:38:49 -080065
Marc De Leenheer16f857b2015-05-05 20:50:24 -070066import static com.google.common.base.Preconditions.checkNotNull;
67
weibitf32383b2014-10-22 10:17:31 -070068/**
Marc De Leenheer16f857b2015-05-05 20:50:24 -070069 * OpticalPathProvisioner listens for event notifications from the Intent F/W.
weibitf32383b2014-10-22 10:17:31 -070070 * It generates one or more opticalConnectivityIntent(s) and submits (or withdraws) to Intent F/W
71 * for adding/releasing capacity at the packet layer.
weibitf32383b2014-10-22 10:17:31 -070072 */
73
74@Component(immediate = true)
75public class OpticalPathProvisioner {
76
77 protected static final Logger log = LoggerFactory
78 .getLogger(OpticalPathProvisioner.class);
79
80 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
81 private IntentService intentService;
82
83 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Brian O'Connor772852a2014-11-17 15:51:19 -080084 protected PathService pathService;
weibitf32383b2014-10-22 10:17:31 -070085
86 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
87 protected CoreService coreService;
88
weibit7e583462014-10-23 10:14:05 -070089 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Praseed Balakrishnanc0029652014-11-14 13:38:49 -080090 protected HostService hostService;
91
Marc De Leenheer8b3e80b2015-03-06 14:27:03 -080092 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
93 protected MastershipService mastershipService;
94
95 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
96 protected ClusterService clusterService;
97
Marc De Leenheer1afa2a02015-05-13 09:18:07 -070098 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Marc De Leenheer1afa2a02015-05-13 09:18:07 -070099 protected DeviceService deviceService;
100
101 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
102 protected DeviceResourceService deviceResourceService;
103
104 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
105 protected LinkResourceService linkResourceService;
106
107 private ApplicationId appId;
Brian O'Connorc7bdd8c2014-12-08 01:29:53 -0800108
Marc De Leenheer16f857b2015-05-05 20:50:24 -0700109 private final Map<ConnectPoint, Map<ConnectPoint, Intent>> intentMap =
Brian O'Connorc7bdd8c2014-12-08 01:29:53 -0800110 new ConcurrentHashMap<>();
weibitf32383b2014-10-22 10:17:31 -0700111
112 private final InternalOpticalPathProvisioner pathProvisioner = new InternalOpticalPathProvisioner();
113
114 @Activate
115 protected void activate() {
116 intentService.addListener(pathProvisioner);
Brian O'Connorabafb502014-12-02 22:26:20 -0800117 appId = coreService.registerApplication("org.onosproject.optical");
Marc De Leenheer1afa2a02015-05-13 09:18:07 -0700118 initOpticalPorts();
Marc De Leenheer16f857b2015-05-05 20:50:24 -0700119 log.info("Started");
120 }
121
122 @Deactivate
123 protected void deactivate() {
124 intentService.removeListener(pathProvisioner);
125 log.info("Stopped");
weibitf32383b2014-10-22 10:17:31 -0700126 }
127
Marc De Leenheer1afa2a02015-05-13 09:18:07 -0700128 /**
129 * Initialize availability of optical ports.
130 */
131 private void initOpticalPorts() {
132 // TODO: check for existing optical intents
133 return;
Brian O'Connorc7bdd8c2014-12-08 01:29:53 -0800134 }
135
weibitf32383b2014-10-22 10:17:31 -0700136 public class InternalOpticalPathProvisioner implements IntentListener {
137 @Override
138 public void event(IntentEvent event) {
139 switch (event.type()) {
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800140 case INSTALL_REQ:
weibitf32383b2014-10-22 10:17:31 -0700141 break;
142 case INSTALLED:
Marc De Leenheer1afa2a02015-05-13 09:18:07 -0700143 // track h2h & p2p intents using our connectivity
weibitf32383b2014-10-22 10:17:31 -0700144 break;
145 case FAILED:
Marc De Leenheer16f857b2015-05-05 20:50:24 -0700146 log.info("Intent {} failed, calling optical path provisioning app.", event.subject());
Brian O'Connor772852a2014-11-17 15:51:19 -0800147 setupLightpath(event.subject());
weibitf32383b2014-10-22 10:17:31 -0700148 break;
149 case WITHDRAWN:
Marc De Leenheer16f857b2015-05-05 20:50:24 -0700150 log.info("Intent {} withdrawn.", event.subject());
Marc De Leenheer1afa2a02015-05-13 09:18:07 -0700151 withdrawIntent(event.subject());
weibitf32383b2014-10-22 10:17:31 -0700152 break;
153 default:
154 break;
155 }
156 }
157
Brian O'Connorc7bdd8c2014-12-08 01:29:53 -0800158 /**
159 * Registers an intent from src to dst.
Marc De Leenheer16f857b2015-05-05 20:50:24 -0700160 *
Brian O'Connorc7bdd8c2014-12-08 01:29:53 -0800161 * @param src source point
162 * @param dst destination point
163 * @param intent intent to be registered
164 * @return true if intent has not been previously added, false otherwise
165 */
166 private boolean addIntent(ConnectPoint src, ConnectPoint dst, Intent intent) {
167 Map<ConnectPoint, Intent> srcMap = intentMap.get(src);
168 if (srcMap == null) {
169 srcMap = new ConcurrentHashMap<>();
170 intentMap.put(src, srcMap);
171 }
172 if (srcMap.containsKey(dst)) {
173 return false;
174 } else {
175 srcMap.put(dst, intent);
176 return true;
177 }
178 }
179
Brian O'Connor772852a2014-11-17 15:51:19 -0800180 private void setupLightpath(Intent intent) {
Marc De Leenheer1afa2a02015-05-13 09:18:07 -0700181 checkNotNull(intent);
182
Brian O'Connorc7bdd8c2014-12-08 01:29:53 -0800183 // TODO change the coordination approach between packet intents and optical intents
184 // Low speed LLDP may cause multiple calls which are not expected
185
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800186 if (!IntentState.FAILED.equals(intentService.getIntentState(intent.key()))) {
Marc De Leenheer8b3e80b2015-03-06 14:27:03 -0800187 return;
Marc De Leenheerb473b9d2015-02-06 15:21:03 -0800188 }
Brian O'Connorc7bdd8c2014-12-08 01:29:53 -0800189
Marc De Leenheer1afa2a02015-05-13 09:18:07 -0700190 // Get source and destination based on intent type
191 ConnectPoint src;
192 ConnectPoint dst;
Brian O'Connor772852a2014-11-17 15:51:19 -0800193 if (intent instanceof HostToHostIntent) {
194 HostToHostIntent hostToHostIntent = (HostToHostIntent) intent;
Marc De Leenheer8b3e80b2015-03-06 14:27:03 -0800195
Brian O'Connor772852a2014-11-17 15:51:19 -0800196 Host one = hostService.getHost(hostToHostIntent.one());
197 Host two = hostService.getHost(hostToHostIntent.two());
Marc De Leenheer8b3e80b2015-03-06 14:27:03 -0800198
Marc De Leenheer1afa2a02015-05-13 09:18:07 -0700199 checkNotNull(one);
200 checkNotNull(two);
Marc De Leenheer8b3e80b2015-03-06 14:27:03 -0800201
Marc De Leenheer1afa2a02015-05-13 09:18:07 -0700202 src = one.location();
203 dst = two.location();
Brian O'Connor772852a2014-11-17 15:51:19 -0800204 } else if (intent instanceof PointToPointIntent) {
205 PointToPointIntent p2pIntent = (PointToPointIntent) intent;
Marc De Leenheer8b3e80b2015-03-06 14:27:03 -0800206
Marc De Leenheer1afa2a02015-05-13 09:18:07 -0700207 src = p2pIntent.ingressPoint();
208 dst = p2pIntent.egressPoint();
Brian O'Connor772852a2014-11-17 15:51:19 -0800209 } else {
Marc De Leenheer1afa2a02015-05-13 09:18:07 -0700210 log.error("Unsupported intent type: {}", intent.getClass());
211 return;
Brian O'Connor772852a2014-11-17 15:51:19 -0800212 }
Praseed Balakrishnanc0029652014-11-14 13:38:49 -0800213
Marc De Leenheer1afa2a02015-05-13 09:18:07 -0700214 if (src == null || dst == null) {
215 return;
216 }
217
218 // Ignore if we're not the master for the intent's origin device
219 NodeId localNode = clusterService.getLocalNode().id();
220 NodeId sourceMaster = mastershipService.getMasterFor(src.deviceId());
221 if (!localNode.equals(sourceMaster)) {
222 return;
223 }
224
225 // Generate optical connectivity intents
Sho SHIMIZUfb446fe2015-05-21 14:45:23 -0700226 List<Intent> intents = getOpticalIntents(src, dst);
Marc De Leenheer1afa2a02015-05-13 09:18:07 -0700227
228 // Submit the intents
Brian O'Connor772852a2014-11-17 15:51:19 -0800229 for (Intent i : intents) {
Marc De Leenheer1afa2a02015-05-13 09:18:07 -0700230 intentService.submit(i);
Sho SHIMIZUb5d38412015-05-21 11:12:58 -0700231 log.debug("Submitted an intent: {}", i);
Brian O'Connor772852a2014-11-17 15:51:19 -0800232 }
weibit7e583462014-10-23 10:14:05 -0700233 }
234
Marc De Leenheer16f857b2015-05-05 20:50:24 -0700235 /**
236 * Returns list of cross connection points of missing optical path sections.
237 *
238 * Scans the given multi-layer path and looks for sections that use cross connect links.
239 * The ingress and egress points in the optical layer are returned in a list.
240 *
241 * @param path the multi-layer path
242 * @return list of cross connection points on the optical layer
243 */
244 private List<ConnectPoint> getCrossConnectPoints(Path path) {
245 boolean scanning = false;
246 List<ConnectPoint> connectPoints = new LinkedList<ConnectPoint>();
247
248 for (Link link : path.links()) {
249 if (!isCrossConnectLink(link)) {
250 continue;
251 }
252
253 if (scanning) {
254 connectPoints.add(checkNotNull(link.src()));
255 scanning = false;
256 } else {
257 connectPoints.add(checkNotNull(link.dst()));
258 scanning = true;
259 }
260 }
261
262 return connectPoints;
263 }
264
265 /**
Marc De Leenheer1afa2a02015-05-13 09:18:07 -0700266 * Checks if cross connect points are of same type.
Marc De Leenheer16f857b2015-05-05 20:50:24 -0700267 *
268 * @param crossConnectPoints list of cross connection points
Marc De Leenheer1afa2a02015-05-13 09:18:07 -0700269 * @return true if cross connect point pairs are of same type, false otherwise
Marc De Leenheer16f857b2015-05-05 20:50:24 -0700270 */
271 private boolean checkCrossConnectPoints(List<ConnectPoint> crossConnectPoints) {
272 checkArgument(crossConnectPoints.size() % 2 == 0);
273
274 Iterator<ConnectPoint> itr = crossConnectPoints.iterator();
275
276 while (itr.hasNext()) {
277 // checkArgument at start ensures we'll always have pairs of connect points
278 ConnectPoint src = itr.next();
279 ConnectPoint dst = itr.next();
280
Marc De Leenheer1afa2a02015-05-13 09:18:07 -0700281 Device.Type srcType = deviceService.getDevice(src.deviceId()).type();
282 Device.Type dstType = deviceService.getDevice(dst.deviceId()).type();
283
284 // Only support connections between identical port types
285 if (srcType != dstType) {
286 log.warn("Unsupported mix of cross connect points");
Marc De Leenheer16f857b2015-05-05 20:50:24 -0700287 return false;
288 }
289 }
290
291 return true;
292 }
293
294 /**
Marc De Leenheer1afa2a02015-05-13 09:18:07 -0700295 * Scans the list of cross connection points and returns a list of optical connectivity intents.
Marc De Leenheer16f857b2015-05-05 20:50:24 -0700296 *
297 * @param crossConnectPoints list of cross connection points
298 * @return list of optical connectivity intents
299 */
300 private List<Intent> getIntents(List<ConnectPoint> crossConnectPoints) {
301 checkArgument(crossConnectPoints.size() % 2 == 0);
302
Sho SHIMIZU79945e82015-05-20 17:20:47 -0700303 List<Intent> intents = new LinkedList<>();
Marc De Leenheer16f857b2015-05-05 20:50:24 -0700304 Iterator<ConnectPoint> itr = crossConnectPoints.iterator();
305
306 while (itr.hasNext()) {
307 // checkArgument at start ensures we'll always have pairs of connect points
308 ConnectPoint src = itr.next();
309 ConnectPoint dst = itr.next();
310
Marc De Leenheer1afa2a02015-05-13 09:18:07 -0700311 Port srcPort = deviceService.getPort(src.deviceId(), src.port());
312 Port dstPort = deviceService.getPort(dst.deviceId(), dst.port());
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700313
Marc De Leenheer1afa2a02015-05-13 09:18:07 -0700314 if (srcPort instanceof OduCltPort && dstPort instanceof OduCltPort) {
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700315 // TODO: Check availability of ports
316
317 // Create OTN circuit
318 Intent circuitIntent = OpticalCircuitIntent.builder()
319 .appId(appId)
320 .src(src)
321 .dst(dst)
322 .signalType(OduCltPort.SignalType.CLT_10GBE)
323 .build();
324 intents.add(circuitIntent);
Marc De Leenheer1afa2a02015-05-13 09:18:07 -0700325 continue;
Marc De Leenheer8c2caac2015-05-28 16:37:33 -0700326 } else if (srcPort instanceof OchPort && dstPort instanceof OchPort) {
327 // Create lightpath
328 // FIXME: hardcoded ODU signal type
329 Intent opticalIntent = OpticalConnectivityIntent.builder()
330 .appId(appId)
331 .src(src)
332 .dst(dst)
333 .signalType(OduSignalType.ODU4)
334 .build();
335 intents.add(opticalIntent);
336 continue;
337 } else {
338 log.warn("Unsupported cross connect point types {} {}", srcPort.type(), dstPort.type());
339 return Collections.emptyList();
Marc De Leenheer1afa2a02015-05-13 09:18:07 -0700340 }
Marc De Leenheer16f857b2015-05-05 20:50:24 -0700341 }
342
343 return intents;
344 }
345
Marc De Leenheer1afa2a02015-05-13 09:18:07 -0700346 /**
347 * Returns list of optical connectivity intents needed to create connectivity
348 * between ingress and egress.
349 *
350 * @param ingress the ingress connect point
351 * @param egress the egress connect point
352 * @return list of optical connectivity intents, empty list if no path was found
353 */
354 private List<Intent> getOpticalIntents(ConnectPoint ingress, ConnectPoint egress) {
Brian O'Connor772852a2014-11-17 15:51:19 -0800355 Set<Path> paths = pathService.getPaths(ingress.deviceId(),
Marc De Leenheer16f857b2015-05-05 20:50:24 -0700356 egress.deviceId(),
357 new OpticalLinkWeight());
Brian O'Connor772852a2014-11-17 15:51:19 -0800358
359 if (paths.isEmpty()) {
Marc De Leenheer16f857b2015-05-05 20:50:24 -0700360 return Collections.emptyList();
weibit7e583462014-10-23 10:14:05 -0700361 }
Brian O'Connor772852a2014-11-17 15:51:19 -0800362
Marc De Leenheer1afa2a02015-05-13 09:18:07 -0700363 // Search path with available cross connect points
364 for (Path path : paths) {
365 List<ConnectPoint> crossConnectPoints = getCrossConnectPoints(path);
Brian O'Connor772852a2014-11-17 15:51:19 -0800366
Marc De Leenheer1afa2a02015-05-13 09:18:07 -0700367 // Skip to next path if cross connect points are mismatched
Marc De Leenheer16f857b2015-05-05 20:50:24 -0700368 if (!checkCrossConnectPoints(crossConnectPoints)) {
369 continue;
Brian O'Connor772852a2014-11-17 15:51:19 -0800370 }
371
Marc De Leenheer16f857b2015-05-05 20:50:24 -0700372 return getIntents(crossConnectPoints);
Brian O'Connor772852a2014-11-17 15:51:19 -0800373 }
Brian O'Connorc7bdd8c2014-12-08 01:29:53 -0800374
Marc De Leenheer16f857b2015-05-05 20:50:24 -0700375 return Collections.emptyList();
Brian O'Connor772852a2014-11-17 15:51:19 -0800376 }
377
Marc De Leenheer1afa2a02015-05-13 09:18:07 -0700378 /**
379 * Link weight function that emphasizes re-use of packet links.
380 */
381 private class OpticalLinkWeight implements LinkWeight {
382 @Override
383 public double weight(TopologyEdge edge) {
384 // Ignore inactive links
385 if (edge.link().state() == Link.State.INACTIVE) {
386 return -1;
387 }
Brian O'Connorc7bdd8c2014-12-08 01:29:53 -0800388
Marc De Leenheer1afa2a02015-05-13 09:18:07 -0700389 // TODO: Ignore cross connect links with used ports
390
391 // Transport links have highest weight
392 if (edge.link().type() == Link.Type.OPTICAL) {
393 return 1000;
394 }
395
396 // Packet links
397 return 1;
Brian O'Connorc7bdd8c2014-12-08 01:29:53 -0800398 }
weibitf32383b2014-10-22 10:17:31 -0700399 }
Brian O'Connorc7bdd8c2014-12-08 01:29:53 -0800400
Marc De Leenheer1afa2a02015-05-13 09:18:07 -0700401 /**
402 * Handle withdrawn intent on each network layer.
403 *
404 * @param intent the withdrawn intent
405 */
406 private void withdrawIntent(Intent intent) {
407 if (intent instanceof OpticalConnectivityIntent) {
408 deviceResourceService.releasePorts(intent.id());
409 linkResourceService.releaseResources(linkResourceService.getAllocations(intent.id()));
410 }
411 // TODO: add other layers
412 }
Brian O'Connor772852a2014-11-17 15:51:19 -0800413 }
weibitf32383b2014-10-22 10:17:31 -0700414
Marc De Leenheer16f857b2015-05-05 20:50:24 -0700415 /**
416 * Verifies if given link is cross-connect between packet and optical layer.
417 *
418 * @param link the link
419 * @return true if the link is a cross-connect link
420 */
421 public static boolean isCrossConnectLink(Link link) {
422 if (link.type() != Link.Type.OPTICAL) {
423 return false;
Praseed Balakrishnanc0029652014-11-14 13:38:49 -0800424 }
Marc De Leenheer16f857b2015-05-05 20:50:24 -0700425
426 checkNotNull(link.annotations());
427 checkNotNull(link.annotations().value("optical.type"));
428
Sho SHIMIZU6d283122015-05-21 11:07:02 -0700429 return link.annotations().value("optical.type").equals("cross-connect");
Brian O'Connor772852a2014-11-17 15:51:19 -0800430 }
Praseed Balakrishnanc0029652014-11-14 13:38:49 -0800431
weibitf32383b2014-10-22 10:17:31 -0700432}