blob: f51ee7c6eb91f8def7314f6b7449900dc8c2e837 [file] [log] [blame]
alshabib77b88482015-04-07 15:47:50 -07001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2015-present Open Networking Laboratory
alshabib77b88482015-04-07 15:47:50 -07003 *
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;
Thomas Vachuskad27097c2016-06-14 19:10:41 -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;
Yi Tseng374c5f32017-03-05 22:51:35 -080023import org.apache.felix.scr.annotations.Modified;
24import org.apache.felix.scr.annotations.Property;
alshabib77b88482015-04-07 15:47:50 -070025import org.apache.felix.scr.annotations.Reference;
26import org.apache.felix.scr.annotations.ReferenceCardinality;
27import org.apache.felix.scr.annotations.Service;
28import org.onlab.osgi.DefaultServiceDirectory;
29import org.onlab.osgi.ServiceDirectory;
Thomas Vachuskaca88bb72015-04-08 19:38:02 -070030import org.onlab.util.ItemNotFoundException;
Yi Tseng374c5f32017-03-05 22:51:35 -080031import org.onlab.util.Tools;
32import org.onosproject.cfg.ComponentConfigService;
alshabib77b88482015-04-07 15:47:50 -070033import org.onosproject.cluster.ClusterService;
alshabib77b88482015-04-07 15:47:50 -070034import org.onosproject.net.DeviceId;
Saurav Das24431192016-03-07 19:13:00 -080035import org.onosproject.net.behaviour.NextGroup;
alshabib77b88482015-04-07 15:47:50 -070036import org.onosproject.net.behaviour.Pipeliner;
Thomas Vachuskaca88bb72015-04-08 19:38:02 -070037import org.onosproject.net.behaviour.PipelinerContext;
alshabibaebe7752015-04-07 17:45:42 -070038import org.onosproject.net.device.DeviceEvent;
39import org.onosproject.net.device.DeviceListener;
alshabib77b88482015-04-07 15:47:50 -070040import org.onosproject.net.device.DeviceService;
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;
Saurav Das1547b3f2017-05-05 17:01:08 -070051import org.onosproject.net.flowobjective.Objective.Operation;
Jonathan Hart17d00452015-04-21 17:10:00 -070052import org.onosproject.net.flowobjective.ObjectiveError;
alshabib2a441c62015-04-13 18:39:38 -070053import org.onosproject.net.flowobjective.ObjectiveEvent;
Saurav Das423fe2b2015-12-04 10:52:59 -080054import org.onosproject.net.flowobjective.ObjectiveEvent.Type;
Thomas Vachuskaca88bb72015-04-08 19:38:02 -070055import org.onosproject.net.group.GroupService;
Yi Tseng374c5f32017-03-05 22:51:35 -080056import org.osgi.service.component.ComponentContext;
alshabib77b88482015-04-07 15:47:50 -070057import org.slf4j.Logger;
58import org.slf4j.LoggerFactory;
59
Saurav Das24431192016-03-07 19:13:00 -080060import java.util.ArrayList;
Saurav Das24431192016-03-07 19:13:00 -080061import java.util.List;
alshabib77b88482015-04-07 15:47:50 -070062import java.util.Map;
Saurav Das8a0732e2015-11-20 15:27:53 -080063import java.util.Objects;
alshabib2a441c62015-04-13 18:39:38 -070064import java.util.Set;
Jonathan Hart17d00452015-04-21 17:10:00 -070065import java.util.concurrent.ExecutorService;
alshabib77b88482015-04-07 15:47:50 -070066
Sho SHIMIZUf45d85d2015-07-01 14:39:11 -070067import static com.google.common.base.Preconditions.checkNotNull;
Yi Tseng374c5f32017-03-05 22:51:35 -080068import static com.google.common.base.Strings.isNullOrEmpty;
Thomas Vachuska866b46a2015-04-30 00:26:55 -070069import static java.util.concurrent.Executors.newFixedThreadPool;
Jonathan Hart17d00452015-04-21 17:10:00 -070070import static org.onlab.util.Tools.groupedThreads;
Changhoon Yoon541ef712015-05-23 17:18:34 +090071import static org.onosproject.security.AppGuard.checkPermission;
Thomas Vachuskad27097c2016-06-14 19:10:41 -070072import static org.onosproject.security.AppPermission.Type.FLOWRULE_WRITE;
alshabib77b88482015-04-07 15:47:50 -070073
74/**
Thomas Vachuskaca88bb72015-04-08 19:38:02 -070075 * Provides implementation of the flow objective programming service.
alshabib77b88482015-04-07 15:47:50 -070076 */
alshabib2a441c62015-04-13 18:39:38 -070077@Component(immediate = true)
alshabib77b88482015-04-07 15:47:50 -070078@Service
79public class FlowObjectiveManager implements FlowObjectiveService {
80
Saurav Dasbd7f7422015-04-23 16:31:47 -070081 public static final int INSTALL_RETRY_ATTEMPTS = 5;
Jonathan Hart17d00452015-04-21 17:10:00 -070082 public static final long INSTALL_RETRY_INTERVAL = 1000; // ms
alshabib77b88482015-04-07 15:47:50 -070083
Yi Tseng374c5f32017-03-05 22:51:35 -080084 private static final String WORKER_PATTERN = "objective-installer-%d";
85 private static final String GROUP_THREAD_NAME = "onos/objective-installer";
86 private static final String NUM_THREAD = "numThreads";
87
Jonathan Hart17d00452015-04-21 17:10:00 -070088 private final Logger log = LoggerFactory.getLogger(getClass());
Thomas Vachuskaca88bb72015-04-08 19:38:02 -070089
Yi Tseng9753fc12017-03-10 18:42:46 -080090 private static final int DEFAULT_NUM_THREADS = 4;
Yi Tseng374c5f32017-03-05 22:51:35 -080091 @Property(name = NUM_THREAD,
92 intValue = DEFAULT_NUM_THREADS,
93 label = "Number of worker threads")
94 private int numThreads = DEFAULT_NUM_THREADS;
95
alshabib77b88482015-04-07 15:47:50 -070096 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
97 protected DriverService driverService;
98
99 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
100 protected DeviceService deviceService;
101
102 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
alshabib77b88482015-04-07 15:47:50 -0700103 protected ClusterService clusterService;
104
Thomas Vachuskaca88bb72015-04-08 19:38:02 -0700105 // Note: The following dependencies are added on behalf of the pipeline
106 // driver behaviours to assure these services are available for their
107 // initialization.
108 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
109 protected FlowRuleService flowRuleService;
110
111 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
112 protected GroupService groupService;
113
alshabib2a441c62015-04-13 18:39:38 -0700114 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
115 protected FlowObjectiveStore flowObjectiveStore;
116
Yi Tseng374c5f32017-03-05 22:51:35 -0800117 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
118 protected ComponentConfigService cfgService;
119
alshabib2a441c62015-04-13 18:39:38 -0700120 private final FlowObjectiveStoreDelegate delegate = new InternalStoreDelegate();
Thomas Vachuskaca88bb72015-04-08 19:38:02 -0700121
122 private final Map<DeviceId, DriverHandler> driverHandlers = Maps.newConcurrentMap();
alshabib910aff12015-04-09 16:55:57 -0700123 private final Map<DeviceId, Pipeliner> pipeliners = Maps.newConcurrentMap();
Thomas Vachuskaca88bb72015-04-08 19:38:02 -0700124
125 private final PipelinerContext context = new InnerPipelineContext();
Thomas Vachuskaca88bb72015-04-08 19:38:02 -0700126 private final DeviceListener deviceListener = new InnerDeviceListener();
127
alshabib77b88482015-04-07 15:47:50 -0700128 protected ServiceDirectory serviceDirectory = new DefaultServiceDirectory();
alshabib910aff12015-04-09 16:55:57 -0700129
Saurav Das1547b3f2017-05-05 17:01:08 -0700130 // local stores for queuing fwd and next objectives that are waiting for an
131 // associated next objective execution to complete. The signal for completed
132 // execution comes from a pipeline driver, in this or another controller
133 // instance, via the DistributedFlowObjectiveStore.
134 private final Map<Integer, Set<PendingFlowObjective>> pendingForwards =
135 Maps.newConcurrentMap();
136 private final Map<Integer, Set<PendingFlowObjective>> pendingNexts =
137 Maps.newConcurrentMap();
alshabib2a441c62015-04-13 18:39:38 -0700138
Saurav Das24431192016-03-07 19:13:00 -0800139 // local store to track which nextObjectives were sent to which device
140 // for debugging purposes
141 private Map<Integer, DeviceId> nextToDevice = Maps.newConcurrentMap();
142
Jonathan Hart17d00452015-04-21 17:10:00 -0700143 private ExecutorService executorService;
alshabib2a441c62015-04-13 18:39:38 -0700144
alshabib77b88482015-04-07 15:47:50 -0700145 @Activate
146 protected void activate() {
Yi Tseng374c5f32017-03-05 22:51:35 -0800147 cfgService.registerProperties(getClass());
148 executorService = newFixedThreadPool(numThreads,
149 groupedThreads(GROUP_THREAD_NAME, WORKER_PATTERN, log));
alshabib2a441c62015-04-13 18:39:38 -0700150 flowObjectiveStore.setDelegate(delegate);
alshabibaebe7752015-04-07 17:45:42 -0700151 deviceService.addListener(deviceListener);
alshabib77b88482015-04-07 15:47:50 -0700152 log.info("Started");
153 }
154
155 @Deactivate
156 protected void deactivate() {
Yi Tseng374c5f32017-03-05 22:51:35 -0800157 cfgService.unregisterProperties(getClass(), false);
alshabib2a441c62015-04-13 18:39:38 -0700158 flowObjectiveStore.unsetDelegate(delegate);
alshabibaebe7752015-04-07 17:45:42 -0700159 deviceService.removeListener(deviceListener);
Thomas Vachuska866b46a2015-04-30 00:26:55 -0700160 executorService.shutdown();
161 pipeliners.clear();
162 driverHandlers.clear();
Saurav Das24431192016-03-07 19:13:00 -0800163 nextToDevice.clear();
alshabib77b88482015-04-07 15:47:50 -0700164 log.info("Stopped");
165 }
166
Yi Tseng374c5f32017-03-05 22:51:35 -0800167 @Modified
168 protected void modified(ComponentContext context) {
169 String propertyValue =
170 Tools.get(context.getProperties(), NUM_THREAD);
171 int newNumThreads = isNullOrEmpty(propertyValue) ? numThreads : Integer.parseInt(propertyValue);
172
173 if (newNumThreads != numThreads && newNumThreads > 0) {
174 numThreads = newNumThreads;
175 ExecutorService oldWorkerExecutor = executorService;
176 executorService = newFixedThreadPool(numThreads,
177 groupedThreads(GROUP_THREAD_NAME, WORKER_PATTERN, log));
178 if (oldWorkerExecutor != null) {
179 oldWorkerExecutor.shutdown();
180 }
181 log.info("Reconfigured number of worker threads to {}", numThreads);
182 }
183 }
184
Jonathan Hart17d00452015-04-21 17:10:00 -0700185 /**
186 * Task that passes the flow objective down to the driver. The task will
187 * make a few attempts to find the appropriate driver, then eventually give
188 * up and report an error if no suitable driver could be found.
189 */
190 private class ObjectiveInstaller implements Runnable {
191 private final DeviceId deviceId;
192 private final Objective objective;
193
Sho SHIMIZUf45d85d2015-07-01 14:39:11 -0700194 private final int numAttempts;
Jonathan Hart17d00452015-04-21 17:10:00 -0700195
196 public ObjectiveInstaller(DeviceId deviceId, Objective objective) {
Sho SHIMIZUf45d85d2015-07-01 14:39:11 -0700197 this(deviceId, objective, 1);
198 }
199
200 public ObjectiveInstaller(DeviceId deviceId, Objective objective, int attemps) {
201 this.deviceId = checkNotNull(deviceId);
202 this.objective = checkNotNull(objective);
203 this.numAttempts = checkNotNull(attemps);
alshabib910aff12015-04-09 16:55:57 -0700204 }
alshabib77b88482015-04-07 15:47:50 -0700205
Jonathan Hart17d00452015-04-21 17:10:00 -0700206 @Override
207 public void run() {
208 try {
Jonathan Hart17d00452015-04-21 17:10:00 -0700209 Pipeliner pipeliner = getDevicePipeliner(deviceId);
210
211 if (pipeliner != null) {
212 if (objective instanceof NextObjective) {
Saurav Das1547b3f2017-05-05 17:01:08 -0700213 nextToDevice.put(objective.id(), deviceId);
Jonathan Hart17d00452015-04-21 17:10:00 -0700214 pipeliner.next((NextObjective) objective);
215 } else if (objective instanceof ForwardingObjective) {
216 pipeliner.forward((ForwardingObjective) objective);
217 } else {
218 pipeliner.filter((FilteringObjective) objective);
219 }
Andrea Campanella1f8188d2016-02-29 13:24:54 -0800220 //Attempts to check if pipeliner is null for retry attempts
Jonathan Hart17d00452015-04-21 17:10:00 -0700221 } else if (numAttempts < INSTALL_RETRY_ATTEMPTS) {
Saurav Das3d038262015-04-23 12:36:58 -0700222 Thread.sleep(INSTALL_RETRY_INTERVAL);
HIGUCHI Yutad9e01052016-04-14 09:31:42 -0700223 executorService.execute(new ObjectiveInstaller(deviceId, objective, numAttempts + 1));
Jonathan Hart17d00452015-04-21 17:10:00 -0700224 } else {
225 // Otherwise we've tried a few times and failed, report an
226 // error back to the user.
227 objective.context().ifPresent(
Andrea Campanella1f8188d2016-02-29 13:24:54 -0800228 c -> c.onError(objective, ObjectiveError.NOPIPELINER));
Jonathan Hart17d00452015-04-21 17:10:00 -0700229 }
Saurav Das1547b3f2017-05-05 17:01:08 -0700230 //Exception thrown
Jonathan Hart17d00452015-04-21 17:10:00 -0700231 } catch (Exception e) {
232 log.warn("Exception while installing flow objective", e);
233 }
234 }
235 }
236
237 @Override
238 public void filter(DeviceId deviceId, FilteringObjective filteringObjective) {
Changhoon Yoonb856b812015-08-10 03:47:19 +0900239 checkPermission(FLOWRULE_WRITE);
HIGUCHI Yutad9e01052016-04-14 09:31:42 -0700240 executorService.execute(new ObjectiveInstaller(deviceId, filteringObjective));
alshabib77b88482015-04-07 15:47:50 -0700241 }
242
243 @Override
Thomas Vachuska866b46a2015-04-30 00:26:55 -0700244 public void forward(DeviceId deviceId, ForwardingObjective forwardingObjective) {
Changhoon Yoonb856b812015-08-10 03:47:19 +0900245 checkPermission(FLOWRULE_WRITE);
Saurav Das1547b3f2017-05-05 17:01:08 -0700246 if (queueFwdObjective(deviceId, forwardingObjective)) {
alshabib2a441c62015-04-13 18:39:38 -0700247 return;
alshabib910aff12015-04-09 16:55:57 -0700248 }
HIGUCHI Yutad9e01052016-04-14 09:31:42 -0700249 executorService.execute(new ObjectiveInstaller(deviceId, forwardingObjective));
Thomas Vachuskaca88bb72015-04-08 19:38:02 -0700250 }
251
alshabib2a441c62015-04-13 18:39:38 -0700252 @Override
Jonathan Hart17d00452015-04-21 17:10:00 -0700253 public void next(DeviceId deviceId, NextObjective nextObjective) {
Changhoon Yoonb856b812015-08-10 03:47:19 +0900254 checkPermission(FLOWRULE_WRITE);
Saurav Das1547b3f2017-05-05 17:01:08 -0700255 if (queueNextObjective(deviceId, nextObjective)) {
256 return;
257 }
HIGUCHI Yutad9e01052016-04-14 09:31:42 -0700258 executorService.execute(new ObjectiveInstaller(deviceId, nextObjective));
alshabib2a441c62015-04-13 18:39:38 -0700259 }
260
alshabibf6ea9e62015-04-21 17:08:26 -0700261 @Override
262 public int allocateNextId() {
Changhoon Yoonb856b812015-08-10 03:47:19 +0900263 checkPermission(FLOWRULE_WRITE);
alshabibf6ea9e62015-04-21 17:08:26 -0700264 return flowObjectiveStore.allocateNextId();
265 }
266
Xin Jin313708b2015-07-09 13:43:04 -0700267 @Override
268 public void initPolicy(String policy) {}
269
Saurav Das1547b3f2017-05-05 17:01:08 -0700270 private boolean queueFwdObjective(DeviceId deviceId, ForwardingObjective fwd) {
Thomas Vachuskad27097c2016-06-14 19:10:41 -0700271 if (fwd.nextId() == null ||
Yi Tseng38fc71e2017-02-03 14:50:47 -0800272 flowObjectiveStore.getNextGroup(fwd.nextId()) != null ||
273 fwd.op() == Objective.Operation.REMOVE) {
Thomas Vachuskad27097c2016-06-14 19:10:41 -0700274 // fast path
275 return false;
alshabib2a441c62015-04-13 18:39:38 -0700276 }
Thomas Vachuskad27097c2016-06-14 19:10:41 -0700277 boolean queued = false;
278 synchronized (pendingForwards) {
279 // double check the flow objective store, because this block could run
280 // after a notification arrives
281 if (flowObjectiveStore.getNextGroup(fwd.nextId()) == null) {
282 pendingForwards.compute(fwd.nextId(), (id, pending) -> {
Saurav Das1547b3f2017-05-05 17:01:08 -0700283 PendingFlowObjective pendfo = new PendingFlowObjective(deviceId, fwd);
Thomas Vachuskad27097c2016-06-14 19:10:41 -0700284 if (pending == null) {
Saurav Das1547b3f2017-05-05 17:01:08 -0700285 return Sets.newHashSet(pendfo);
Thomas Vachuskad27097c2016-06-14 19:10:41 -0700286 } else {
Saurav Das1547b3f2017-05-05 17:01:08 -0700287 pending.add(pendfo);
Thomas Vachuskad27097c2016-06-14 19:10:41 -0700288 return pending;
289 }
290 });
291 queued = true;
292 }
293 }
294 if (queued) {
295 log.debug("Queued forwarding objective {} for nextId {} meant for device {}",
296 fwd.id(), fwd.nextId(), deviceId);
297 }
298 return queued;
alshabib2a441c62015-04-13 18:39:38 -0700299 }
300
Saurav Das1547b3f2017-05-05 17:01:08 -0700301 private boolean queueNextObjective(DeviceId deviceId, NextObjective next) {
302 if (flowObjectiveStore.getNextGroup(next.id()) != null ||
303 next.op() == Operation.ADD) {
304 // either group exists or we are trying to create it - let it through
305 return false;
306 }
307 // we need to hold off on other operations till we get notified that the
308 // initial group creation has succeeded
309 boolean queued = false;
310 synchronized (pendingNexts) {
311 // double check the flow objective store, because this block could run
312 // after a notification arrives
313 if (flowObjectiveStore.getNextGroup(next.id()) == null) {
314 pendingNexts.compute(next.id(), (id, pending) -> {
315 PendingFlowObjective pendfo = new PendingFlowObjective(deviceId, next);
316 if (pending == null) {
317 return Sets.newHashSet(pendfo);
318 } else {
319 pending.add(pendfo);
320 return pending;
321 }
322 });
323 queued = true;
324 }
325 }
326 if (queued) {
327 log.debug("Queued next objective {} with operation {} meant for device {}",
328 next.id(), next.op(), deviceId);
329 }
330 return queued;
331 }
332
Pier Ventre57a61cd2016-09-07 10:55:41 -0700333 /**
334 * Retrieves (if it exists) the device pipeline behaviour from the cache.
335 * Otherwise it warms the caches and triggers the init method of the Pipeline.
336 *
337 * @param deviceId the id of the device associated to the pipeline
338 * @return the implementation of the Pipeliner behaviour
339 */
Thomas Vachuskaca88bb72015-04-08 19:38:02 -0700340 private Pipeliner getDevicePipeliner(DeviceId deviceId) {
Yuta HIGUCHI1fb0a8c2016-08-12 10:59:24 -0700341 return pipeliners.computeIfAbsent(deviceId, this::initPipelineHandler);
alshabib77b88482015-04-07 15:47:50 -0700342 }
343
Yuta HIGUCHI1fb0a8c2016-08-12 10:59:24 -0700344 /**
Pier Ventre57a61cd2016-09-07 10:55:41 -0700345 * Retrieves (if it exists) the device pipeline behaviour from the cache and
346 * and triggers the init method of the pipeline. Otherwise (DEVICE_ADDED) it warms
347 * the caches and triggers the init method of the Pipeline. The rationale of this
348 * method is for managing the scenario of a switch that goes down for a failure
349 * and goes up after a while.
350 *
351 * @param deviceId the id of the device associated to the pipeline
352 * @return the implementation of the Pipeliner behaviour
353 */
354 private Pipeliner getAndInitDevicePipeliner(DeviceId deviceId) {
355 return pipeliners.compute(deviceId, (deviceIdValue, pipelinerValue) -> {
356 if (pipelinerValue != null) {
357 pipelinerValue.init(deviceId, context);
358 return pipelinerValue;
359 }
360 return this.initPipelineHandler(deviceId);
361 });
362 }
363
364 /**
Yuta HIGUCHI1fb0a8c2016-08-12 10:59:24 -0700365 * Creates and initialize {@link Pipeliner}.
366 * <p>
367 * Note: Expected to be called under per-Device lock.
368 * e.g., {@code pipeliners}' Map#compute family methods
369 *
370 * @param deviceId Device to initialize pipeliner
371 * @return {@link Pipeliner} instance or null
372 */
373 private Pipeliner initPipelineHandler(DeviceId deviceId) {
374 start = now();
Thomas Vachuska866b46a2015-04-30 00:26:55 -0700375
Jonathan Hart17d00452015-04-21 17:10:00 -0700376 // Attempt to lookup the handler in the cache
377 DriverHandler handler = driverHandlers.get(deviceId);
Thomas Vachuskab19bffb2015-07-22 10:56:16 -0700378 cTime = now();
379
Jonathan Hart17d00452015-04-21 17:10:00 -0700380 if (handler == null) {
381 try {
382 // Otherwise create it and if it has pipeline behaviour, cache it
383 handler = driverService.createHandler(deviceId);
Thomas Vachuskab19bffb2015-07-22 10:56:16 -0700384 dTime = now();
Jonathan Hart17d00452015-04-21 17:10:00 -0700385 if (!handler.driver().hasBehaviour(Pipeliner.class)) {
Yuta HIGUCHIa2a4f342017-03-17 11:38:57 -0700386 log.debug("Pipeline behaviour not supported for device {}",
Jonathan Hart17d00452015-04-21 17:10:00 -0700387 deviceId);
Yuta HIGUCHI1fb0a8c2016-08-12 10:59:24 -0700388 return null;
alshabib2a441c62015-04-13 18:39:38 -0700389 }
Jonathan Hart17d00452015-04-21 17:10:00 -0700390 } catch (ItemNotFoundException e) {
391 log.warn("No applicable driver for device {}", deviceId);
Yuta HIGUCHI1fb0a8c2016-08-12 10:59:24 -0700392 return null;
alshabib2a441c62015-04-13 18:39:38 -0700393 }
394
Jonathan Hart17d00452015-04-21 17:10:00 -0700395 driverHandlers.put(deviceId, handler);
Thomas Vachuskab19bffb2015-07-22 10:56:16 -0700396 eTime = now();
alshabib2a441c62015-04-13 18:39:38 -0700397 }
Jonathan Hart17d00452015-04-21 17:10:00 -0700398
399 // Always (re)initialize the pipeline behaviour
400 log.info("Driver {} bound to device {} ... initializing driver",
401 handler.driver().name(), deviceId);
Thomas Vachuska0121a612015-07-21 11:18:09 -0700402 hTime = now();
Jonathan Hart17d00452015-04-21 17:10:00 -0700403 Pipeliner pipeliner = handler.behaviour(Pipeliner.class);
Thomas Vachuska94c3cf42015-07-20 13:01:12 -0700404 hbTime = now();
Jonathan Hart17d00452015-04-21 17:10:00 -0700405 pipeliner.init(deviceId, context);
Yuta HIGUCHI1fb0a8c2016-08-12 10:59:24 -0700406 stopWatch();
407 return pipeliner;
alshabibaebe7752015-04-07 17:45:42 -0700408 }
alshabib77b88482015-04-07 15:47:50 -0700409
Thomas Vachuskaca88bb72015-04-08 19:38:02 -0700410 // Triggers driver setup when a device is (re)detected.
alshabibaebe7752015-04-07 17:45:42 -0700411 private class InnerDeviceListener implements DeviceListener {
412 @Override
413 public void event(DeviceEvent event) {
414 switch (event.type()) {
415 case DEVICE_ADDED:
416 case DEVICE_AVAILABILITY_CHANGED:
Madan Jampani0174f452015-05-29 11:52:05 -0700417 log.debug("Device either added or availability changed {}",
Thomas Vachuska9c9ff7c2015-07-20 10:38:59 -0700418 event.subject().id());
alshabib4313d102015-04-08 18:55:08 -0700419 if (deviceService.isAvailable(event.subject().id())) {
Madan Jampani0174f452015-05-29 11:52:05 -0700420 log.debug("Device is now available {}", event.subject().id());
Pier Ventre57a61cd2016-09-07 10:55:41 -0700421 getAndInitDevicePipeliner(event.subject().id());
Yuta HIGUCHI1fb0a8c2016-08-12 10:59:24 -0700422 } else {
423 log.debug("Device is no longer available {}", event.subject().id());
alshabib4313d102015-04-08 18:55:08 -0700424 }
425 break;
426 case DEVICE_UPDATED:
427 break;
428 case DEVICE_REMOVED:
Yuta HIGUCHIad0c9902016-08-23 10:37:32 -0700429 // evict Pipeliner and Handler cache, when
430 // the Device was administratively removed.
431 //
432 // System expect the user to clear all existing flows,
433 // before removing device, especially if they intend to
434 // replace driver/pipeliner assigned to the device.
435 driverHandlers.remove(event.subject().id());
Yuta HIGUCHI1fb0a8c2016-08-12 10:59:24 -0700436 pipeliners.remove(event.subject().id());
alshabib4313d102015-04-08 18:55:08 -0700437 break;
Yuta HIGUCHIad0c9902016-08-23 10:37:32 -0700438 case DEVICE_SUSPENDED:
439 break;
alshabib4313d102015-04-08 18:55:08 -0700440 case PORT_ADDED:
441 break;
442 case PORT_UPDATED:
443 break;
444 case PORT_REMOVED:
alshabibaebe7752015-04-07 17:45:42 -0700445 break;
446 default:
Thomas Vachuskaca88bb72015-04-08 19:38:02 -0700447 break;
alshabibaebe7752015-04-07 17:45:42 -0700448 }
alshabib77b88482015-04-07 15:47:50 -0700449 }
450 }
alshabibaebe7752015-04-07 17:45:42 -0700451
Thomas Vachuska174bb912015-07-16 21:27:14 -0700452 // Temporary mechanism to monitor pipeliner setup time-cost; there are
453 // intermittent time where this takes in excess of 2 seconds. Why?
Thomas Vachuska9c9ff7c2015-07-20 10:38:59 -0700454 private long start = 0, totals = 0, count = 0;
Thomas Vachuskab19bffb2015-07-22 10:56:16 -0700455 private long cTime, dTime, eTime, hTime, hbTime;
Thomas Vachuska9c9ff7c2015-07-20 10:38:59 -0700456 private static final long LIMIT = 500;
Thomas Vachuska174bb912015-07-16 21:27:14 -0700457
Thomas Vachuska9c9ff7c2015-07-20 10:38:59 -0700458 private long now() {
Thomas Vachuska174bb912015-07-16 21:27:14 -0700459 return System.currentTimeMillis();
460 }
461
Thomas Vachuska9c9ff7c2015-07-20 10:38:59 -0700462 private void stopWatch() {
Thomas Vachuska174bb912015-07-16 21:27:14 -0700463 long duration = System.currentTimeMillis() - start;
464 totals += duration;
465 count += 1;
466 if (duration > LIMIT) {
Thomas Vachuskab19bffb2015-07-22 10:56:16 -0700467 log.info("Pipeline setup took {} ms; avg {} ms; cTime={}, dTime={}, eTime={}, hTime={}, hbTime={}",
468 duration, totals / count, diff(cTime), diff(dTime), diff(eTime), diff(hTime), diff(hbTime));
Thomas Vachuska174bb912015-07-16 21:27:14 -0700469 }
470 }
471
Thomas Vachuska9c9ff7c2015-07-20 10:38:59 -0700472 private long diff(long bTime) {
Thomas Vachuskab19bffb2015-07-22 10:56:16 -0700473 long diff = bTime - start;
474 return diff < 0 ? 0 : diff;
Thomas Vachuska9c9ff7c2015-07-20 10:38:59 -0700475 }
476
Thomas Vachuskaca88bb72015-04-08 19:38:02 -0700477 // Processing context for initializing pipeline driver behaviours.
478 private class InnerPipelineContext implements PipelinerContext {
479 @Override
480 public ServiceDirectory directory() {
481 return serviceDirectory;
alshabibaebe7752015-04-07 17:45:42 -0700482 }
alshabib2a441c62015-04-13 18:39:38 -0700483
484 @Override
485 public FlowObjectiveStore store() {
486 return flowObjectiveStore;
487 }
alshabib2a441c62015-04-13 18:39:38 -0700488 }
489
490 private class InternalStoreDelegate implements FlowObjectiveStoreDelegate {
491 @Override
492 public void notify(ObjectiveEvent event) {
Saurav Das423fe2b2015-12-04 10:52:59 -0800493 if (event.type() == Type.ADD) {
494 log.debug("Received notification of obj event {}", event);
Saurav Das1547b3f2017-05-05 17:01:08 -0700495 Set<PendingFlowObjective> pending;
496
497 // first send all pending flows
Thomas Vachuskad27097c2016-06-14 19:10:41 -0700498 synchronized (pendingForwards) {
499 // needs to be synchronized for queueObjective lookup
500 pending = pendingForwards.remove(event.subject());
501 }
Saurav Das423fe2b2015-12-04 10:52:59 -0800502 if (pending == null) {
Saurav Das1547b3f2017-05-05 17:01:08 -0700503 log.debug("No forwarding objectives pending for this "
504 + "obj event {}", event);
505 } else {
506 log.debug("Processing {} pending forwarding objectives for nextId {}",
507 pending.size(), event.subject());
508 pending.forEach(p -> getDevicePipeliner(p.deviceId())
509 .forward((ForwardingObjective) p.flowObjective()));
Saurav Das423fe2b2015-12-04 10:52:59 -0800510 }
511
Saurav Das1547b3f2017-05-05 17:01:08 -0700512 // now check for pending next-objectives
513 synchronized (pendingNexts) {
514 // needs to be synchronized for queueObjective lookup
515 pending = pendingNexts.remove(event.subject());
516 }
517 if (pending == null) {
518 log.debug("No next objectives pending for this "
519 + "obj event {}", event);
520 } else {
521 log.debug("Processing {} pending next objectives for nextId {}",
522 pending.size(), event.subject());
523 pending.forEach(p -> getDevicePipeliner(p.deviceId())
524 .next((NextObjective) p.flowObjective()));
525 }
alshabib2a441c62015-04-13 18:39:38 -0700526 }
alshabib2a441c62015-04-13 18:39:38 -0700527 }
528 }
529
530 /**
Saurav Das1547b3f2017-05-05 17:01:08 -0700531 * Data class used to hold a pending flow objective that could not
alshabib2a441c62015-04-13 18:39:38 -0700532 * be processed because the associated next object was not present.
Saurav Das1547b3f2017-05-05 17:01:08 -0700533 * Note that this pending flow objective could be a forwarding objective
534 * waiting for a next objective to complete execution. Or it could a
535 * next objective (with a different operation - remove, addToExisting, or
536 * removeFromExisting) waiting for a next objective with the same id to
537 * complete execution.
alshabib2a441c62015-04-13 18:39:38 -0700538 */
Saurav Das1547b3f2017-05-05 17:01:08 -0700539 private class PendingFlowObjective {
alshabib2a441c62015-04-13 18:39:38 -0700540 private final DeviceId deviceId;
Saurav Das1547b3f2017-05-05 17:01:08 -0700541 private final Objective flowObj;
alshabib2a441c62015-04-13 18:39:38 -0700542
Saurav Das1547b3f2017-05-05 17:01:08 -0700543 public PendingFlowObjective(DeviceId deviceId, Objective flowObj) {
alshabib2a441c62015-04-13 18:39:38 -0700544 this.deviceId = deviceId;
Saurav Das1547b3f2017-05-05 17:01:08 -0700545 this.flowObj = flowObj;
alshabib2a441c62015-04-13 18:39:38 -0700546 }
547
548 public DeviceId deviceId() {
549 return deviceId;
550 }
551
Saurav Das1547b3f2017-05-05 17:01:08 -0700552 public Objective flowObjective() {
553 return flowObj;
alshabib2a441c62015-04-13 18:39:38 -0700554 }
Saurav Das8a0732e2015-11-20 15:27:53 -0800555
556 @Override
557 public int hashCode() {
Saurav Das1547b3f2017-05-05 17:01:08 -0700558 return Objects.hash(deviceId, flowObj);
Saurav Das8a0732e2015-11-20 15:27:53 -0800559 }
560
561 @Override
562 public boolean equals(final Object obj) {
563 if (this == obj) {
564 return true;
565 }
Saurav Das1547b3f2017-05-05 17:01:08 -0700566 if (!(obj instanceof PendingFlowObjective)) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800567 return false;
568 }
Saurav Das1547b3f2017-05-05 17:01:08 -0700569 final PendingFlowObjective other = (PendingFlowObjective) obj;
Saurav Das8a0732e2015-11-20 15:27:53 -0800570 if (this.deviceId.equals(other.deviceId) &&
Saurav Das1547b3f2017-05-05 17:01:08 -0700571 this.flowObj.equals(other.flowObj)) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800572 return true;
573 }
574 return false;
575 }
alshabibaebe7752015-04-07 17:45:42 -0700576 }
Saurav Das24431192016-03-07 19:13:00 -0800577
578 @Override
579 public List<String> getNextMappings() {
580 List<String> mappings = new ArrayList<>();
581 Map<Integer, NextGroup> allnexts = flowObjectiveStore.getAllGroups();
Saurav Das25190812016-05-27 13:54:07 -0700582 // XXX if the NextGroup after de-serialization actually stored info of the deviceId
Saurav Das24431192016-03-07 19:13:00 -0800583 // then info on any nextObj could be retrieved from one controller instance.
584 // Right now the drivers on one instance can only fetch for next-ids that came
585 // to them.
586 // Also, we still need to send the right next-id to the right driver as potentially
587 // there can be different drivers for different devices. But on that account,
588 // no instance should be decoding for another instance's nextIds.
589
590 for (Map.Entry<Integer, NextGroup> e : allnexts.entrySet()) {
591 // get the device this next Objective was sent to
592 DeviceId deviceId = nextToDevice.get(e.getKey());
593 mappings.add("NextId " + e.getKey() + ": " +
594 ((deviceId != null) ? deviceId : "nextId not in this onos instance"));
595 if (deviceId != null) {
596 // this instance of the controller sent the nextObj to a driver
597 Pipeliner pipeliner = getDevicePipeliner(deviceId);
598 List<String> nextMappings = pipeliner.getNextMappings(e.getValue());
599 if (nextMappings != null) {
600 mappings.addAll(nextMappings);
601 }
602 }
603 }
604 return mappings;
605 }
Saurav Dasb5c236e2016-06-07 10:08:06 -0700606
607 @Override
Saurav Das1547b3f2017-05-05 17:01:08 -0700608 public List<String> getPendingFlowObjectives() {
609 List<String> pendingFlowObjectives = new ArrayList<>();
Charles Chan54734712017-03-29 11:07:55 -0700610
Saurav Das1547b3f2017-05-05 17:01:08 -0700611 for (Integer nextId : pendingForwards.keySet()) {
612 Set<PendingFlowObjective> pfwd = pendingForwards.get(nextId);
Sho SHIMIZU81470a52016-08-12 17:24:55 -0700613 StringBuilder pend = new StringBuilder();
Charles Chan54734712017-03-29 11:07:55 -0700614 pend.append("NextId: ")
615 .append(nextId);
Saurav Das1547b3f2017-05-05 17:01:08 -0700616 for (PendingFlowObjective pf : pfwd) {
Charles Chan54734712017-03-29 11:07:55 -0700617 pend.append("\n FwdId: ")
Saurav Das1547b3f2017-05-05 17:01:08 -0700618 .append(String.format("%11s", pf.flowObjective().id()))
619 .append(", DeviceId: ")
620 .append(pf.deviceId())
621 .append(", Selector: ")
622 .append(((ForwardingObjective) pf.flowObjective())
623 .selector().criteria());
624 }
625 pendingFlowObjectives.add(pend.toString());
626 }
627
628 for (Integer nextId : pendingNexts.keySet()) {
629 Set<PendingFlowObjective> pnext = pendingNexts.get(nextId);
630 StringBuilder pend = new StringBuilder();
631 pend.append("NextId: ")
632 .append(nextId);
633 for (PendingFlowObjective pn : pnext) {
634 pend.append("\n NextOp: ")
635 .append(pn.flowObjective().op())
Charles Chan54734712017-03-29 11:07:55 -0700636 .append(", DeviceId: ")
637 .append(pn.deviceId())
Saurav Das1547b3f2017-05-05 17:01:08 -0700638 .append(", Treatments: ")
639 .append(((NextObjective) pn.flowObjective())
640 .next());
Saurav Dasb5c236e2016-06-07 10:08:06 -0700641 }
Saurav Das1547b3f2017-05-05 17:01:08 -0700642 pendingFlowObjectives.add(pend.toString());
Saurav Dasb5c236e2016-06-07 10:08:06 -0700643 }
Saurav Das1547b3f2017-05-05 17:01:08 -0700644
645 return pendingFlowObjectives;
646 }
647
648 @Override
649 public List<String> getPendingNexts() {
650 return getPendingFlowObjectives();
Saurav Dasb5c236e2016-06-07 10:08:06 -0700651 }
alshabib77b88482015-04-07 15:47:50 -0700652}