blob: 6b95d63fa2650aca694ec0b3a688169077b74ef4 [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;
30import org.onosproject.net.Host;
31import org.onosproject.net.Link;
32import org.onosproject.net.Path;
33import org.onosproject.net.host.HostService;
34import org.onosproject.net.intent.HostToHostIntent;
35import org.onosproject.net.intent.Intent;
36import org.onosproject.net.intent.IntentEvent;
37import org.onosproject.net.intent.IntentListener;
Brian O'Connorabafb502014-12-02 22:26:20 -080038import org.onosproject.net.intent.IntentService;
39import org.onosproject.net.intent.IntentState;
40import org.onosproject.net.intent.OpticalConnectivityIntent;
41import org.onosproject.net.intent.PointToPointIntent;
42import org.onosproject.net.topology.LinkWeight;
43import org.onosproject.net.topology.PathService;
44import org.onosproject.net.topology.TopologyEdge;
weibitf32383b2014-10-22 10:17:31 -070045import org.slf4j.Logger;
46import org.slf4j.LoggerFactory;
47
Marc De Leenheer16f857b2015-05-05 20:50:24 -070048import java.util.Collections;
Brian O'Connor772852a2014-11-17 15:51:19 -080049import java.util.Iterator;
Marc De Leenheer16f857b2015-05-05 20:50:24 -070050import java.util.LinkedList;
Brian O'Connor772852a2014-11-17 15:51:19 -080051import java.util.List;
Brian O'Connorc7bdd8c2014-12-08 01:29:53 -080052import java.util.Map;
Brian O'Connor772852a2014-11-17 15:51:19 -080053import java.util.Set;
Brian O'Connorc7bdd8c2014-12-08 01:29:53 -080054import java.util.concurrent.ConcurrentHashMap;
55
Marc De Leenheer16f857b2015-05-05 20:50:24 -070056import static com.google.common.base.Preconditions.checkArgument;
Brian O'Connorc7bdd8c2014-12-08 01:29:53 -080057import static org.onosproject.net.intent.IntentState.INSTALLED;
Praseed Balakrishnanc0029652014-11-14 13:38:49 -080058
Marc De Leenheer16f857b2015-05-05 20:50:24 -070059import static com.google.common.base.Preconditions.checkNotNull;
60
weibitf32383b2014-10-22 10:17:31 -070061/**
Marc De Leenheer16f857b2015-05-05 20:50:24 -070062 * OpticalPathProvisioner listens for event notifications from the Intent F/W.
weibitf32383b2014-10-22 10:17:31 -070063 * It generates one or more opticalConnectivityIntent(s) and submits (or withdraws) to Intent F/W
64 * for adding/releasing capacity at the packet layer.
weibitf32383b2014-10-22 10:17:31 -070065 */
66
67@Component(immediate = true)
68public class OpticalPathProvisioner {
69
70 protected static final Logger log = LoggerFactory
71 .getLogger(OpticalPathProvisioner.class);
72
73 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
74 private IntentService intentService;
75
76 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Brian O'Connor772852a2014-11-17 15:51:19 -080077 protected PathService pathService;
weibitf32383b2014-10-22 10:17:31 -070078
79 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
80 protected CoreService coreService;
81
weibit7e583462014-10-23 10:14:05 -070082 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Praseed Balakrishnanc0029652014-11-14 13:38:49 -080083 protected HostService hostService;
84
Marc De Leenheer8b3e80b2015-03-06 14:27:03 -080085 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
86 protected MastershipService mastershipService;
87
88 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
89 protected ClusterService clusterService;
90
weibitf32383b2014-10-22 10:17:31 -070091 private ApplicationId appId;
weibitf32383b2014-10-22 10:17:31 -070092
Brian O'Connorc7bdd8c2014-12-08 01:29:53 -080093 // TODO use a shared map for distributed operation
Marc De Leenheer16f857b2015-05-05 20:50:24 -070094 private final Map<ConnectPoint, OpticalConnectivityIntent> inStatusTportMap =
Brian O'Connorc7bdd8c2014-12-08 01:29:53 -080095 new ConcurrentHashMap<>();
Marc De Leenheer16f857b2015-05-05 20:50:24 -070096 private final Map<ConnectPoint, OpticalConnectivityIntent> outStatusTportMap =
Brian O'Connorc7bdd8c2014-12-08 01:29:53 -080097 new ConcurrentHashMap<>();
98
Marc De Leenheer16f857b2015-05-05 20:50:24 -070099 private final Map<ConnectPoint, Map<ConnectPoint, Intent>> intentMap =
Brian O'Connorc7bdd8c2014-12-08 01:29:53 -0800100 new ConcurrentHashMap<>();
weibitf32383b2014-10-22 10:17:31 -0700101
102 private final InternalOpticalPathProvisioner pathProvisioner = new InternalOpticalPathProvisioner();
103
104 @Activate
105 protected void activate() {
106 intentService.addListener(pathProvisioner);
Brian O'Connorabafb502014-12-02 22:26:20 -0800107 appId = coreService.registerApplication("org.onosproject.optical");
Brian O'Connorc7bdd8c2014-12-08 01:29:53 -0800108 initTport();
Marc De Leenheer16f857b2015-05-05 20:50:24 -0700109 log.info("Started");
110 }
111
112 @Deactivate
113 protected void deactivate() {
114 intentService.removeListener(pathProvisioner);
115 log.info("Stopped");
weibitf32383b2014-10-22 10:17:31 -0700116 }
117
Brian O'Connorc7bdd8c2014-12-08 01:29:53 -0800118 protected void initTport() {
119 inStatusTportMap.clear();
120 outStatusTportMap.clear();
121 for (Intent intent : intentService.getIntents()) {
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800122 if (intentService.getIntentState(intent.key()) == INSTALLED) {
Brian O'Connorc7bdd8c2014-12-08 01:29:53 -0800123 if (intent instanceof OpticalConnectivityIntent) {
124 inStatusTportMap.put(((OpticalConnectivityIntent) intent).getSrc(),
125 (OpticalConnectivityIntent) intent);
126 outStatusTportMap.put(((OpticalConnectivityIntent) intent).getDst(),
127 (OpticalConnectivityIntent) intent);
128 }
129 }
130 }
131 }
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:
140 break;
141 case FAILED:
Marc De Leenheer16f857b2015-05-05 20:50:24 -0700142 log.info("Intent {} failed, calling optical path provisioning app.", event.subject());
Brian O'Connor772852a2014-11-17 15:51:19 -0800143 setupLightpath(event.subject());
weibitf32383b2014-10-22 10:17:31 -0700144 break;
145 case WITHDRAWN:
Marc De Leenheer16f857b2015-05-05 20:50:24 -0700146 log.info("Intent {} withdrawn.", event.subject());
Brian O'Connorc7bdd8c2014-12-08 01:29:53 -0800147 //FIXME
148 //teardownLightpath(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 private void reserveTport(Intent intent) {
156 // TODO move to resourceManager
157 if (intent instanceof OpticalConnectivityIntent) {
158 OpticalConnectivityIntent opticalIntent =
159 (OpticalConnectivityIntent) intent;
160 if (inStatusTportMap.containsKey(opticalIntent.getSrc()) ||
161 outStatusTportMap.containsKey(opticalIntent.getDst())) {
162 //TODO throw an exception, perhaps
163 log.warn("Overlapping reservation: {}", opticalIntent);
164 }
165 inStatusTportMap.put(opticalIntent.getSrc(), opticalIntent);
166 outStatusTportMap.put(opticalIntent.getDst(), opticalIntent);
167 }
168 }
169
170 /**
171 * Registers an intent from src to dst.
Marc De Leenheer16f857b2015-05-05 20:50:24 -0700172 *
Brian O'Connorc7bdd8c2014-12-08 01:29:53 -0800173 * @param src source point
174 * @param dst destination point
175 * @param intent intent to be registered
176 * @return true if intent has not been previously added, false otherwise
177 */
178 private boolean addIntent(ConnectPoint src, ConnectPoint dst, Intent intent) {
179 Map<ConnectPoint, Intent> srcMap = intentMap.get(src);
180 if (srcMap == null) {
181 srcMap = new ConcurrentHashMap<>();
182 intentMap.put(src, srcMap);
183 }
184 if (srcMap.containsKey(dst)) {
185 return false;
186 } else {
187 srcMap.put(dst, intent);
188 return true;
189 }
190 }
191
Brian O'Connor772852a2014-11-17 15:51:19 -0800192 private void setupLightpath(Intent intent) {
Brian O'Connorc7bdd8c2014-12-08 01:29:53 -0800193 // TODO change the coordination approach between packet intents and optical intents
194 // Low speed LLDP may cause multiple calls which are not expected
195
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800196 if (!IntentState.FAILED.equals(intentService.getIntentState(intent.key()))) {
Marc De Leenheer8b3e80b2015-03-06 14:27:03 -0800197 return;
Marc De Leenheerb473b9d2015-02-06 15:21:03 -0800198 }
Brian O'Connorc7bdd8c2014-12-08 01:29:53 -0800199
Marc De Leenheer8b3e80b2015-03-06 14:27:03 -0800200 NodeId localNode = clusterService.getLocalNode().id();
201
Brian O'Connor772852a2014-11-17 15:51:19 -0800202 List<Intent> intents = Lists.newArrayList();
203 if (intent instanceof HostToHostIntent) {
204 HostToHostIntent hostToHostIntent = (HostToHostIntent) intent;
Marc De Leenheer8b3e80b2015-03-06 14:27:03 -0800205
Brian O'Connor772852a2014-11-17 15:51:19 -0800206 Host one = hostService.getHost(hostToHostIntent.one());
207 Host two = hostService.getHost(hostToHostIntent.two());
Brian O'Connorc7bdd8c2014-12-08 01:29:53 -0800208 if (one == null || two == null) {
209 return; //FIXME
210 }
Marc De Leenheer8b3e80b2015-03-06 14:27:03 -0800211
212 // Ignore if we're not the master for the intent's origin device
213 NodeId sourceMaster = mastershipService.getMasterFor(one.location().deviceId());
214 if (!localNode.equals(sourceMaster)) {
215 return;
216 }
217
Brian O'Connor772852a2014-11-17 15:51:19 -0800218 // provision both directions
Brian O'Connorc7bdd8c2014-12-08 01:29:53 -0800219 intents.addAll(getOpticalPath(one.location(), two.location()));
Brian O'Connor025fb442014-12-08 20:02:15 -0800220 // note: bi-directional intent is set up
221 // HostToHost Intent requires symmetric path!
222 //intents.addAll(getOpticalPath(two.location(), one.location()));
Brian O'Connor772852a2014-11-17 15:51:19 -0800223 } else if (intent instanceof PointToPointIntent) {
224 PointToPointIntent p2pIntent = (PointToPointIntent) intent;
Marc De Leenheer8b3e80b2015-03-06 14:27:03 -0800225
226 // Ignore if we're not the master for the intent's origin device
227 NodeId sourceMaster = mastershipService.getMasterFor(p2pIntent.ingressPoint().deviceId());
228 if (!localNode.equals(sourceMaster)) {
229 return;
230 }
231
Brian O'Connorc7bdd8c2014-12-08 01:29:53 -0800232 intents.addAll(getOpticalPath(p2pIntent.ingressPoint(), p2pIntent.egressPoint()));
Brian O'Connor772852a2014-11-17 15:51:19 -0800233 } else {
234 log.info("Unsupported intent type: {}", intent.getClass());
235 }
Praseed Balakrishnanc0029652014-11-14 13:38:49 -0800236
Brian O'Connor03406a42015-02-03 17:28:57 -0800237 // Create the intents
Brian O'Connor772852a2014-11-17 15:51:19 -0800238 for (Intent i : intents) {
239 // TODO: don't allow duplicate intents between the same points for now
Brian O'Connorc7bdd8c2014-12-08 01:29:53 -0800240 // we may want to allow this carefully in future to increase capacity
241 if (i instanceof OpticalConnectivityIntent) {
242 OpticalConnectivityIntent oi = (OpticalConnectivityIntent) i;
243 if (addIntent(oi.getSrc(), oi.getDst(), oi)) {
Brian O'Connor03406a42015-02-03 17:28:57 -0800244 intentService.submit(i);
Brian O'Connorc7bdd8c2014-12-08 01:29:53 -0800245 reserveTport(i);
246 }
247 } else {
248 log.warn("Invalid intent type: {} for {}", i.getClass(), i);
Brian O'Connor772852a2014-11-17 15:51:19 -0800249 }
250 }
weibit7e583462014-10-23 10:14:05 -0700251 }
252
Marc De Leenheer16f857b2015-05-05 20:50:24 -0700253 /**
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
Brian O'Connorc7bdd8c2014-12-08 01:29:53 -0800344 private List<Intent> getOpticalPath(ConnectPoint ingress, ConnectPoint egress) {
Brian O'Connor772852a2014-11-17 15:51:19 -0800345 Set<Path> paths = pathService.getPaths(ingress.deviceId(),
Marc De Leenheer16f857b2015-05-05 20:50:24 -0700346 egress.deviceId(),
347 new OpticalLinkWeight());
Brian O'Connor772852a2014-11-17 15:51:19 -0800348
349 if (paths.isEmpty()) {
Marc De Leenheer16f857b2015-05-05 20:50:24 -0700350 return Collections.emptyList();
weibit7e583462014-10-23 10:14:05 -0700351 }
Brian O'Connor772852a2014-11-17 15:51:19 -0800352
Brian O'Connor772852a2014-11-17 15:51:19 -0800353 List<Intent> connectionList = Lists.newArrayList();
354
Marc De Leenheer16f857b2015-05-05 20:50:24 -0700355 // Iterate over all paths until a suitable one has been found
Brian O'Connorc7bdd8c2014-12-08 01:29:53 -0800356 Iterator<Path> itrPath = paths.iterator();
357 while (itrPath.hasNext()) {
Brian O'Connorc7bdd8c2014-12-08 01:29:53 -0800358 Path nextPath = itrPath.next();
Brian O'Connor772852a2014-11-17 15:51:19 -0800359
Marc De Leenheer16f857b2015-05-05 20:50:24 -0700360 List<ConnectPoint> crossConnectPoints = getCrossConnectPoints(nextPath);
Brian O'Connorc7bdd8c2014-12-08 01:29:53 -0800361
Marc De Leenheer16f857b2015-05-05 20:50:24 -0700362 // Skip to next path if not all connect points are available
363 if (!checkCrossConnectPoints(crossConnectPoints)) {
364 continue;
Brian O'Connor772852a2014-11-17 15:51:19 -0800365 }
366
Marc De Leenheer16f857b2015-05-05 20:50:24 -0700367 return getIntents(crossConnectPoints);
Brian O'Connor772852a2014-11-17 15:51:19 -0800368 }
Brian O'Connorc7bdd8c2014-12-08 01:29:53 -0800369
Marc De Leenheer16f857b2015-05-05 20:50:24 -0700370 return Collections.emptyList();
Brian O'Connor772852a2014-11-17 15:51:19 -0800371 }
372
weibitf32383b2014-10-22 10:17:31 -0700373 private void teardownLightpath(Intent intent) {
Brian O'Connorc7bdd8c2014-12-08 01:29:53 -0800374 /* FIXME this command doesn't make much sense. we need to define the semantics
375 // TODO move to resourceManager
376 if (intent instanceof OpticalConnectivityIntent) {
377 inStatusTportMap.remove(((OpticalConnectivityIntent) intent).getSrc());
378 outStatusTportMap.remove(((OpticalConnectivityIntent) intent).getDst());
379 // TODO tear down the idle lightpath if the utilization is zero.
380
381 }
382 */ //end-FIXME
weibitf32383b2014-10-22 10:17:31 -0700383 }
Brian O'Connorc7bdd8c2014-12-08 01:29:53 -0800384
Brian O'Connor772852a2014-11-17 15:51:19 -0800385 }
weibitf32383b2014-10-22 10:17:31 -0700386
Marc De Leenheer16f857b2015-05-05 20:50:24 -0700387 /**
388 * Verifies if given link is cross-connect between packet and optical layer.
389 *
390 * @param link the link
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;
Praseed Balakrishnanc0029652014-11-14 13:38:49 -0800396 }
Marc De Leenheer16f857b2015-05-05 20:50:24 -0700397
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;
Brian O'Connor772852a2014-11-17 15:51:19 -0800406 }
Praseed Balakrishnanc0029652014-11-14 13:38:49 -0800407
Marc De Leenheer16f857b2015-05-05 20:50:24 -0700408 /**
409 * Link weight function that emphasizes re-use of packet links.
410 */
Brian O'Connor772852a2014-11-17 15:51:19 -0800411 private static class OpticalLinkWeight implements LinkWeight {
412 @Override
413 public double weight(TopologyEdge edge) {
Marc De Leenheer16f857b2015-05-05 20:50:24 -0700414 // Ignore inactive links
Praseed Balakrishnan00dd1f92014-11-19 17:12:36 -0800415 if (edge.link().state() == Link.State.INACTIVE) {
Marc De Leenheer16f857b2015-05-05 20:50:24 -0700416 return -1;
Praseed Balakrishnan00dd1f92014-11-19 17:12:36 -0800417 }
Marc De Leenheer16f857b2015-05-05 20:50:24 -0700418
419 // Transport links have highest weight
420 if (edge.link().type() == Link.Type.OPTICAL) {
421 return 1000;
Brian O'Connor772852a2014-11-17 15:51:19 -0800422 }
Marc De Leenheer16f857b2015-05-05 20:50:24 -0700423
424 // Packet links
425 return 1;
Brian O'Connor772852a2014-11-17 15:51:19 -0800426 }
weibitf32383b2014-10-22 10:17:31 -0700427 }
428
429}