blob: 47efc1b7c19706b401076d2a2e5a55246a8a4ec6 [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 Leenheer1afa2a02015-05-13 09:18:07 -070032import org.onosproject.net.OduCltPort;
Brian O'Connorabafb502014-12-02 22:26:20 -080033import org.onosproject.net.Path;
Marc De Leenheer1afa2a02015-05-13 09:18:07 -070034import org.onosproject.net.Port;
35import org.onosproject.net.device.DeviceService;
Brian O'Connorabafb502014-12-02 22:26:20 -080036import org.onosproject.net.host.HostService;
37import org.onosproject.net.intent.HostToHostIntent;
38import org.onosproject.net.intent.Intent;
39import org.onosproject.net.intent.IntentEvent;
40import org.onosproject.net.intent.IntentListener;
Brian O'Connorabafb502014-12-02 22:26:20 -080041import org.onosproject.net.intent.IntentService;
42import org.onosproject.net.intent.IntentState;
43import org.onosproject.net.intent.OpticalConnectivityIntent;
44import org.onosproject.net.intent.PointToPointIntent;
Brian O'Connor6de2e202015-05-21 14:30:41 -070045import org.onosproject.net.resource.device.DeviceResourceService;
46import org.onosproject.net.resource.link.LinkResourceService;
Brian O'Connorabafb502014-12-02 22:26:20 -080047import org.onosproject.net.topology.LinkWeight;
48import org.onosproject.net.topology.PathService;
49import org.onosproject.net.topology.TopologyEdge;
weibitf32383b2014-10-22 10:17:31 -070050import org.slf4j.Logger;
51import org.slf4j.LoggerFactory;
52
Marc De Leenheer16f857b2015-05-05 20:50:24 -070053import java.util.Collections;
Brian O'Connor772852a2014-11-17 15:51:19 -080054import java.util.Iterator;
Marc De Leenheer16f857b2015-05-05 20:50:24 -070055import java.util.LinkedList;
Brian O'Connor772852a2014-11-17 15:51:19 -080056import java.util.List;
Brian O'Connorc7bdd8c2014-12-08 01:29:53 -080057import java.util.Map;
Brian O'Connor772852a2014-11-17 15:51:19 -080058import java.util.Set;
Brian O'Connorc7bdd8c2014-12-08 01:29:53 -080059import java.util.concurrent.ConcurrentHashMap;
60
Marc De Leenheer16f857b2015-05-05 20:50:24 -070061import static com.google.common.base.Preconditions.checkArgument;
Praseed Balakrishnanc0029652014-11-14 13:38:49 -080062
Marc De Leenheer16f857b2015-05-05 20:50:24 -070063import static com.google.common.base.Preconditions.checkNotNull;
64
weibitf32383b2014-10-22 10:17:31 -070065/**
Marc De Leenheer16f857b2015-05-05 20:50:24 -070066 * OpticalPathProvisioner listens for event notifications from the Intent F/W.
weibitf32383b2014-10-22 10:17:31 -070067 * It generates one or more opticalConnectivityIntent(s) and submits (or withdraws) to Intent F/W
68 * for adding/releasing capacity at the packet layer.
weibitf32383b2014-10-22 10:17:31 -070069 */
70
71@Component(immediate = true)
72public class OpticalPathProvisioner {
73
74 protected static final Logger log = LoggerFactory
75 .getLogger(OpticalPathProvisioner.class);
76
77 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
78 private IntentService intentService;
79
80 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Brian O'Connor772852a2014-11-17 15:51:19 -080081 protected PathService pathService;
weibitf32383b2014-10-22 10:17:31 -070082
83 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
84 protected CoreService coreService;
85
weibit7e583462014-10-23 10:14:05 -070086 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Praseed Balakrishnanc0029652014-11-14 13:38:49 -080087 protected HostService hostService;
88
Marc De Leenheer8b3e80b2015-03-06 14:27:03 -080089 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
90 protected MastershipService mastershipService;
91
92 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
93 protected ClusterService clusterService;
94
Marc De Leenheer1afa2a02015-05-13 09:18:07 -070095 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Marc De Leenheer1afa2a02015-05-13 09:18:07 -070096 protected DeviceService deviceService;
97
98 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
99 protected DeviceResourceService deviceResourceService;
100
101 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
102 protected LinkResourceService linkResourceService;
103
104 private ApplicationId appId;
Brian O'Connorc7bdd8c2014-12-08 01:29:53 -0800105
Marc De Leenheer16f857b2015-05-05 20:50:24 -0700106 private final Map<ConnectPoint, Map<ConnectPoint, Intent>> intentMap =
Brian O'Connorc7bdd8c2014-12-08 01:29:53 -0800107 new ConcurrentHashMap<>();
weibitf32383b2014-10-22 10:17:31 -0700108
109 private final InternalOpticalPathProvisioner pathProvisioner = new InternalOpticalPathProvisioner();
110
111 @Activate
112 protected void activate() {
113 intentService.addListener(pathProvisioner);
Brian O'Connorabafb502014-12-02 22:26:20 -0800114 appId = coreService.registerApplication("org.onosproject.optical");
Marc De Leenheer1afa2a02015-05-13 09:18:07 -0700115 initOpticalPorts();
Marc De Leenheer16f857b2015-05-05 20:50:24 -0700116 log.info("Started");
117 }
118
119 @Deactivate
120 protected void deactivate() {
121 intentService.removeListener(pathProvisioner);
122 log.info("Stopped");
weibitf32383b2014-10-22 10:17:31 -0700123 }
124
Marc De Leenheer1afa2a02015-05-13 09:18:07 -0700125 /**
126 * Initialize availability of optical ports.
127 */
128 private void initOpticalPorts() {
129 // TODO: check for existing optical intents
130 return;
Brian O'Connorc7bdd8c2014-12-08 01:29:53 -0800131 }
132
weibitf32383b2014-10-22 10:17:31 -0700133 public class InternalOpticalPathProvisioner implements IntentListener {
134 @Override
135 public void event(IntentEvent event) {
136 switch (event.type()) {
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800137 case INSTALL_REQ:
weibitf32383b2014-10-22 10:17:31 -0700138 break;
139 case INSTALLED:
Marc De Leenheer1afa2a02015-05-13 09:18:07 -0700140 // track h2h & p2p intents using our connectivity
weibitf32383b2014-10-22 10:17:31 -0700141 break;
142 case FAILED:
Marc De Leenheer16f857b2015-05-05 20:50:24 -0700143 log.info("Intent {} failed, calling optical path provisioning app.", event.subject());
Brian O'Connor772852a2014-11-17 15:51:19 -0800144 setupLightpath(event.subject());
weibitf32383b2014-10-22 10:17:31 -0700145 break;
146 case WITHDRAWN:
Marc De Leenheer16f857b2015-05-05 20:50:24 -0700147 log.info("Intent {} withdrawn.", event.subject());
Marc De Leenheer1afa2a02015-05-13 09:18:07 -0700148 withdrawIntent(event.subject());
weibitf32383b2014-10-22 10:17:31 -0700149 break;
150 default:
151 break;
152 }
153 }
154
Brian O'Connorc7bdd8c2014-12-08 01:29:53 -0800155 /**
156 * Registers an intent from src to dst.
Marc De Leenheer16f857b2015-05-05 20:50:24 -0700157 *
Brian O'Connorc7bdd8c2014-12-08 01:29:53 -0800158 * @param src source point
159 * @param dst destination point
160 * @param intent intent to be registered
161 * @return true if intent has not been previously added, false otherwise
162 */
163 private boolean addIntent(ConnectPoint src, ConnectPoint dst, Intent intent) {
164 Map<ConnectPoint, Intent> srcMap = intentMap.get(src);
165 if (srcMap == null) {
166 srcMap = new ConcurrentHashMap<>();
167 intentMap.put(src, srcMap);
168 }
169 if (srcMap.containsKey(dst)) {
170 return false;
171 } else {
172 srcMap.put(dst, intent);
173 return true;
174 }
175 }
176
Brian O'Connor772852a2014-11-17 15:51:19 -0800177 private void setupLightpath(Intent intent) {
Marc De Leenheer1afa2a02015-05-13 09:18:07 -0700178 checkNotNull(intent);
179
Brian O'Connorc7bdd8c2014-12-08 01:29:53 -0800180 // TODO change the coordination approach between packet intents and optical intents
181 // Low speed LLDP may cause multiple calls which are not expected
182
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800183 if (!IntentState.FAILED.equals(intentService.getIntentState(intent.key()))) {
Marc De Leenheer8b3e80b2015-03-06 14:27:03 -0800184 return;
Marc De Leenheerb473b9d2015-02-06 15:21:03 -0800185 }
Brian O'Connorc7bdd8c2014-12-08 01:29:53 -0800186
Marc De Leenheer1afa2a02015-05-13 09:18:07 -0700187 // Get source and destination based on intent type
188 ConnectPoint src;
189 ConnectPoint dst;
Brian O'Connor772852a2014-11-17 15:51:19 -0800190 if (intent instanceof HostToHostIntent) {
191 HostToHostIntent hostToHostIntent = (HostToHostIntent) intent;
Marc De Leenheer8b3e80b2015-03-06 14:27:03 -0800192
Brian O'Connor772852a2014-11-17 15:51:19 -0800193 Host one = hostService.getHost(hostToHostIntent.one());
194 Host two = hostService.getHost(hostToHostIntent.two());
Marc De Leenheer8b3e80b2015-03-06 14:27:03 -0800195
Marc De Leenheer1afa2a02015-05-13 09:18:07 -0700196 checkNotNull(one);
197 checkNotNull(two);
Marc De Leenheer8b3e80b2015-03-06 14:27:03 -0800198
Marc De Leenheer1afa2a02015-05-13 09:18:07 -0700199 src = one.location();
200 dst = two.location();
Brian O'Connor772852a2014-11-17 15:51:19 -0800201 } else if (intent instanceof PointToPointIntent) {
202 PointToPointIntent p2pIntent = (PointToPointIntent) intent;
Marc De Leenheer8b3e80b2015-03-06 14:27:03 -0800203
Marc De Leenheer1afa2a02015-05-13 09:18:07 -0700204 src = p2pIntent.ingressPoint();
205 dst = p2pIntent.egressPoint();
Brian O'Connor772852a2014-11-17 15:51:19 -0800206 } else {
Marc De Leenheer1afa2a02015-05-13 09:18:07 -0700207 log.error("Unsupported intent type: {}", intent.getClass());
208 return;
Brian O'Connor772852a2014-11-17 15:51:19 -0800209 }
Praseed Balakrishnanc0029652014-11-14 13:38:49 -0800210
Marc De Leenheer1afa2a02015-05-13 09:18:07 -0700211 if (src == null || dst == null) {
212 return;
213 }
214
215 // Ignore if we're not the master for the intent's origin device
216 NodeId localNode = clusterService.getLocalNode().id();
217 NodeId sourceMaster = mastershipService.getMasterFor(src.deviceId());
218 if (!localNode.equals(sourceMaster)) {
219 return;
220 }
221
222 // Generate optical connectivity intents
Sho SHIMIZUfb446fe2015-05-21 14:45:23 -0700223 List<Intent> intents = getOpticalIntents(src, dst);
Marc De Leenheer1afa2a02015-05-13 09:18:07 -0700224
225 // Submit the intents
Brian O'Connor772852a2014-11-17 15:51:19 -0800226 for (Intent i : intents) {
Marc De Leenheer1afa2a02015-05-13 09:18:07 -0700227 intentService.submit(i);
Sho SHIMIZUb5d38412015-05-21 11:12:58 -0700228 log.debug("Submitted an intent: {}", i);
Brian O'Connor772852a2014-11-17 15:51:19 -0800229 }
weibit7e583462014-10-23 10:14:05 -0700230 }
231
Marc De Leenheer16f857b2015-05-05 20:50:24 -0700232 /**
233 * Returns list of cross connection points of missing optical path sections.
234 *
235 * Scans the given multi-layer path and looks for sections that use cross connect links.
236 * The ingress and egress points in the optical layer are returned in a list.
237 *
238 * @param path the multi-layer path
239 * @return list of cross connection points on the optical layer
240 */
241 private List<ConnectPoint> getCrossConnectPoints(Path path) {
242 boolean scanning = false;
243 List<ConnectPoint> connectPoints = new LinkedList<ConnectPoint>();
244
245 for (Link link : path.links()) {
246 if (!isCrossConnectLink(link)) {
247 continue;
248 }
249
250 if (scanning) {
251 connectPoints.add(checkNotNull(link.src()));
252 scanning = false;
253 } else {
254 connectPoints.add(checkNotNull(link.dst()));
255 scanning = true;
256 }
257 }
258
259 return connectPoints;
260 }
261
262 /**
Marc De Leenheer1afa2a02015-05-13 09:18:07 -0700263 * Checks if cross connect points are of same type.
Marc De Leenheer16f857b2015-05-05 20:50:24 -0700264 *
265 * @param crossConnectPoints list of cross connection points
Marc De Leenheer1afa2a02015-05-13 09:18:07 -0700266 * @return true if cross connect point pairs are of same type, false otherwise
Marc De Leenheer16f857b2015-05-05 20:50:24 -0700267 */
268 private boolean checkCrossConnectPoints(List<ConnectPoint> crossConnectPoints) {
269 checkArgument(crossConnectPoints.size() % 2 == 0);
270
271 Iterator<ConnectPoint> itr = crossConnectPoints.iterator();
272
273 while (itr.hasNext()) {
274 // checkArgument at start ensures we'll always have pairs of connect points
275 ConnectPoint src = itr.next();
276 ConnectPoint dst = itr.next();
277
Marc De Leenheer1afa2a02015-05-13 09:18:07 -0700278 Device.Type srcType = deviceService.getDevice(src.deviceId()).type();
279 Device.Type dstType = deviceService.getDevice(dst.deviceId()).type();
280
281 // Only support connections between identical port types
282 if (srcType != dstType) {
283 log.warn("Unsupported mix of cross connect points");
Marc De Leenheer16f857b2015-05-05 20:50:24 -0700284 return false;
285 }
286 }
287
288 return true;
289 }
290
291 /**
Marc De Leenheer1afa2a02015-05-13 09:18:07 -0700292 * Scans the list of cross connection points and returns a list of optical connectivity intents.
Marc De Leenheer16f857b2015-05-05 20:50:24 -0700293 *
294 * @param crossConnectPoints list of cross connection points
295 * @return list of optical connectivity intents
296 */
297 private List<Intent> getIntents(List<ConnectPoint> crossConnectPoints) {
298 checkArgument(crossConnectPoints.size() % 2 == 0);
299
Sho SHIMIZU79945e82015-05-20 17:20:47 -0700300 List<Intent> intents = new LinkedList<>();
Marc De Leenheer16f857b2015-05-05 20:50:24 -0700301 Iterator<ConnectPoint> itr = crossConnectPoints.iterator();
302
303 while (itr.hasNext()) {
304 // checkArgument at start ensures we'll always have pairs of connect points
305 ConnectPoint src = itr.next();
306 ConnectPoint dst = itr.next();
307
Marc De Leenheer1afa2a02015-05-13 09:18:07 -0700308 Port srcPort = deviceService.getPort(src.deviceId(), src.port());
309 Port dstPort = deviceService.getPort(dst.deviceId(), dst.port());
310 // Create lightpath
311 // TODO: Ensure src & dst are of type OchPort
Marc De Leenheer16f857b2015-05-05 20:50:24 -0700312 Intent opticalIntent = OpticalConnectivityIntent.builder()
313 .appId(appId)
314 .src(src)
315 .dst(dst)
316 .build();
Marc De Leenheer16f857b2015-05-05 20:50:24 -0700317 intents.add(opticalIntent);
Marc De Leenheer1afa2a02015-05-13 09:18:07 -0700318 if (srcPort instanceof OduCltPort && dstPort instanceof OduCltPort) {
319 continue;
320 // also create OTN service
321 }
Marc De Leenheer16f857b2015-05-05 20:50:24 -0700322 }
323
324 return intents;
325 }
326
Marc De Leenheer1afa2a02015-05-13 09:18:07 -0700327 /**
328 * Returns list of optical connectivity intents needed to create connectivity
329 * between ingress and egress.
330 *
331 * @param ingress the ingress connect point
332 * @param egress the egress connect point
333 * @return list of optical connectivity intents, empty list if no path was found
334 */
335 private List<Intent> getOpticalIntents(ConnectPoint ingress, ConnectPoint egress) {
Brian O'Connor772852a2014-11-17 15:51:19 -0800336 Set<Path> paths = pathService.getPaths(ingress.deviceId(),
Marc De Leenheer16f857b2015-05-05 20:50:24 -0700337 egress.deviceId(),
338 new OpticalLinkWeight());
Brian O'Connor772852a2014-11-17 15:51:19 -0800339
340 if (paths.isEmpty()) {
Marc De Leenheer16f857b2015-05-05 20:50:24 -0700341 return Collections.emptyList();
weibit7e583462014-10-23 10:14:05 -0700342 }
Brian O'Connor772852a2014-11-17 15:51:19 -0800343
Marc De Leenheer1afa2a02015-05-13 09:18:07 -0700344 // Search path with available cross connect points
345 for (Path path : paths) {
346 List<ConnectPoint> crossConnectPoints = getCrossConnectPoints(path);
Brian O'Connor772852a2014-11-17 15:51:19 -0800347
Marc De Leenheer1afa2a02015-05-13 09:18:07 -0700348 // Skip to next path if cross connect points are mismatched
Marc De Leenheer16f857b2015-05-05 20:50:24 -0700349 if (!checkCrossConnectPoints(crossConnectPoints)) {
350 continue;
Brian O'Connor772852a2014-11-17 15:51:19 -0800351 }
352
Marc De Leenheer16f857b2015-05-05 20:50:24 -0700353 return getIntents(crossConnectPoints);
Brian O'Connor772852a2014-11-17 15:51:19 -0800354 }
Brian O'Connorc7bdd8c2014-12-08 01:29:53 -0800355
Marc De Leenheer16f857b2015-05-05 20:50:24 -0700356 return Collections.emptyList();
Brian O'Connor772852a2014-11-17 15:51:19 -0800357 }
358
Marc De Leenheer1afa2a02015-05-13 09:18:07 -0700359 /**
360 * Link weight function that emphasizes re-use of packet links.
361 */
362 private class OpticalLinkWeight implements LinkWeight {
363 @Override
364 public double weight(TopologyEdge edge) {
365 // Ignore inactive links
366 if (edge.link().state() == Link.State.INACTIVE) {
367 return -1;
368 }
Brian O'Connorc7bdd8c2014-12-08 01:29:53 -0800369
Marc De Leenheer1afa2a02015-05-13 09:18:07 -0700370 // TODO: Ignore cross connect links with used ports
371
372 // Transport links have highest weight
373 if (edge.link().type() == Link.Type.OPTICAL) {
374 return 1000;
375 }
376
377 // Packet links
378 return 1;
Brian O'Connorc7bdd8c2014-12-08 01:29:53 -0800379 }
weibitf32383b2014-10-22 10:17:31 -0700380 }
Brian O'Connorc7bdd8c2014-12-08 01:29:53 -0800381
Marc De Leenheer1afa2a02015-05-13 09:18:07 -0700382 /**
383 * Handle withdrawn intent on each network layer.
384 *
385 * @param intent the withdrawn intent
386 */
387 private void withdrawIntent(Intent intent) {
388 if (intent instanceof OpticalConnectivityIntent) {
389 deviceResourceService.releasePorts(intent.id());
390 linkResourceService.releaseResources(linkResourceService.getAllocations(intent.id()));
391 }
392 // TODO: add other layers
393 }
Brian O'Connor772852a2014-11-17 15:51:19 -0800394 }
weibitf32383b2014-10-22 10:17:31 -0700395
Marc De Leenheer16f857b2015-05-05 20:50:24 -0700396 /**
397 * Verifies if given link is cross-connect between packet and optical layer.
398 *
399 * @param link the link
400 * @return true if the link is a cross-connect link
401 */
402 public static boolean isCrossConnectLink(Link link) {
403 if (link.type() != Link.Type.OPTICAL) {
404 return false;
Praseed Balakrishnanc0029652014-11-14 13:38:49 -0800405 }
Marc De Leenheer16f857b2015-05-05 20:50:24 -0700406
407 checkNotNull(link.annotations());
408 checkNotNull(link.annotations().value("optical.type"));
409
Sho SHIMIZU6d283122015-05-21 11:07:02 -0700410 return link.annotations().value("optical.type").equals("cross-connect");
Brian O'Connor772852a2014-11-17 15:51:19 -0800411 }
Praseed Balakrishnanc0029652014-11-14 13:38:49 -0800412
weibitf32383b2014-10-22 10:17:31 -0700413}