blob: 7cbc67f6f753eef2fe19bb921b947302dd4efdab [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
Sho SHIMIZUf45d85d2015-07-01 14:39:11 -070061import static com.google.common.base.Preconditions.checkNotNull;
Thomas Vachuska866b46a2015-04-30 00:26:55 -070062import static java.util.concurrent.Executors.newFixedThreadPool;
Jonathan Hart17d00452015-04-21 17:10:00 -070063import static org.onlab.util.Tools.groupedThreads;
Changhoon Yoon541ef712015-05-23 17:18:34 +090064import static org.onosproject.security.AppGuard.checkPermission;
65
alshabib77b88482015-04-07 15:47:50 -070066
67/**
Thomas Vachuskaca88bb72015-04-08 19:38:02 -070068 * Provides implementation of the flow objective programming service.
alshabib77b88482015-04-07 15:47:50 -070069 */
alshabib2a441c62015-04-13 18:39:38 -070070@Component(immediate = true)
alshabib77b88482015-04-07 15:47:50 -070071@Service
72public class FlowObjectiveManager implements FlowObjectiveService {
73
Saurav Dasbd7f7422015-04-23 16:31:47 -070074 public static final int INSTALL_RETRY_ATTEMPTS = 5;
Jonathan Hart17d00452015-04-21 17:10:00 -070075 public static final long INSTALL_RETRY_INTERVAL = 1000; // ms
alshabib77b88482015-04-07 15:47:50 -070076
Jonathan Hart17d00452015-04-21 17:10:00 -070077 private final Logger log = LoggerFactory.getLogger(getClass());
Thomas Vachuskaca88bb72015-04-08 19:38:02 -070078
alshabib77b88482015-04-07 15:47:50 -070079 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
80 protected DriverService driverService;
81
82 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
83 protected DeviceService deviceService;
84
85 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
86 protected MastershipService mastershipService;
87
88 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
89 protected ClusterService clusterService;
90
Thomas Vachuskaca88bb72015-04-08 19:38:02 -070091 // Note: The following dependencies are added on behalf of the pipeline
92 // driver behaviours to assure these services are available for their
93 // initialization.
94 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
95 protected FlowRuleService flowRuleService;
96
97 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
98 protected GroupService groupService;
99
alshabib2a441c62015-04-13 18:39:38 -0700100 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
101 protected FlowObjectiveStore flowObjectiveStore;
102
Thomas Vachuska866b46a2015-04-30 00:26:55 -0700103 // Note: This must remain an optional dependency to allow re-install of default drivers.
104 // Note: For now disabled until we can move to OPTIONAL_UNARY dependency
105 // @Reference(cardinality = ReferenceCardinality.OPTIONAL_UNARY, policy = ReferencePolicy.DYNAMIC)
106 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
107 protected DefaultDriverProviderService defaultDriverService;
108
alshabib2a441c62015-04-13 18:39:38 -0700109 private final FlowObjectiveStoreDelegate delegate = new InternalStoreDelegate();
Thomas Vachuskaca88bb72015-04-08 19:38:02 -0700110
111 private final Map<DeviceId, DriverHandler> driverHandlers = Maps.newConcurrentMap();
alshabib910aff12015-04-09 16:55:57 -0700112 private final Map<DeviceId, Pipeliner> pipeliners = Maps.newConcurrentMap();
Thomas Vachuskaca88bb72015-04-08 19:38:02 -0700113
114 private final PipelinerContext context = new InnerPipelineContext();
115 private final MastershipListener mastershipListener = new InnerMastershipListener();
116 private final DeviceListener deviceListener = new InnerDeviceListener();
117
alshabib77b88482015-04-07 15:47:50 -0700118 protected ServiceDirectory serviceDirectory = new DefaultServiceDirectory();
alshabib910aff12015-04-09 16:55:57 -0700119
Thomas Vachuska866b46a2015-04-30 00:26:55 -0700120 private Map<Integer, Set<PendingNext>> pendingForwards = Maps.newConcurrentMap();
alshabib2a441c62015-04-13 18:39:38 -0700121
Jonathan Hart17d00452015-04-21 17:10:00 -0700122 private ExecutorService executorService;
alshabib2a441c62015-04-13 18:39:38 -0700123
alshabib77b88482015-04-07 15:47:50 -0700124 @Activate
125 protected void activate() {
Thomas Vachuska866b46a2015-04-30 00:26:55 -0700126 executorService = newFixedThreadPool(4, groupedThreads("onos/objective-installer", "%d"));
alshabib2a441c62015-04-13 18:39:38 -0700127 flowObjectiveStore.setDelegate(delegate);
alshabib77b88482015-04-07 15:47:50 -0700128 mastershipService.addListener(mastershipListener);
alshabibaebe7752015-04-07 17:45:42 -0700129 deviceService.addListener(deviceListener);
Thomas Vachuskaca88bb72015-04-08 19:38:02 -0700130 deviceService.getDevices().forEach(device -> setupPipelineHandler(device.id()));
alshabib77b88482015-04-07 15:47:50 -0700131 log.info("Started");
132 }
133
134 @Deactivate
135 protected void deactivate() {
alshabib2a441c62015-04-13 18:39:38 -0700136 flowObjectiveStore.unsetDelegate(delegate);
alshabib77b88482015-04-07 15:47:50 -0700137 mastershipService.removeListener(mastershipListener);
alshabibaebe7752015-04-07 17:45:42 -0700138 deviceService.removeListener(deviceListener);
Thomas Vachuska866b46a2015-04-30 00:26:55 -0700139 executorService.shutdown();
140 pipeliners.clear();
141 driverHandlers.clear();
alshabib77b88482015-04-07 15:47:50 -0700142 log.info("Stopped");
143 }
144
Jonathan Hart17d00452015-04-21 17:10:00 -0700145 /**
146 * Task that passes the flow objective down to the driver. The task will
147 * make a few attempts to find the appropriate driver, then eventually give
148 * up and report an error if no suitable driver could be found.
149 */
150 private class ObjectiveInstaller implements Runnable {
151 private final DeviceId deviceId;
152 private final Objective objective;
153
Sho SHIMIZUf45d85d2015-07-01 14:39:11 -0700154 private final int numAttempts;
Jonathan Hart17d00452015-04-21 17:10:00 -0700155
156 public ObjectiveInstaller(DeviceId deviceId, Objective objective) {
Sho SHIMIZUf45d85d2015-07-01 14:39:11 -0700157 this(deviceId, objective, 1);
158 }
159
160 public ObjectiveInstaller(DeviceId deviceId, Objective objective, int attemps) {
161 this.deviceId = checkNotNull(deviceId);
162 this.objective = checkNotNull(objective);
163 this.numAttempts = checkNotNull(attemps);
alshabib910aff12015-04-09 16:55:57 -0700164 }
alshabib77b88482015-04-07 15:47:50 -0700165
Jonathan Hart17d00452015-04-21 17:10:00 -0700166 @Override
167 public void run() {
168 try {
Jonathan Hart17d00452015-04-21 17:10:00 -0700169 Pipeliner pipeliner = getDevicePipeliner(deviceId);
170
171 if (pipeliner != null) {
172 if (objective instanceof NextObjective) {
173 pipeliner.next((NextObjective) objective);
174 } else if (objective instanceof ForwardingObjective) {
175 pipeliner.forward((ForwardingObjective) objective);
176 } else {
177 pipeliner.filter((FilteringObjective) objective);
178 }
179 } else if (numAttempts < INSTALL_RETRY_ATTEMPTS) {
Saurav Das3d038262015-04-23 12:36:58 -0700180 Thread.sleep(INSTALL_RETRY_INTERVAL);
Sho SHIMIZUf45d85d2015-07-01 14:39:11 -0700181 executorService.submit(new ObjectiveInstaller(deviceId, objective, numAttempts + 1));
Jonathan Hart17d00452015-04-21 17:10:00 -0700182 } else {
183 // Otherwise we've tried a few times and failed, report an
184 // error back to the user.
185 objective.context().ifPresent(
186 c -> c.onError(objective, ObjectiveError.DEVICEMISSING));
187 }
188 } catch (Exception e) {
189 log.warn("Exception while installing flow objective", e);
190 }
191 }
192 }
193
194 @Override
195 public void filter(DeviceId deviceId, FilteringObjective filteringObjective) {
Changhoon Yoon541ef712015-05-23 17:18:34 +0900196 checkPermission(Permission.FLOWRULE_WRITE);
197
Jonathan Hart17d00452015-04-21 17:10:00 -0700198 executorService.submit(new ObjectiveInstaller(deviceId, filteringObjective));
alshabib77b88482015-04-07 15:47:50 -0700199 }
200
201 @Override
Thomas Vachuska866b46a2015-04-30 00:26:55 -0700202 public void forward(DeviceId deviceId, ForwardingObjective forwardingObjective) {
Changhoon Yoon541ef712015-05-23 17:18:34 +0900203 checkPermission(Permission.FLOWRULE_WRITE);
204
alshabib2a441c62015-04-13 18:39:38 -0700205 if (queueObjective(deviceId, forwardingObjective)) {
206 return;
alshabib910aff12015-04-09 16:55:57 -0700207 }
Jonathan Hart17d00452015-04-21 17:10:00 -0700208 executorService.submit(new ObjectiveInstaller(deviceId, forwardingObjective));
Thomas Vachuskaca88bb72015-04-08 19:38:02 -0700209 }
210
alshabib2a441c62015-04-13 18:39:38 -0700211 @Override
Jonathan Hart17d00452015-04-21 17:10:00 -0700212 public void next(DeviceId deviceId, NextObjective nextObjective) {
Changhoon Yoon541ef712015-05-23 17:18:34 +0900213 checkPermission(Permission.FLOWRULE_WRITE);
214
Jonathan Hart17d00452015-04-21 17:10:00 -0700215 executorService.submit(new ObjectiveInstaller(deviceId, nextObjective));
alshabib2a441c62015-04-13 18:39:38 -0700216 }
217
alshabibf6ea9e62015-04-21 17:08:26 -0700218 @Override
219 public int allocateNextId() {
Changhoon Yoon541ef712015-05-23 17:18:34 +0900220 checkPermission(Permission.FLOWRULE_WRITE);
221
alshabibf6ea9e62015-04-21 17:08:26 -0700222 return flowObjectiveStore.allocateNextId();
223 }
224
alshabib2a441c62015-04-13 18:39:38 -0700225 private boolean queueObjective(DeviceId deviceId, ForwardingObjective fwd) {
226 if (fwd.nextId() != null &&
227 flowObjectiveStore.getNextGroup(fwd.nextId()) == null) {
Saurav Das3ea46622015-04-22 14:01:34 -0700228 log.trace("Queuing forwarding objective for nextId {}", fwd.nextId());
alshabib2a441c62015-04-13 18:39:38 -0700229 if (pendingForwards.putIfAbsent(fwd.nextId(),
Thomas Vachuska866b46a2015-04-30 00:26:55 -0700230 Sets.newHashSet(new PendingNext(deviceId, fwd))) != null) {
alshabib2a441c62015-04-13 18:39:38 -0700231 Set<PendingNext> pending = pendingForwards.get(fwd.nextId());
232 pending.add(new PendingNext(deviceId, fwd));
233 }
234 return true;
235 }
236 return false;
237 }
238
alshabib910aff12015-04-09 16:55:57 -0700239 // Retrieves the device pipeline behaviour from the cache.
Thomas Vachuskaca88bb72015-04-08 19:38:02 -0700240 private Pipeliner getDevicePipeliner(DeviceId deviceId) {
alshabib910aff12015-04-09 16:55:57 -0700241 Pipeliner pipeliner = pipeliners.get(deviceId);
alshabib910aff12015-04-09 16:55:57 -0700242 return pipeliner;
alshabib77b88482015-04-07 15:47:50 -0700243 }
244
alshabib2a441c62015-04-13 18:39:38 -0700245 private void setupPipelineHandler(DeviceId deviceId) {
Thomas Vachuska866b46a2015-04-30 00:26:55 -0700246 if (defaultDriverService == null) {
247 // We're not ready to go to work yet.
248 return;
249 }
250
Jonathan Hart17d00452015-04-21 17:10:00 -0700251 // Attempt to lookup the handler in the cache
252 DriverHandler handler = driverHandlers.get(deviceId);
253 if (handler == null) {
254 try {
255 // Otherwise create it and if it has pipeline behaviour, cache it
256 handler = driverService.createHandler(deviceId);
257 if (!handler.driver().hasBehaviour(Pipeliner.class)) {
258 log.warn("Pipeline behaviour not supported for device {}",
259 deviceId);
alshabib2a441c62015-04-13 18:39:38 -0700260 return;
261 }
Jonathan Hart17d00452015-04-21 17:10:00 -0700262 } catch (ItemNotFoundException e) {
263 log.warn("No applicable driver for device {}", deviceId);
264 return;
alshabib2a441c62015-04-13 18:39:38 -0700265 }
266
Jonathan Hart17d00452015-04-21 17:10:00 -0700267 driverHandlers.put(deviceId, handler);
alshabib2a441c62015-04-13 18:39:38 -0700268 }
Jonathan Hart17d00452015-04-21 17:10:00 -0700269
270 // Always (re)initialize the pipeline behaviour
271 log.info("Driver {} bound to device {} ... initializing driver",
272 handler.driver().name(), deviceId);
273 Pipeliner pipeliner = handler.behaviour(Pipeliner.class);
274 pipeliner.init(deviceId, context);
275 pipeliners.putIfAbsent(deviceId, pipeliner);
alshabib2a441c62015-04-13 18:39:38 -0700276 }
alshabibaebe7752015-04-07 17:45:42 -0700277
Thomas Vachuskaca88bb72015-04-08 19:38:02 -0700278 // Triggers driver setup when the local node becomes a device master.
alshabib77b88482015-04-07 15:47:50 -0700279 private class InnerMastershipListener implements MastershipListener {
280 @Override
281 public void event(MastershipEvent event) {
282 switch (event.type()) {
alshabib77b88482015-04-07 15:47:50 -0700283 case MASTER_CHANGED:
Madan Jampani0174f452015-05-29 11:52:05 -0700284 log.debug("mastership changed on device {}", event.subject());
Saurav Das3d038262015-04-23 12:36:58 -0700285 if (deviceService.isAvailable(event.subject())) {
286 setupPipelineHandler(event.subject());
287 }
alshabib4313d102015-04-08 18:55:08 -0700288 break;
289 case BACKUPS_CHANGED:
alshabib77b88482015-04-07 15:47:50 -0700290 break;
291 default:
Thomas Vachuskaca88bb72015-04-08 19:38:02 -0700292 break;
alshabib77b88482015-04-07 15:47:50 -0700293 }
294 }
alshabibaebe7752015-04-07 17:45:42 -0700295 }
alshabib77b88482015-04-07 15:47:50 -0700296
Thomas Vachuskaca88bb72015-04-08 19:38:02 -0700297 // Triggers driver setup when a device is (re)detected.
alshabibaebe7752015-04-07 17:45:42 -0700298 private class InnerDeviceListener implements DeviceListener {
299 @Override
300 public void event(DeviceEvent event) {
301 switch (event.type()) {
302 case DEVICE_ADDED:
303 case DEVICE_AVAILABILITY_CHANGED:
Madan Jampani0174f452015-05-29 11:52:05 -0700304 log.debug("Device either added or availability changed {}",
Saurav Das3ea46622015-04-22 14:01:34 -0700305 event.subject().id());
alshabib4313d102015-04-08 18:55:08 -0700306 if (deviceService.isAvailable(event.subject().id())) {
Madan Jampani0174f452015-05-29 11:52:05 -0700307 log.debug("Device is now available {}", event.subject().id());
alshabib4313d102015-04-08 18:55:08 -0700308 setupPipelineHandler(event.subject().id());
309 }
310 break;
311 case DEVICE_UPDATED:
312 break;
313 case DEVICE_REMOVED:
314 break;
315 case DEVICE_SUSPENDED:
316 break;
317 case PORT_ADDED:
318 break;
319 case PORT_UPDATED:
320 break;
321 case PORT_REMOVED:
alshabibaebe7752015-04-07 17:45:42 -0700322 break;
323 default:
Thomas Vachuskaca88bb72015-04-08 19:38:02 -0700324 break;
alshabibaebe7752015-04-07 17:45:42 -0700325 }
alshabib77b88482015-04-07 15:47:50 -0700326 }
327 }
alshabibaebe7752015-04-07 17:45:42 -0700328
Thomas Vachuskaca88bb72015-04-08 19:38:02 -0700329 // Processing context for initializing pipeline driver behaviours.
330 private class InnerPipelineContext implements PipelinerContext {
331 @Override
332 public ServiceDirectory directory() {
333 return serviceDirectory;
alshabibaebe7752015-04-07 17:45:42 -0700334 }
alshabib2a441c62015-04-13 18:39:38 -0700335
336 @Override
337 public FlowObjectiveStore store() {
338 return flowObjectiveStore;
339 }
alshabib2a441c62015-04-13 18:39:38 -0700340 }
341
342 private class InternalStoreDelegate implements FlowObjectiveStoreDelegate {
343 @Override
344 public void notify(ObjectiveEvent event) {
Saurav Das3ea46622015-04-22 14:01:34 -0700345 log.debug("Received notification of obj event {}", event);
alshabib2a441c62015-04-13 18:39:38 -0700346 Set<PendingNext> pending = pendingForwards.remove(event.subject());
347
348 if (pending == null) {
Saurav Das3ea46622015-04-22 14:01:34 -0700349 log.debug("Nothing pending for this obj event");
alshabib2a441c62015-04-13 18:39:38 -0700350 return;
351 }
352
Saurav Das3ea46622015-04-22 14:01:34 -0700353 log.debug("Processing pending forwarding objectives {}", pending.size());
alshabib2a441c62015-04-13 18:39:38 -0700354
355 pending.forEach(p -> getDevicePipeliner(p.deviceId())
356 .forward(p.forwardingObjective()));
357
358 }
359 }
360
361 /**
362 * Data class used to hold a pending forwarding objective that could not
363 * be processed because the associated next object was not present.
364 */
365 private class PendingNext {
366 private final DeviceId deviceId;
367 private final ForwardingObjective fwd;
368
369 public PendingNext(DeviceId deviceId, ForwardingObjective fwd) {
370 this.deviceId = deviceId;
371 this.fwd = fwd;
372 }
373
374 public DeviceId deviceId() {
375 return deviceId;
376 }
377
378 public ForwardingObjective forwardingObjective() {
379 return fwd;
380 }
alshabibaebe7752015-04-07 17:45:42 -0700381 }
alshabib77b88482015-04-07 15:47:50 -0700382}