blob: 4c64784c3fb42c5f8206fd0b2aa19e1553191c63 [file] [log] [blame]
alshabib77b88482015-04-07 15:47:50 -07001/*
2 * Copyright 2015 Open Networking Laboratory
3 *
4 * 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
7 *
8 * 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.
15 */
16package org.onosproject.net.flowobjective.impl;
17
18import com.google.common.collect.Maps;
alshabib2a441c62015-04-13 18:39:38 -070019import com.google.common.collect.Sets;
alshabib77b88482015-04-07 15:47:50 -070020import org.apache.felix.scr.annotations.Activate;
21import org.apache.felix.scr.annotations.Component;
22import org.apache.felix.scr.annotations.Deactivate;
23import org.apache.felix.scr.annotations.Reference;
24import org.apache.felix.scr.annotations.ReferenceCardinality;
25import org.apache.felix.scr.annotations.Service;
26import org.onlab.osgi.DefaultServiceDirectory;
27import org.onlab.osgi.ServiceDirectory;
Thomas Vachuskaca88bb72015-04-08 19:38:02 -070028import org.onlab.util.ItemNotFoundException;
alshabib77b88482015-04-07 15:47:50 -070029import org.onosproject.cluster.ClusterService;
Changhoon Yoon541ef712015-05-23 17:18:34 +090030import org.onosproject.core.Permission;
alshabib77b88482015-04-07 15:47:50 -070031import org.onosproject.mastership.MastershipEvent;
32import org.onosproject.mastership.MastershipListener;
33import org.onosproject.mastership.MastershipService;
alshabib77b88482015-04-07 15:47:50 -070034import org.onosproject.net.DeviceId;
35import org.onosproject.net.behaviour.Pipeliner;
Thomas Vachuskaca88bb72015-04-08 19:38:02 -070036import org.onosproject.net.behaviour.PipelinerContext;
alshabibaebe7752015-04-07 17:45:42 -070037import org.onosproject.net.device.DeviceEvent;
38import org.onosproject.net.device.DeviceListener;
alshabib77b88482015-04-07 15:47:50 -070039import org.onosproject.net.device.DeviceService;
Thomas Vachuska866b46a2015-04-30 00:26:55 -070040import org.onosproject.net.driver.DefaultDriverProviderService;
alshabib77b88482015-04-07 15:47:50 -070041import org.onosproject.net.driver.DriverHandler;
42import org.onosproject.net.driver.DriverService;
Thomas Vachuskaca88bb72015-04-08 19:38:02 -070043import org.onosproject.net.flow.FlowRuleService;
alshabib77b88482015-04-07 15:47:50 -070044import org.onosproject.net.flowobjective.FilteringObjective;
45import org.onosproject.net.flowobjective.FlowObjectiveService;
alshabib2a441c62015-04-13 18:39:38 -070046import org.onosproject.net.flowobjective.FlowObjectiveStore;
47import org.onosproject.net.flowobjective.FlowObjectiveStoreDelegate;
alshabib77b88482015-04-07 15:47:50 -070048import org.onosproject.net.flowobjective.ForwardingObjective;
49import org.onosproject.net.flowobjective.NextObjective;
alshabib910aff12015-04-09 16:55:57 -070050import org.onosproject.net.flowobjective.Objective;
Jonathan Hart17d00452015-04-21 17:10:00 -070051import org.onosproject.net.flowobjective.ObjectiveError;
alshabib2a441c62015-04-13 18:39:38 -070052import org.onosproject.net.flowobjective.ObjectiveEvent;
Thomas Vachuskaca88bb72015-04-08 19:38:02 -070053import org.onosproject.net.group.GroupService;
alshabib77b88482015-04-07 15:47:50 -070054import org.slf4j.Logger;
55import org.slf4j.LoggerFactory;
56
alshabib77b88482015-04-07 15:47:50 -070057import java.util.Map;
alshabib2a441c62015-04-13 18:39:38 -070058import java.util.Set;
Jonathan Hart17d00452015-04-21 17:10:00 -070059import java.util.concurrent.ExecutorService;
alshabib77b88482015-04-07 15:47:50 -070060
Thomas Vachuska866b46a2015-04-30 00:26:55 -070061import static java.util.concurrent.Executors.newFixedThreadPool;
Jonathan Hart17d00452015-04-21 17:10:00 -070062import static org.onlab.util.Tools.groupedThreads;
Changhoon Yoon541ef712015-05-23 17:18:34 +090063import static org.onosproject.security.AppGuard.checkPermission;
64
alshabib77b88482015-04-07 15:47:50 -070065
66/**
Thomas Vachuskaca88bb72015-04-08 19:38:02 -070067 * Provides implementation of the flow objective programming service.
alshabib77b88482015-04-07 15:47:50 -070068 */
alshabib2a441c62015-04-13 18:39:38 -070069@Component(immediate = true)
alshabib77b88482015-04-07 15:47:50 -070070@Service
71public class FlowObjectiveManager implements FlowObjectiveService {
72
Saurav Dasbd7f7422015-04-23 16:31:47 -070073 public static final int INSTALL_RETRY_ATTEMPTS = 5;
Jonathan Hart17d00452015-04-21 17:10:00 -070074 public static final long INSTALL_RETRY_INTERVAL = 1000; // ms
alshabib77b88482015-04-07 15:47:50 -070075
Jonathan Hart17d00452015-04-21 17:10:00 -070076 private final Logger log = LoggerFactory.getLogger(getClass());
Thomas Vachuskaca88bb72015-04-08 19:38:02 -070077
alshabib77b88482015-04-07 15:47:50 -070078 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
79 protected DriverService driverService;
80
81 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
82 protected DeviceService deviceService;
83
84 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
85 protected MastershipService mastershipService;
86
87 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
88 protected ClusterService clusterService;
89
Thomas Vachuskaca88bb72015-04-08 19:38:02 -070090 // Note: The following dependencies are added on behalf of the pipeline
91 // driver behaviours to assure these services are available for their
92 // initialization.
93 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
94 protected FlowRuleService flowRuleService;
95
96 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
97 protected GroupService groupService;
98
alshabib2a441c62015-04-13 18:39:38 -070099 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
100 protected FlowObjectiveStore flowObjectiveStore;
101
Thomas Vachuska866b46a2015-04-30 00:26:55 -0700102 // Note: This must remain an optional dependency to allow re-install of default drivers.
103 // Note: For now disabled until we can move to OPTIONAL_UNARY dependency
104 // @Reference(cardinality = ReferenceCardinality.OPTIONAL_UNARY, policy = ReferencePolicy.DYNAMIC)
105 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
106 protected DefaultDriverProviderService defaultDriverService;
107
alshabib2a441c62015-04-13 18:39:38 -0700108 private final FlowObjectiveStoreDelegate delegate = new InternalStoreDelegate();
Thomas Vachuskaca88bb72015-04-08 19:38:02 -0700109
110 private final Map<DeviceId, DriverHandler> driverHandlers = Maps.newConcurrentMap();
alshabib910aff12015-04-09 16:55:57 -0700111 private final Map<DeviceId, Pipeliner> pipeliners = Maps.newConcurrentMap();
Thomas Vachuskaca88bb72015-04-08 19:38:02 -0700112
113 private final PipelinerContext context = new InnerPipelineContext();
114 private final MastershipListener mastershipListener = new InnerMastershipListener();
115 private final DeviceListener deviceListener = new InnerDeviceListener();
116
alshabib77b88482015-04-07 15:47:50 -0700117 protected ServiceDirectory serviceDirectory = new DefaultServiceDirectory();
alshabib910aff12015-04-09 16:55:57 -0700118
Thomas Vachuska866b46a2015-04-30 00:26:55 -0700119 private Map<Integer, Set<PendingNext>> pendingForwards = Maps.newConcurrentMap();
alshabib2a441c62015-04-13 18:39:38 -0700120
Jonathan Hart17d00452015-04-21 17:10:00 -0700121 private ExecutorService executorService;
alshabib2a441c62015-04-13 18:39:38 -0700122
alshabib77b88482015-04-07 15:47:50 -0700123 @Activate
124 protected void activate() {
Thomas Vachuska866b46a2015-04-30 00:26:55 -0700125 executorService = newFixedThreadPool(4, groupedThreads("onos/objective-installer", "%d"));
alshabib2a441c62015-04-13 18:39:38 -0700126 flowObjectiveStore.setDelegate(delegate);
alshabib77b88482015-04-07 15:47:50 -0700127 mastershipService.addListener(mastershipListener);
alshabibaebe7752015-04-07 17:45:42 -0700128 deviceService.addListener(deviceListener);
Thomas Vachuskaca88bb72015-04-08 19:38:02 -0700129 deviceService.getDevices().forEach(device -> setupPipelineHandler(device.id()));
alshabib77b88482015-04-07 15:47:50 -0700130 log.info("Started");
131 }
132
133 @Deactivate
134 protected void deactivate() {
alshabib2a441c62015-04-13 18:39:38 -0700135 flowObjectiveStore.unsetDelegate(delegate);
alshabib77b88482015-04-07 15:47:50 -0700136 mastershipService.removeListener(mastershipListener);
alshabibaebe7752015-04-07 17:45:42 -0700137 deviceService.removeListener(deviceListener);
Thomas Vachuska866b46a2015-04-30 00:26:55 -0700138 executorService.shutdown();
139 pipeliners.clear();
140 driverHandlers.clear();
alshabib77b88482015-04-07 15:47:50 -0700141 log.info("Stopped");
142 }
143
Jonathan Hart17d00452015-04-21 17:10:00 -0700144 /**
Thomas Vachuska866b46a2015-04-30 00:26:55 -0700145 * Hook for binding the optional default driver providers.
146 *
147 * @param service arriving default driver provider service
148 */
149 // Note: For now disabled until we can move to OPTIONAL_UNARY dependency
150 protected void xbindDefaultDriverService(DefaultDriverProviderService service) {
151 log.info("Detected default drivers... going active");
152 defaultDriverService = service;
153 deviceService.getDevices().forEach(device -> setupPipelineHandler(device.id()));
154 }
155
156 /**
157 * Hook for unbinding the optional default driver providers.
158 *
159 * @param service departing default driver provider service
160 */
161 // Note: For now disabled until we can move to OPTIONAL_UNARY dependency
162 protected void xunbindDefaultDriverService(DefaultDriverProviderService service) {
163 log.info("Lost default drivers... going dormant");
164 defaultDriverService = null;
165 pipeliners.clear();
166 driverHandlers.clear();
167 }
168
169
170 /**
Jonathan Hart17d00452015-04-21 17:10:00 -0700171 * Task that passes the flow objective down to the driver. The task will
172 * make a few attempts to find the appropriate driver, then eventually give
173 * up and report an error if no suitable driver could be found.
174 */
175 private class ObjectiveInstaller implements Runnable {
176 private final DeviceId deviceId;
177 private final Objective objective;
178
179 private int numAttempts = 0;
180
181 public ObjectiveInstaller(DeviceId deviceId, Objective objective) {
182 this.deviceId = deviceId;
183 this.objective = objective;
alshabib910aff12015-04-09 16:55:57 -0700184 }
alshabib77b88482015-04-07 15:47:50 -0700185
Jonathan Hart17d00452015-04-21 17:10:00 -0700186 @Override
187 public void run() {
188 try {
189 numAttempts++;
190
191 Pipeliner pipeliner = getDevicePipeliner(deviceId);
192
193 if (pipeliner != null) {
194 if (objective instanceof NextObjective) {
195 pipeliner.next((NextObjective) objective);
196 } else if (objective instanceof ForwardingObjective) {
197 pipeliner.forward((ForwardingObjective) objective);
198 } else {
199 pipeliner.filter((FilteringObjective) objective);
200 }
201 } else if (numAttempts < INSTALL_RETRY_ATTEMPTS) {
Saurav Das3d038262015-04-23 12:36:58 -0700202 Thread.sleep(INSTALL_RETRY_INTERVAL);
Jonathan Hart17d00452015-04-21 17:10:00 -0700203 executorService.submit(this);
204 } else {
205 // Otherwise we've tried a few times and failed, report an
206 // error back to the user.
207 objective.context().ifPresent(
208 c -> c.onError(objective, ObjectiveError.DEVICEMISSING));
209 }
210 } catch (Exception e) {
211 log.warn("Exception while installing flow objective", e);
212 }
213 }
214 }
215
216 @Override
217 public void filter(DeviceId deviceId, FilteringObjective filteringObjective) {
Changhoon Yoon541ef712015-05-23 17:18:34 +0900218 checkPermission(Permission.FLOWRULE_WRITE);
219
Jonathan Hart17d00452015-04-21 17:10:00 -0700220 executorService.submit(new ObjectiveInstaller(deviceId, filteringObjective));
alshabib77b88482015-04-07 15:47:50 -0700221 }
222
223 @Override
Thomas Vachuska866b46a2015-04-30 00:26:55 -0700224 public void forward(DeviceId deviceId, ForwardingObjective forwardingObjective) {
Changhoon Yoon541ef712015-05-23 17:18:34 +0900225 checkPermission(Permission.FLOWRULE_WRITE);
226
alshabib2a441c62015-04-13 18:39:38 -0700227 if (queueObjective(deviceId, forwardingObjective)) {
228 return;
alshabib910aff12015-04-09 16:55:57 -0700229 }
Jonathan Hart17d00452015-04-21 17:10:00 -0700230 executorService.submit(new ObjectiveInstaller(deviceId, forwardingObjective));
Thomas Vachuskaca88bb72015-04-08 19:38:02 -0700231 }
232
alshabib2a441c62015-04-13 18:39:38 -0700233 @Override
Jonathan Hart17d00452015-04-21 17:10:00 -0700234 public void next(DeviceId deviceId, NextObjective nextObjective) {
Changhoon Yoon541ef712015-05-23 17:18:34 +0900235 checkPermission(Permission.FLOWRULE_WRITE);
236
Jonathan Hart17d00452015-04-21 17:10:00 -0700237 executorService.submit(new ObjectiveInstaller(deviceId, nextObjective));
alshabib2a441c62015-04-13 18:39:38 -0700238 }
239
alshabibf6ea9e62015-04-21 17:08:26 -0700240 @Override
241 public int allocateNextId() {
Changhoon Yoon541ef712015-05-23 17:18:34 +0900242 checkPermission(Permission.FLOWRULE_WRITE);
243
alshabibf6ea9e62015-04-21 17:08:26 -0700244 return flowObjectiveStore.allocateNextId();
245 }
246
alshabib2a441c62015-04-13 18:39:38 -0700247 private boolean queueObjective(DeviceId deviceId, ForwardingObjective fwd) {
248 if (fwd.nextId() != null &&
249 flowObjectiveStore.getNextGroup(fwd.nextId()) == null) {
Saurav Das3ea46622015-04-22 14:01:34 -0700250 log.trace("Queuing forwarding objective for nextId {}", fwd.nextId());
alshabib2a441c62015-04-13 18:39:38 -0700251 if (pendingForwards.putIfAbsent(fwd.nextId(),
Thomas Vachuska866b46a2015-04-30 00:26:55 -0700252 Sets.newHashSet(new PendingNext(deviceId, fwd))) != null) {
alshabib2a441c62015-04-13 18:39:38 -0700253 Set<PendingNext> pending = pendingForwards.get(fwd.nextId());
254 pending.add(new PendingNext(deviceId, fwd));
255 }
256 return true;
257 }
258 return false;
259 }
260
alshabib910aff12015-04-09 16:55:57 -0700261 // Retrieves the device pipeline behaviour from the cache.
Thomas Vachuskaca88bb72015-04-08 19:38:02 -0700262 private Pipeliner getDevicePipeliner(DeviceId deviceId) {
alshabib910aff12015-04-09 16:55:57 -0700263 Pipeliner pipeliner = pipeliners.get(deviceId);
alshabib910aff12015-04-09 16:55:57 -0700264 return pipeliner;
alshabib77b88482015-04-07 15:47:50 -0700265 }
266
alshabib2a441c62015-04-13 18:39:38 -0700267 private void setupPipelineHandler(DeviceId deviceId) {
Thomas Vachuska866b46a2015-04-30 00:26:55 -0700268 if (defaultDriverService == null) {
269 // We're not ready to go to work yet.
270 return;
271 }
272
Jonathan Hart17d00452015-04-21 17:10:00 -0700273 // Attempt to lookup the handler in the cache
274 DriverHandler handler = driverHandlers.get(deviceId);
275 if (handler == null) {
276 try {
277 // Otherwise create it and if it has pipeline behaviour, cache it
278 handler = driverService.createHandler(deviceId);
279 if (!handler.driver().hasBehaviour(Pipeliner.class)) {
280 log.warn("Pipeline behaviour not supported for device {}",
281 deviceId);
alshabib2a441c62015-04-13 18:39:38 -0700282 return;
283 }
Jonathan Hart17d00452015-04-21 17:10:00 -0700284 } catch (ItemNotFoundException e) {
285 log.warn("No applicable driver for device {}", deviceId);
286 return;
alshabib2a441c62015-04-13 18:39:38 -0700287 }
288
Jonathan Hart17d00452015-04-21 17:10:00 -0700289 driverHandlers.put(deviceId, handler);
alshabib2a441c62015-04-13 18:39:38 -0700290 }
Jonathan Hart17d00452015-04-21 17:10:00 -0700291
292 // Always (re)initialize the pipeline behaviour
293 log.info("Driver {} bound to device {} ... initializing driver",
294 handler.driver().name(), deviceId);
295 Pipeliner pipeliner = handler.behaviour(Pipeliner.class);
296 pipeliner.init(deviceId, context);
297 pipeliners.putIfAbsent(deviceId, pipeliner);
alshabib2a441c62015-04-13 18:39:38 -0700298 }
alshabibaebe7752015-04-07 17:45:42 -0700299
Thomas Vachuskaca88bb72015-04-08 19:38:02 -0700300 // Triggers driver setup when the local node becomes a device master.
alshabib77b88482015-04-07 15:47:50 -0700301 private class InnerMastershipListener implements MastershipListener {
302 @Override
303 public void event(MastershipEvent event) {
304 switch (event.type()) {
alshabib77b88482015-04-07 15:47:50 -0700305 case MASTER_CHANGED:
Jonathan Hart17d00452015-04-21 17:10:00 -0700306 log.info("mastership changed on device {}", event.subject());
Saurav Das3d038262015-04-23 12:36:58 -0700307 if (deviceService.isAvailable(event.subject())) {
308 setupPipelineHandler(event.subject());
309 }
alshabib4313d102015-04-08 18:55:08 -0700310 break;
311 case BACKUPS_CHANGED:
alshabib77b88482015-04-07 15:47:50 -0700312 break;
313 default:
Thomas Vachuskaca88bb72015-04-08 19:38:02 -0700314 break;
alshabib77b88482015-04-07 15:47:50 -0700315 }
316 }
alshabibaebe7752015-04-07 17:45:42 -0700317 }
alshabib77b88482015-04-07 15:47:50 -0700318
Thomas Vachuskaca88bb72015-04-08 19:38:02 -0700319 // Triggers driver setup when a device is (re)detected.
alshabibaebe7752015-04-07 17:45:42 -0700320 private class InnerDeviceListener implements DeviceListener {
321 @Override
322 public void event(DeviceEvent event) {
323 switch (event.type()) {
324 case DEVICE_ADDED:
325 case DEVICE_AVAILABILITY_CHANGED:
Saurav Das3ea46622015-04-22 14:01:34 -0700326 log.info("Device either added or availability changed {}",
327 event.subject().id());
alshabib4313d102015-04-08 18:55:08 -0700328 if (deviceService.isAvailable(event.subject().id())) {
Saurav Das3ea46622015-04-22 14:01:34 -0700329 log.info("Device is now available {}", event.subject().id());
alshabib4313d102015-04-08 18:55:08 -0700330 setupPipelineHandler(event.subject().id());
331 }
332 break;
333 case DEVICE_UPDATED:
334 break;
335 case DEVICE_REMOVED:
336 break;
337 case DEVICE_SUSPENDED:
338 break;
339 case PORT_ADDED:
340 break;
341 case PORT_UPDATED:
342 break;
343 case PORT_REMOVED:
alshabibaebe7752015-04-07 17:45:42 -0700344 break;
345 default:
Thomas Vachuskaca88bb72015-04-08 19:38:02 -0700346 break;
alshabibaebe7752015-04-07 17:45:42 -0700347 }
alshabib77b88482015-04-07 15:47:50 -0700348 }
349 }
alshabibaebe7752015-04-07 17:45:42 -0700350
Thomas Vachuskaca88bb72015-04-08 19:38:02 -0700351 // Processing context for initializing pipeline driver behaviours.
352 private class InnerPipelineContext implements PipelinerContext {
353 @Override
354 public ServiceDirectory directory() {
355 return serviceDirectory;
alshabibaebe7752015-04-07 17:45:42 -0700356 }
alshabib2a441c62015-04-13 18:39:38 -0700357
358 @Override
359 public FlowObjectiveStore store() {
360 return flowObjectiveStore;
361 }
alshabib2a441c62015-04-13 18:39:38 -0700362 }
363
364 private class InternalStoreDelegate implements FlowObjectiveStoreDelegate {
365 @Override
366 public void notify(ObjectiveEvent event) {
Saurav Das3ea46622015-04-22 14:01:34 -0700367 log.debug("Received notification of obj event {}", event);
alshabib2a441c62015-04-13 18:39:38 -0700368 Set<PendingNext> pending = pendingForwards.remove(event.subject());
369
370 if (pending == null) {
Saurav Das3ea46622015-04-22 14:01:34 -0700371 log.debug("Nothing pending for this obj event");
alshabib2a441c62015-04-13 18:39:38 -0700372 return;
373 }
374
Saurav Das3ea46622015-04-22 14:01:34 -0700375 log.debug("Processing pending forwarding objectives {}", pending.size());
alshabib2a441c62015-04-13 18:39:38 -0700376
377 pending.forEach(p -> getDevicePipeliner(p.deviceId())
378 .forward(p.forwardingObjective()));
379
380 }
381 }
382
383 /**
384 * Data class used to hold a pending forwarding objective that could not
385 * be processed because the associated next object was not present.
386 */
387 private class PendingNext {
388 private final DeviceId deviceId;
389 private final ForwardingObjective fwd;
390
391 public PendingNext(DeviceId deviceId, ForwardingObjective fwd) {
392 this.deviceId = deviceId;
393 this.fwd = fwd;
394 }
395
396 public DeviceId deviceId() {
397 return deviceId;
398 }
399
400 public ForwardingObjective forwardingObjective() {
401 return fwd;
402 }
alshabibaebe7752015-04-07 17:45:42 -0700403 }
alshabib77b88482015-04-07 15:47:50 -0700404}