blob: e5e1c69c140f62e6682447fe61678f4e95200fc6 [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
Brian O'Connor772852a2014-11-17 15:51:19 -080018import com.google.common.collect.Lists;
weibitf32383b2014-10-22 10:17:31 -070019import org.apache.felix.scr.annotations.Activate;
20import org.apache.felix.scr.annotations.Component;
Marc De Leenheer16f857b2015-05-05 20:50:24 -070021import org.apache.felix.scr.annotations.Deactivate;
weibitf32383b2014-10-22 10:17:31 -070022import org.apache.felix.scr.annotations.Reference;
23import org.apache.felix.scr.annotations.ReferenceCardinality;
Marc De Leenheer8b3e80b2015-03-06 14:27:03 -080024import org.onosproject.cluster.ClusterService;
25import org.onosproject.cluster.NodeId;
Brian O'Connorabafb502014-12-02 22:26:20 -080026import org.onosproject.core.ApplicationId;
27import org.onosproject.core.CoreService;
Marc De Leenheer8b3e80b2015-03-06 14:27:03 -080028import org.onosproject.mastership.MastershipService;
Brian O'Connorabafb502014-12-02 22:26:20 -080029import org.onosproject.net.ConnectPoint;
Marc De Leenheer1afa2a02015-05-13 09:18:07 -070030import org.onosproject.net.Device;
Brian O'Connorabafb502014-12-02 22:26:20 -080031import org.onosproject.net.Host;
32import org.onosproject.net.Link;
Marc De Leenheer1afa2a02015-05-13 09:18:07 -070033import org.onosproject.net.OduCltPort;
Brian O'Connorabafb502014-12-02 22:26:20 -080034import org.onosproject.net.Path;
Marc De Leenheer1afa2a02015-05-13 09:18:07 -070035import org.onosproject.net.Port;
36import org.onosproject.net.device.DeviceService;
Brian O'Connorabafb502014-12-02 22:26:20 -080037import org.onosproject.net.host.HostService;
38import org.onosproject.net.intent.HostToHostIntent;
39import org.onosproject.net.intent.Intent;
40import org.onosproject.net.intent.IntentEvent;
41import org.onosproject.net.intent.IntentListener;
Brian O'Connorabafb502014-12-02 22:26:20 -080042import org.onosproject.net.intent.IntentService;
43import org.onosproject.net.intent.IntentState;
44import org.onosproject.net.intent.OpticalConnectivityIntent;
45import org.onosproject.net.intent.PointToPointIntent;
Brian O'Connor6de2e202015-05-21 14:30:41 -070046import org.onosproject.net.resource.device.DeviceResourceService;
47import org.onosproject.net.resource.link.LinkResourceService;
Brian O'Connorabafb502014-12-02 22:26:20 -080048import org.onosproject.net.topology.LinkWeight;
49import org.onosproject.net.topology.PathService;
50import org.onosproject.net.topology.TopologyEdge;
weibitf32383b2014-10-22 10:17:31 -070051import org.slf4j.Logger;
52import org.slf4j.LoggerFactory;
53
Marc De Leenheer16f857b2015-05-05 20:50:24 -070054import java.util.Collections;
Brian O'Connor772852a2014-11-17 15:51:19 -080055import java.util.Iterator;
Marc De Leenheer16f857b2015-05-05 20:50:24 -070056import java.util.LinkedList;
Brian O'Connor772852a2014-11-17 15:51:19 -080057import java.util.List;
Brian O'Connorc7bdd8c2014-12-08 01:29:53 -080058import java.util.Map;
Brian O'Connor772852a2014-11-17 15:51:19 -080059import java.util.Set;
Brian O'Connorc7bdd8c2014-12-08 01:29:53 -080060import java.util.concurrent.ConcurrentHashMap;
61
Marc De Leenheer16f857b2015-05-05 20:50:24 -070062import static com.google.common.base.Preconditions.checkArgument;
Praseed Balakrishnanc0029652014-11-14 13:38:49 -080063
Marc De Leenheer16f857b2015-05-05 20:50:24 -070064import static com.google.common.base.Preconditions.checkNotNull;
65
weibitf32383b2014-10-22 10:17:31 -070066/**
Marc De Leenheer16f857b2015-05-05 20:50:24 -070067 * OpticalPathProvisioner listens for event notifications from the Intent F/W.
weibitf32383b2014-10-22 10:17:31 -070068 * It generates one or more opticalConnectivityIntent(s) and submits (or withdraws) to Intent F/W
69 * for adding/releasing capacity at the packet layer.
weibitf32383b2014-10-22 10:17:31 -070070 */
71
72@Component(immediate = true)
73public class OpticalPathProvisioner {
74
75 protected static final Logger log = LoggerFactory
76 .getLogger(OpticalPathProvisioner.class);
77
78 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
79 private IntentService intentService;
80
81 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Brian O'Connor772852a2014-11-17 15:51:19 -080082 protected PathService pathService;
weibitf32383b2014-10-22 10:17:31 -070083
84 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
85 protected CoreService coreService;
86
weibit7e583462014-10-23 10:14:05 -070087 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Praseed Balakrishnanc0029652014-11-14 13:38:49 -080088 protected HostService hostService;
89
Marc De Leenheer8b3e80b2015-03-06 14:27:03 -080090 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
91 protected MastershipService mastershipService;
92
93 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
94 protected ClusterService clusterService;
95
Marc De Leenheer1afa2a02015-05-13 09:18:07 -070096 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Marc De Leenheer1afa2a02015-05-13 09:18:07 -070097 protected DeviceService deviceService;
98
99 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
100 protected DeviceResourceService deviceResourceService;
101
102 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
103 protected LinkResourceService linkResourceService;
104
105 private ApplicationId appId;
Brian O'Connorc7bdd8c2014-12-08 01:29:53 -0800106
Marc De Leenheer16f857b2015-05-05 20:50:24 -0700107 private final Map<ConnectPoint, Map<ConnectPoint, Intent>> intentMap =
Brian O'Connorc7bdd8c2014-12-08 01:29:53 -0800108 new ConcurrentHashMap<>();
weibitf32383b2014-10-22 10:17:31 -0700109
110 private final InternalOpticalPathProvisioner pathProvisioner = new InternalOpticalPathProvisioner();
111
112 @Activate
113 protected void activate() {
114 intentService.addListener(pathProvisioner);
Brian O'Connorabafb502014-12-02 22:26:20 -0800115 appId = coreService.registerApplication("org.onosproject.optical");
Marc De Leenheer1afa2a02015-05-13 09:18:07 -0700116 initOpticalPorts();
Marc De Leenheer16f857b2015-05-05 20:50:24 -0700117 log.info("Started");
118 }
119
120 @Deactivate
121 protected void deactivate() {
122 intentService.removeListener(pathProvisioner);
123 log.info("Stopped");
weibitf32383b2014-10-22 10:17:31 -0700124 }
125
Marc De Leenheer1afa2a02015-05-13 09:18:07 -0700126 /**
127 * Initialize availability of optical ports.
128 */
129 private void initOpticalPorts() {
130 // TODO: check for existing optical intents
131 return;
Brian O'Connorc7bdd8c2014-12-08 01:29:53 -0800132 }
133
weibitf32383b2014-10-22 10:17:31 -0700134 public class InternalOpticalPathProvisioner implements IntentListener {
135 @Override
136 public void event(IntentEvent event) {
137 switch (event.type()) {
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800138 case INSTALL_REQ:
weibitf32383b2014-10-22 10:17:31 -0700139 break;
140 case INSTALLED:
Marc De Leenheer1afa2a02015-05-13 09:18:07 -0700141 // track h2h & p2p intents using our connectivity
weibitf32383b2014-10-22 10:17:31 -0700142 break;
143 case FAILED:
Marc De Leenheer16f857b2015-05-05 20:50:24 -0700144 log.info("Intent {} failed, calling optical path provisioning app.", event.subject());
Brian O'Connor772852a2014-11-17 15:51:19 -0800145 setupLightpath(event.subject());
weibitf32383b2014-10-22 10:17:31 -0700146 break;
147 case WITHDRAWN:
Marc De Leenheer16f857b2015-05-05 20:50:24 -0700148 log.info("Intent {} withdrawn.", event.subject());
Marc De Leenheer1afa2a02015-05-13 09:18:07 -0700149 withdrawIntent(event.subject());
weibitf32383b2014-10-22 10:17:31 -0700150 break;
151 default:
152 break;
153 }
154 }
155
Brian O'Connorc7bdd8c2014-12-08 01:29:53 -0800156 /**
157 * Registers an intent from src to dst.
Marc De Leenheer16f857b2015-05-05 20:50:24 -0700158 *
Brian O'Connorc7bdd8c2014-12-08 01:29:53 -0800159 * @param src source point
160 * @param dst destination point
161 * @param intent intent to be registered
162 * @return true if intent has not been previously added, false otherwise
163 */
164 private boolean addIntent(ConnectPoint src, ConnectPoint dst, Intent intent) {
165 Map<ConnectPoint, Intent> srcMap = intentMap.get(src);
166 if (srcMap == null) {
167 srcMap = new ConcurrentHashMap<>();
168 intentMap.put(src, srcMap);
169 }
170 if (srcMap.containsKey(dst)) {
171 return false;
172 } else {
173 srcMap.put(dst, intent);
174 return true;
175 }
176 }
177
Brian O'Connor772852a2014-11-17 15:51:19 -0800178 private void setupLightpath(Intent intent) {
Marc De Leenheer1afa2a02015-05-13 09:18:07 -0700179 checkNotNull(intent);
180
Brian O'Connorc7bdd8c2014-12-08 01:29:53 -0800181 // TODO change the coordination approach between packet intents and optical intents
182 // Low speed LLDP may cause multiple calls which are not expected
183
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800184 if (!IntentState.FAILED.equals(intentService.getIntentState(intent.key()))) {
Marc De Leenheer8b3e80b2015-03-06 14:27:03 -0800185 return;
Marc De Leenheerb473b9d2015-02-06 15:21:03 -0800186 }
Brian O'Connorc7bdd8c2014-12-08 01:29:53 -0800187
Marc De Leenheer1afa2a02015-05-13 09:18:07 -0700188 // Get source and destination based on intent type
189 ConnectPoint src;
190 ConnectPoint dst;
Brian O'Connor772852a2014-11-17 15:51:19 -0800191 if (intent instanceof HostToHostIntent) {
192 HostToHostIntent hostToHostIntent = (HostToHostIntent) intent;
Marc De Leenheer8b3e80b2015-03-06 14:27:03 -0800193
Brian O'Connor772852a2014-11-17 15:51:19 -0800194 Host one = hostService.getHost(hostToHostIntent.one());
195 Host two = hostService.getHost(hostToHostIntent.two());
Marc De Leenheer8b3e80b2015-03-06 14:27:03 -0800196
Marc De Leenheer1afa2a02015-05-13 09:18:07 -0700197 checkNotNull(one);
198 checkNotNull(two);
Marc De Leenheer8b3e80b2015-03-06 14:27:03 -0800199
Marc De Leenheer1afa2a02015-05-13 09:18:07 -0700200 src = one.location();
201 dst = two.location();
Brian O'Connor772852a2014-11-17 15:51:19 -0800202 } else if (intent instanceof PointToPointIntent) {
203 PointToPointIntent p2pIntent = (PointToPointIntent) intent;
Marc De Leenheer8b3e80b2015-03-06 14:27:03 -0800204
Marc De Leenheer1afa2a02015-05-13 09:18:07 -0700205 src = p2pIntent.ingressPoint();
206 dst = p2pIntent.egressPoint();
Brian O'Connor772852a2014-11-17 15:51:19 -0800207 } else {
Marc De Leenheer1afa2a02015-05-13 09:18:07 -0700208 log.error("Unsupported intent type: {}", intent.getClass());
209 return;
Brian O'Connor772852a2014-11-17 15:51:19 -0800210 }
Praseed Balakrishnanc0029652014-11-14 13:38:49 -0800211
Marc De Leenheer1afa2a02015-05-13 09:18:07 -0700212 if (src == null || dst == null) {
213 return;
214 }
215
216 // Ignore if we're not the master for the intent's origin device
217 NodeId localNode = clusterService.getLocalNode().id();
218 NodeId sourceMaster = mastershipService.getMasterFor(src.deviceId());
219 if (!localNode.equals(sourceMaster)) {
220 return;
221 }
222
223 // Generate optical connectivity intents
224 List<Intent> intents = Lists.newArrayList();
225 intents.addAll(getOpticalIntents(src, dst));
226
227 // Submit the intents
Brian O'Connor772852a2014-11-17 15:51:19 -0800228 for (Intent i : intents) {
Marc De Leenheer1afa2a02015-05-13 09:18:07 -0700229 intentService.submit(i);
Sho SHIMIZUb5d38412015-05-21 11:12:58 -0700230 log.debug("Submitted an intent: {}", i);
Brian O'Connor772852a2014-11-17 15:51:19 -0800231 }
weibit7e583462014-10-23 10:14:05 -0700232 }
233
Marc De Leenheer16f857b2015-05-05 20:50:24 -0700234 /**
235 * Returns list of cross connection points of missing optical path sections.
236 *
237 * Scans the given multi-layer path and looks for sections that use cross connect links.
238 * The ingress and egress points in the optical layer are returned in a list.
239 *
240 * @param path the multi-layer path
241 * @return list of cross connection points on the optical layer
242 */
243 private List<ConnectPoint> getCrossConnectPoints(Path path) {
244 boolean scanning = false;
245 List<ConnectPoint> connectPoints = new LinkedList<ConnectPoint>();
246
247 for (Link link : path.links()) {
248 if (!isCrossConnectLink(link)) {
249 continue;
250 }
251
252 if (scanning) {
253 connectPoints.add(checkNotNull(link.src()));
254 scanning = false;
255 } else {
256 connectPoints.add(checkNotNull(link.dst()));
257 scanning = true;
258 }
259 }
260
261 return connectPoints;
262 }
263
264 /**
Marc De Leenheer1afa2a02015-05-13 09:18:07 -0700265 * Checks if cross connect points are of same type.
Marc De Leenheer16f857b2015-05-05 20:50:24 -0700266 *
267 * @param crossConnectPoints list of cross connection points
Marc De Leenheer1afa2a02015-05-13 09:18:07 -0700268 * @return true if cross connect point pairs are of same type, false otherwise
Marc De Leenheer16f857b2015-05-05 20:50:24 -0700269 */
270 private boolean checkCrossConnectPoints(List<ConnectPoint> crossConnectPoints) {
271 checkArgument(crossConnectPoints.size() % 2 == 0);
272
273 Iterator<ConnectPoint> itr = crossConnectPoints.iterator();
274
275 while (itr.hasNext()) {
276 // checkArgument at start ensures we'll always have pairs of connect points
277 ConnectPoint src = itr.next();
278 ConnectPoint dst = itr.next();
279
Marc De Leenheer1afa2a02015-05-13 09:18:07 -0700280 Device.Type srcType = deviceService.getDevice(src.deviceId()).type();
281 Device.Type dstType = deviceService.getDevice(dst.deviceId()).type();
282
283 // Only support connections between identical port types
284 if (srcType != dstType) {
285 log.warn("Unsupported mix of cross connect points");
Marc De Leenheer16f857b2015-05-05 20:50:24 -0700286 return false;
287 }
288 }
289
290 return true;
291 }
292
293 /**
Marc De Leenheer1afa2a02015-05-13 09:18:07 -0700294 * Scans the list of cross connection points and returns a list of optical connectivity intents.
Marc De Leenheer16f857b2015-05-05 20:50:24 -0700295 *
296 * @param crossConnectPoints list of cross connection points
297 * @return list of optical connectivity intents
298 */
299 private List<Intent> getIntents(List<ConnectPoint> crossConnectPoints) {
300 checkArgument(crossConnectPoints.size() % 2 == 0);
301
Sho SHIMIZU79945e82015-05-20 17:20:47 -0700302 List<Intent> intents = new LinkedList<>();
Marc De Leenheer16f857b2015-05-05 20:50:24 -0700303 Iterator<ConnectPoint> itr = crossConnectPoints.iterator();
304
305 while (itr.hasNext()) {
306 // checkArgument at start ensures we'll always have pairs of connect points
307 ConnectPoint src = itr.next();
308 ConnectPoint dst = itr.next();
309
Marc De Leenheer1afa2a02015-05-13 09:18:07 -0700310 Port srcPort = deviceService.getPort(src.deviceId(), src.port());
311 Port dstPort = deviceService.getPort(dst.deviceId(), dst.port());
312 // Create lightpath
313 // TODO: Ensure src & dst are of type OchPort
Marc De Leenheer16f857b2015-05-05 20:50:24 -0700314 Intent opticalIntent = OpticalConnectivityIntent.builder()
315 .appId(appId)
316 .src(src)
317 .dst(dst)
318 .build();
Marc De Leenheer16f857b2015-05-05 20:50:24 -0700319 intents.add(opticalIntent);
Marc De Leenheer1afa2a02015-05-13 09:18:07 -0700320 if (srcPort instanceof OduCltPort && dstPort instanceof OduCltPort) {
321 continue;
322 // also create OTN service
323 }
Marc De Leenheer16f857b2015-05-05 20:50:24 -0700324 }
325
326 return intents;
327 }
328
Marc De Leenheer1afa2a02015-05-13 09:18:07 -0700329 /**
330 * Returns list of optical connectivity intents needed to create connectivity
331 * between ingress and egress.
332 *
333 * @param ingress the ingress connect point
334 * @param egress the egress connect point
335 * @return list of optical connectivity intents, empty list if no path was found
336 */
337 private List<Intent> getOpticalIntents(ConnectPoint ingress, ConnectPoint egress) {
Brian O'Connor772852a2014-11-17 15:51:19 -0800338 Set<Path> paths = pathService.getPaths(ingress.deviceId(),
Marc De Leenheer16f857b2015-05-05 20:50:24 -0700339 egress.deviceId(),
340 new OpticalLinkWeight());
Brian O'Connor772852a2014-11-17 15:51:19 -0800341
342 if (paths.isEmpty()) {
Marc De Leenheer16f857b2015-05-05 20:50:24 -0700343 return Collections.emptyList();
weibit7e583462014-10-23 10:14:05 -0700344 }
Brian O'Connor772852a2014-11-17 15:51:19 -0800345
Marc De Leenheer1afa2a02015-05-13 09:18:07 -0700346 // Search path with available cross connect points
347 for (Path path : paths) {
348 List<ConnectPoint> crossConnectPoints = getCrossConnectPoints(path);
Brian O'Connor772852a2014-11-17 15:51:19 -0800349
Marc De Leenheer1afa2a02015-05-13 09:18:07 -0700350 // Skip to next path if cross connect points are mismatched
Marc De Leenheer16f857b2015-05-05 20:50:24 -0700351 if (!checkCrossConnectPoints(crossConnectPoints)) {
352 continue;
Brian O'Connor772852a2014-11-17 15:51:19 -0800353 }
354
Marc De Leenheer16f857b2015-05-05 20:50:24 -0700355 return getIntents(crossConnectPoints);
Brian O'Connor772852a2014-11-17 15:51:19 -0800356 }
Brian O'Connorc7bdd8c2014-12-08 01:29:53 -0800357
Marc De Leenheer16f857b2015-05-05 20:50:24 -0700358 return Collections.emptyList();
Brian O'Connor772852a2014-11-17 15:51:19 -0800359 }
360
Marc De Leenheer1afa2a02015-05-13 09:18:07 -0700361 /**
362 * Link weight function that emphasizes re-use of packet links.
363 */
364 private class OpticalLinkWeight implements LinkWeight {
365 @Override
366 public double weight(TopologyEdge edge) {
367 // Ignore inactive links
368 if (edge.link().state() == Link.State.INACTIVE) {
369 return -1;
370 }
Brian O'Connorc7bdd8c2014-12-08 01:29:53 -0800371
Marc De Leenheer1afa2a02015-05-13 09:18:07 -0700372 // TODO: Ignore cross connect links with used ports
373
374 // Transport links have highest weight
375 if (edge.link().type() == Link.Type.OPTICAL) {
376 return 1000;
377 }
378
379 // Packet links
380 return 1;
Brian O'Connorc7bdd8c2014-12-08 01:29:53 -0800381 }
weibitf32383b2014-10-22 10:17:31 -0700382 }
Brian O'Connorc7bdd8c2014-12-08 01:29:53 -0800383
Marc De Leenheer1afa2a02015-05-13 09:18:07 -0700384 /**
385 * Handle withdrawn intent on each network layer.
386 *
387 * @param intent the withdrawn intent
388 */
389 private void withdrawIntent(Intent intent) {
390 if (intent instanceof OpticalConnectivityIntent) {
391 deviceResourceService.releasePorts(intent.id());
392 linkResourceService.releaseResources(linkResourceService.getAllocations(intent.id()));
393 }
394 // TODO: add other layers
395 }
Brian O'Connor772852a2014-11-17 15:51:19 -0800396 }
weibitf32383b2014-10-22 10:17:31 -0700397
Marc De Leenheer16f857b2015-05-05 20:50:24 -0700398 /**
399 * Verifies if given link is cross-connect between packet and optical layer.
400 *
401 * @param link the link
402 * @return true if the link is a cross-connect link
403 */
404 public static boolean isCrossConnectLink(Link link) {
405 if (link.type() != Link.Type.OPTICAL) {
406 return false;
Praseed Balakrishnanc0029652014-11-14 13:38:49 -0800407 }
Marc De Leenheer16f857b2015-05-05 20:50:24 -0700408
409 checkNotNull(link.annotations());
410 checkNotNull(link.annotations().value("optical.type"));
411
412 if (link.annotations().value("optical.type").equals("cross-connect")) {
413 return true;
414 }
415
416 return false;
Brian O'Connor772852a2014-11-17 15:51:19 -0800417 }
Praseed Balakrishnanc0029652014-11-14 13:38:49 -0800418
weibitf32383b2014-10-22 10:17:31 -0700419}