blob: 4bd4febaa4cb98673b0ada3bb3347a11bd7b07f4 [file] [log] [blame]
alshabib77b88482015-04-07 15:47:50 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2015-present Open Networking Foundation
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;
Thomas Vachuskad24ba732017-08-23 15:29:34 -070034import org.onosproject.net.Device;
alshabib77b88482015-04-07 15:47:50 -070035import org.onosproject.net.DeviceId;
Saurav Das24431192016-03-07 19:13:00 -080036import org.onosproject.net.behaviour.NextGroup;
alshabib77b88482015-04-07 15:47:50 -070037import org.onosproject.net.behaviour.Pipeliner;
Thomas Vachuskaca88bb72015-04-08 19:38:02 -070038import org.onosproject.net.behaviour.PipelinerContext;
alshabibaebe7752015-04-07 17:45:42 -070039import org.onosproject.net.device.DeviceEvent;
40import org.onosproject.net.device.DeviceListener;
alshabib77b88482015-04-07 15:47:50 -070041import org.onosproject.net.device.DeviceService;
Thomas Vachuskad24ba732017-08-23 15:29:34 -070042import org.onosproject.net.driver.DriverEvent;
alshabib77b88482015-04-07 15:47:50 -070043import org.onosproject.net.driver.DriverHandler;
Thomas Vachuskad24ba732017-08-23 15:29:34 -070044import org.onosproject.net.driver.DriverListener;
alshabib77b88482015-04-07 15:47:50 -070045import org.onosproject.net.driver.DriverService;
Thomas Vachuskaca88bb72015-04-08 19:38:02 -070046import org.onosproject.net.flow.FlowRuleService;
alshabib77b88482015-04-07 15:47:50 -070047import org.onosproject.net.flowobjective.FilteringObjective;
48import org.onosproject.net.flowobjective.FlowObjectiveService;
alshabib2a441c62015-04-13 18:39:38 -070049import org.onosproject.net.flowobjective.FlowObjectiveStore;
50import org.onosproject.net.flowobjective.FlowObjectiveStoreDelegate;
alshabib77b88482015-04-07 15:47:50 -070051import org.onosproject.net.flowobjective.ForwardingObjective;
52import org.onosproject.net.flowobjective.NextObjective;
alshabib910aff12015-04-09 16:55:57 -070053import org.onosproject.net.flowobjective.Objective;
Saurav Das1547b3f2017-05-05 17:01:08 -070054import org.onosproject.net.flowobjective.Objective.Operation;
Jonathan Hart17d00452015-04-21 17:10:00 -070055import org.onosproject.net.flowobjective.ObjectiveError;
alshabib2a441c62015-04-13 18:39:38 -070056import org.onosproject.net.flowobjective.ObjectiveEvent;
Saurav Das423fe2b2015-12-04 10:52:59 -080057import org.onosproject.net.flowobjective.ObjectiveEvent.Type;
Thomas Vachuskaca88bb72015-04-08 19:38:02 -070058import org.onosproject.net.group.GroupService;
Yi Tseng374c5f32017-03-05 22:51:35 -080059import org.osgi.service.component.ComponentContext;
alshabib77b88482015-04-07 15:47:50 -070060import org.slf4j.Logger;
61import org.slf4j.LoggerFactory;
62
Saurav Das24431192016-03-07 19:13:00 -080063import java.util.ArrayList;
Saurav Das24431192016-03-07 19:13:00 -080064import java.util.List;
alshabib77b88482015-04-07 15:47:50 -070065import java.util.Map;
Saurav Das8a0732e2015-11-20 15:27:53 -080066import java.util.Objects;
alshabib2a441c62015-04-13 18:39:38 -070067import java.util.Set;
Jonathan Hart17d00452015-04-21 17:10:00 -070068import java.util.concurrent.ExecutorService;
alshabib77b88482015-04-07 15:47:50 -070069
Sho SHIMIZUf45d85d2015-07-01 14:39:11 -070070import static com.google.common.base.Preconditions.checkNotNull;
Yi Tseng374c5f32017-03-05 22:51:35 -080071import static com.google.common.base.Strings.isNullOrEmpty;
Thomas Vachuska866b46a2015-04-30 00:26:55 -070072import static java.util.concurrent.Executors.newFixedThreadPool;
Jonathan Hart17d00452015-04-21 17:10:00 -070073import static org.onlab.util.Tools.groupedThreads;
Thomas Vachuskad24ba732017-08-23 15:29:34 -070074import static org.onosproject.net.AnnotationKeys.DRIVER;
Changhoon Yoon541ef712015-05-23 17:18:34 +090075import static org.onosproject.security.AppGuard.checkPermission;
Thomas Vachuskad27097c2016-06-14 19:10:41 -070076import static org.onosproject.security.AppPermission.Type.FLOWRULE_WRITE;
alshabib77b88482015-04-07 15:47:50 -070077
78/**
Thomas Vachuskaca88bb72015-04-08 19:38:02 -070079 * Provides implementation of the flow objective programming service.
alshabib77b88482015-04-07 15:47:50 -070080 */
alshabib2a441c62015-04-13 18:39:38 -070081@Component(immediate = true)
alshabib77b88482015-04-07 15:47:50 -070082@Service
83public class FlowObjectiveManager implements FlowObjectiveService {
84
Saurav Dasbd7f7422015-04-23 16:31:47 -070085 public static final int INSTALL_RETRY_ATTEMPTS = 5;
Jonathan Hart17d00452015-04-21 17:10:00 -070086 public static final long INSTALL_RETRY_INTERVAL = 1000; // ms
alshabib77b88482015-04-07 15:47:50 -070087
Yi Tseng374c5f32017-03-05 22:51:35 -080088 private static final String WORKER_PATTERN = "objective-installer-%d";
89 private static final String GROUP_THREAD_NAME = "onos/objective-installer";
90 private static final String NUM_THREAD = "numThreads";
91
Jonathan Hart17d00452015-04-21 17:10:00 -070092 private final Logger log = LoggerFactory.getLogger(getClass());
Thomas Vachuskaca88bb72015-04-08 19:38:02 -070093
Yi Tseng9753fc12017-03-10 18:42:46 -080094 private static final int DEFAULT_NUM_THREADS = 4;
Yi Tseng374c5f32017-03-05 22:51:35 -080095 @Property(name = NUM_THREAD,
96 intValue = DEFAULT_NUM_THREADS,
97 label = "Number of worker threads")
98 private int numThreads = DEFAULT_NUM_THREADS;
99
alshabib77b88482015-04-07 15:47:50 -0700100 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
101 protected DriverService driverService;
102
103 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
104 protected DeviceService deviceService;
105
106 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
alshabib77b88482015-04-07 15:47:50 -0700107 protected ClusterService clusterService;
108
Thomas Vachuskaca88bb72015-04-08 19:38:02 -0700109 // Note: The following dependencies are added on behalf of the pipeline
110 // driver behaviours to assure these services are available for their
111 // initialization.
112 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
113 protected FlowRuleService flowRuleService;
114
115 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
116 protected GroupService groupService;
117
alshabib2a441c62015-04-13 18:39:38 -0700118 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
119 protected FlowObjectiveStore flowObjectiveStore;
120
Yi Tseng374c5f32017-03-05 22:51:35 -0800121 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
122 protected ComponentConfigService cfgService;
123
alshabib2a441c62015-04-13 18:39:38 -0700124 private final FlowObjectiveStoreDelegate delegate = new InternalStoreDelegate();
Thomas Vachuskaca88bb72015-04-08 19:38:02 -0700125
126 private final Map<DeviceId, DriverHandler> driverHandlers = Maps.newConcurrentMap();
alshabib910aff12015-04-09 16:55:57 -0700127 private final Map<DeviceId, Pipeliner> pipeliners = Maps.newConcurrentMap();
Thomas Vachuskaca88bb72015-04-08 19:38:02 -0700128
129 private final PipelinerContext context = new InnerPipelineContext();
Thomas Vachuskaca88bb72015-04-08 19:38:02 -0700130 private final DeviceListener deviceListener = new InnerDeviceListener();
Thomas Vachuskad24ba732017-08-23 15:29:34 -0700131 private final DriverListener driverListener = new InnerDriverListener();
Thomas Vachuskaca88bb72015-04-08 19:38:02 -0700132
alshabib77b88482015-04-07 15:47:50 -0700133 protected ServiceDirectory serviceDirectory = new DefaultServiceDirectory();
alshabib910aff12015-04-09 16:55:57 -0700134
Saurav Das1547b3f2017-05-05 17:01:08 -0700135 // local stores for queuing fwd and next objectives that are waiting for an
136 // associated next objective execution to complete. The signal for completed
137 // execution comes from a pipeline driver, in this or another controller
138 // instance, via the DistributedFlowObjectiveStore.
139 private final Map<Integer, Set<PendingFlowObjective>> pendingForwards =
140 Maps.newConcurrentMap();
141 private final Map<Integer, Set<PendingFlowObjective>> pendingNexts =
142 Maps.newConcurrentMap();
alshabib2a441c62015-04-13 18:39:38 -0700143
Saurav Das24431192016-03-07 19:13:00 -0800144 // local store to track which nextObjectives were sent to which device
145 // for debugging purposes
146 private Map<Integer, DeviceId> nextToDevice = Maps.newConcurrentMap();
147
Jonathan Hart17d00452015-04-21 17:10:00 -0700148 private ExecutorService executorService;
alshabib2a441c62015-04-13 18:39:38 -0700149
alshabib77b88482015-04-07 15:47:50 -0700150 @Activate
151 protected void activate() {
Yi Tseng374c5f32017-03-05 22:51:35 -0800152 cfgService.registerProperties(getClass());
153 executorService = newFixedThreadPool(numThreads,
154 groupedThreads(GROUP_THREAD_NAME, WORKER_PATTERN, log));
alshabib2a441c62015-04-13 18:39:38 -0700155 flowObjectiveStore.setDelegate(delegate);
alshabibaebe7752015-04-07 17:45:42 -0700156 deviceService.addListener(deviceListener);
Thomas Vachuskad24ba732017-08-23 15:29:34 -0700157 driverService.addListener(driverListener);
alshabib77b88482015-04-07 15:47:50 -0700158 log.info("Started");
159 }
160
161 @Deactivate
162 protected void deactivate() {
Yi Tseng374c5f32017-03-05 22:51:35 -0800163 cfgService.unregisterProperties(getClass(), false);
alshabib2a441c62015-04-13 18:39:38 -0700164 flowObjectiveStore.unsetDelegate(delegate);
alshabibaebe7752015-04-07 17:45:42 -0700165 deviceService.removeListener(deviceListener);
Thomas Vachuskad24ba732017-08-23 15:29:34 -0700166 driverService.removeListener(driverListener);
Thomas Vachuska866b46a2015-04-30 00:26:55 -0700167 executorService.shutdown();
168 pipeliners.clear();
169 driverHandlers.clear();
Saurav Das24431192016-03-07 19:13:00 -0800170 nextToDevice.clear();
alshabib77b88482015-04-07 15:47:50 -0700171 log.info("Stopped");
172 }
173
Yi Tseng374c5f32017-03-05 22:51:35 -0800174 @Modified
175 protected void modified(ComponentContext context) {
176 String propertyValue =
177 Tools.get(context.getProperties(), NUM_THREAD);
178 int newNumThreads = isNullOrEmpty(propertyValue) ? numThreads : Integer.parseInt(propertyValue);
179
180 if (newNumThreads != numThreads && newNumThreads > 0) {
181 numThreads = newNumThreads;
182 ExecutorService oldWorkerExecutor = executorService;
183 executorService = newFixedThreadPool(numThreads,
184 groupedThreads(GROUP_THREAD_NAME, WORKER_PATTERN, log));
185 if (oldWorkerExecutor != null) {
186 oldWorkerExecutor.shutdown();
187 }
188 log.info("Reconfigured number of worker threads to {}", numThreads);
189 }
190 }
191
Jonathan Hart17d00452015-04-21 17:10:00 -0700192 /**
193 * Task that passes the flow objective down to the driver. The task will
194 * make a few attempts to find the appropriate driver, then eventually give
195 * up and report an error if no suitable driver could be found.
196 */
197 private class ObjectiveInstaller implements Runnable {
198 private final DeviceId deviceId;
199 private final Objective objective;
200
Sho SHIMIZUf45d85d2015-07-01 14:39:11 -0700201 private final int numAttempts;
Jonathan Hart17d00452015-04-21 17:10:00 -0700202
203 public ObjectiveInstaller(DeviceId deviceId, Objective objective) {
Sho SHIMIZUf45d85d2015-07-01 14:39:11 -0700204 this(deviceId, objective, 1);
205 }
206
207 public ObjectiveInstaller(DeviceId deviceId, Objective objective, int attemps) {
208 this.deviceId = checkNotNull(deviceId);
209 this.objective = checkNotNull(objective);
210 this.numAttempts = checkNotNull(attemps);
alshabib910aff12015-04-09 16:55:57 -0700211 }
alshabib77b88482015-04-07 15:47:50 -0700212
Jonathan Hart17d00452015-04-21 17:10:00 -0700213 @Override
214 public void run() {
215 try {
Jonathan Hart17d00452015-04-21 17:10:00 -0700216 Pipeliner pipeliner = getDevicePipeliner(deviceId);
217
218 if (pipeliner != null) {
219 if (objective instanceof NextObjective) {
Saurav Das1547b3f2017-05-05 17:01:08 -0700220 nextToDevice.put(objective.id(), deviceId);
Jonathan Hart17d00452015-04-21 17:10:00 -0700221 pipeliner.next((NextObjective) objective);
222 } else if (objective instanceof ForwardingObjective) {
223 pipeliner.forward((ForwardingObjective) objective);
224 } else {
225 pipeliner.filter((FilteringObjective) objective);
226 }
Andrea Campanella1f8188d2016-02-29 13:24:54 -0800227 //Attempts to check if pipeliner is null for retry attempts
Jonathan Hart17d00452015-04-21 17:10:00 -0700228 } else if (numAttempts < INSTALL_RETRY_ATTEMPTS) {
Saurav Das3d038262015-04-23 12:36:58 -0700229 Thread.sleep(INSTALL_RETRY_INTERVAL);
HIGUCHI Yutad9e01052016-04-14 09:31:42 -0700230 executorService.execute(new ObjectiveInstaller(deviceId, objective, numAttempts + 1));
Jonathan Hart17d00452015-04-21 17:10:00 -0700231 } else {
232 // Otherwise we've tried a few times and failed, report an
233 // error back to the user.
234 objective.context().ifPresent(
Andrea Campanella1f8188d2016-02-29 13:24:54 -0800235 c -> c.onError(objective, ObjectiveError.NOPIPELINER));
Jonathan Hart17d00452015-04-21 17:10:00 -0700236 }
Saurav Das1547b3f2017-05-05 17:01:08 -0700237 //Exception thrown
Jonathan Hart17d00452015-04-21 17:10:00 -0700238 } catch (Exception e) {
239 log.warn("Exception while installing flow objective", e);
240 }
241 }
242 }
243
244 @Override
245 public void filter(DeviceId deviceId, FilteringObjective filteringObjective) {
Changhoon Yoonb856b812015-08-10 03:47:19 +0900246 checkPermission(FLOWRULE_WRITE);
HIGUCHI Yutad9e01052016-04-14 09:31:42 -0700247 executorService.execute(new ObjectiveInstaller(deviceId, filteringObjective));
alshabib77b88482015-04-07 15:47:50 -0700248 }
249
250 @Override
Thomas Vachuska866b46a2015-04-30 00:26:55 -0700251 public void forward(DeviceId deviceId, ForwardingObjective forwardingObjective) {
Changhoon Yoonb856b812015-08-10 03:47:19 +0900252 checkPermission(FLOWRULE_WRITE);
Yi Tseng1666b502017-05-17 11:05:18 -0700253 if (forwardingObjective.nextId() == null ||
254 forwardingObjective.op() == Objective.Operation.REMOVE ||
255 flowObjectiveStore.getNextGroup(forwardingObjective.nextId()) != null ||
256 !queueFwdObjective(deviceId, forwardingObjective)) {
257 // fast path
258 executorService.execute(new ObjectiveInstaller(deviceId, forwardingObjective));
alshabib910aff12015-04-09 16:55:57 -0700259 }
Thomas Vachuskaca88bb72015-04-08 19:38:02 -0700260 }
261
alshabib2a441c62015-04-13 18:39:38 -0700262 @Override
Jonathan Hart17d00452015-04-21 17:10:00 -0700263 public void next(DeviceId deviceId, NextObjective nextObjective) {
Changhoon Yoonb856b812015-08-10 03:47:19 +0900264 checkPermission(FLOWRULE_WRITE);
Yi Tseng1666b502017-05-17 11:05:18 -0700265 if (nextObjective.op() == Operation.ADD ||
266 flowObjectiveStore.getNextGroup(nextObjective.id()) != null ||
267 !queueNextObjective(deviceId, nextObjective)) {
268 // either group exists or we are trying to create it - let it through
269 executorService.execute(new ObjectiveInstaller(deviceId, nextObjective));
Saurav Das1547b3f2017-05-05 17:01:08 -0700270 }
alshabib2a441c62015-04-13 18:39:38 -0700271 }
272
alshabibf6ea9e62015-04-21 17:08:26 -0700273 @Override
274 public int allocateNextId() {
Changhoon Yoonb856b812015-08-10 03:47:19 +0900275 checkPermission(FLOWRULE_WRITE);
alshabibf6ea9e62015-04-21 17:08:26 -0700276 return flowObjectiveStore.allocateNextId();
277 }
278
Xin Jin313708b2015-07-09 13:43:04 -0700279 @Override
Thomas Vachuskad24ba732017-08-23 15:29:34 -0700280 public void initPolicy(String policy) {
281 }
Xin Jin313708b2015-07-09 13:43:04 -0700282
Saurav Das1547b3f2017-05-05 17:01:08 -0700283 private boolean queueFwdObjective(DeviceId deviceId, ForwardingObjective fwd) {
Thomas Vachuskad27097c2016-06-14 19:10:41 -0700284 boolean queued = false;
285 synchronized (pendingForwards) {
286 // double check the flow objective store, because this block could run
287 // after a notification arrives
288 if (flowObjectiveStore.getNextGroup(fwd.nextId()) == null) {
289 pendingForwards.compute(fwd.nextId(), (id, pending) -> {
Saurav Das1547b3f2017-05-05 17:01:08 -0700290 PendingFlowObjective pendfo = new PendingFlowObjective(deviceId, fwd);
Thomas Vachuskad27097c2016-06-14 19:10:41 -0700291 if (pending == null) {
Saurav Das1547b3f2017-05-05 17:01:08 -0700292 return Sets.newHashSet(pendfo);
Thomas Vachuskad27097c2016-06-14 19:10:41 -0700293 } else {
Saurav Das1547b3f2017-05-05 17:01:08 -0700294 pending.add(pendfo);
Thomas Vachuskad27097c2016-06-14 19:10:41 -0700295 return pending;
296 }
297 });
298 queued = true;
299 }
300 }
301 if (queued) {
Sivachidambaram Subramaniana0efdcc2017-06-22 05:26:46 +0530302 log.info("Queued forwarding objective {} for nextId {} meant for device {}",
Thomas Vachuskad27097c2016-06-14 19:10:41 -0700303 fwd.id(), fwd.nextId(), deviceId);
304 }
305 return queued;
alshabib2a441c62015-04-13 18:39:38 -0700306 }
307
Saurav Das1547b3f2017-05-05 17:01:08 -0700308 private boolean queueNextObjective(DeviceId deviceId, NextObjective next) {
Yi Tseng1666b502017-05-17 11:05:18 -0700309
Saurav Das1547b3f2017-05-05 17:01:08 -0700310 // we need to hold off on other operations till we get notified that the
311 // initial group creation has succeeded
312 boolean queued = false;
313 synchronized (pendingNexts) {
314 // double check the flow objective store, because this block could run
315 // after a notification arrives
316 if (flowObjectiveStore.getNextGroup(next.id()) == null) {
317 pendingNexts.compute(next.id(), (id, pending) -> {
318 PendingFlowObjective pendfo = new PendingFlowObjective(deviceId, next);
319 if (pending == null) {
320 return Sets.newHashSet(pendfo);
321 } else {
322 pending.add(pendfo);
323 return pending;
324 }
325 });
326 queued = true;
327 }
328 }
329 if (queued) {
Sivachidambaram Subramaniana0efdcc2017-06-22 05:26:46 +0530330 log.info("Queued next objective {} with operation {} meant for device {}",
Saurav Das1547b3f2017-05-05 17:01:08 -0700331 next.id(), next.op(), deviceId);
332 }
333 return queued;
334 }
335
Pier Ventre57a61cd2016-09-07 10:55:41 -0700336 /**
337 * Retrieves (if it exists) the device pipeline behaviour from the cache.
338 * Otherwise it warms the caches and triggers the init method of the Pipeline.
339 *
340 * @param deviceId the id of the device associated to the pipeline
341 * @return the implementation of the Pipeliner behaviour
342 */
Thomas Vachuskaca88bb72015-04-08 19:38:02 -0700343 private Pipeliner getDevicePipeliner(DeviceId deviceId) {
Yuta HIGUCHI1fb0a8c2016-08-12 10:59:24 -0700344 return pipeliners.computeIfAbsent(deviceId, this::initPipelineHandler);
alshabib77b88482015-04-07 15:47:50 -0700345 }
346
Yuta HIGUCHI1fb0a8c2016-08-12 10:59:24 -0700347 /**
Pier Ventre57a61cd2016-09-07 10:55:41 -0700348 * Retrieves (if it exists) the device pipeline behaviour from the cache and
349 * and triggers the init method of the pipeline. Otherwise (DEVICE_ADDED) it warms
350 * the caches and triggers the init method of the Pipeline. The rationale of this
351 * method is for managing the scenario of a switch that goes down for a failure
352 * and goes up after a while.
353 *
354 * @param deviceId the id of the device associated to the pipeline
355 * @return the implementation of the Pipeliner behaviour
356 */
357 private Pipeliner getAndInitDevicePipeliner(DeviceId deviceId) {
358 return pipeliners.compute(deviceId, (deviceIdValue, pipelinerValue) -> {
359 if (pipelinerValue != null) {
360 pipelinerValue.init(deviceId, context);
361 return pipelinerValue;
362 }
363 return this.initPipelineHandler(deviceId);
364 });
365 }
366
367 /**
Yuta HIGUCHI1fb0a8c2016-08-12 10:59:24 -0700368 * Creates and initialize {@link Pipeliner}.
369 * <p>
370 * Note: Expected to be called under per-Device lock.
371 * e.g., {@code pipeliners}' Map#compute family methods
372 *
373 * @param deviceId Device to initialize pipeliner
374 * @return {@link Pipeliner} instance or null
375 */
376 private Pipeliner initPipelineHandler(DeviceId deviceId) {
377 start = now();
Thomas Vachuska866b46a2015-04-30 00:26:55 -0700378
Jonathan Hart17d00452015-04-21 17:10:00 -0700379 // Attempt to lookup the handler in the cache
380 DriverHandler handler = driverHandlers.get(deviceId);
Thomas Vachuskab19bffb2015-07-22 10:56:16 -0700381 cTime = now();
382
Jonathan Hart17d00452015-04-21 17:10:00 -0700383 if (handler == null) {
384 try {
385 // Otherwise create it and if it has pipeline behaviour, cache it
386 handler = driverService.createHandler(deviceId);
Thomas Vachuskab19bffb2015-07-22 10:56:16 -0700387 dTime = now();
Jonathan Hart17d00452015-04-21 17:10:00 -0700388 if (!handler.driver().hasBehaviour(Pipeliner.class)) {
Yuta HIGUCHIa2a4f342017-03-17 11:38:57 -0700389 log.debug("Pipeline behaviour not supported for device {}",
Jonathan Hart17d00452015-04-21 17:10:00 -0700390 deviceId);
Yuta HIGUCHI1fb0a8c2016-08-12 10:59:24 -0700391 return null;
alshabib2a441c62015-04-13 18:39:38 -0700392 }
Jonathan Hart17d00452015-04-21 17:10:00 -0700393 } catch (ItemNotFoundException e) {
394 log.warn("No applicable driver for device {}", deviceId);
Yuta HIGUCHI1fb0a8c2016-08-12 10:59:24 -0700395 return null;
alshabib2a441c62015-04-13 18:39:38 -0700396 }
397
Jonathan Hart17d00452015-04-21 17:10:00 -0700398 driverHandlers.put(deviceId, handler);
Thomas Vachuskab19bffb2015-07-22 10:56:16 -0700399 eTime = now();
alshabib2a441c62015-04-13 18:39:38 -0700400 }
Jonathan Hart17d00452015-04-21 17:10:00 -0700401
402 // Always (re)initialize the pipeline behaviour
403 log.info("Driver {} bound to device {} ... initializing driver",
404 handler.driver().name(), deviceId);
Thomas Vachuska0121a612015-07-21 11:18:09 -0700405 hTime = now();
Jonathan Hart17d00452015-04-21 17:10:00 -0700406 Pipeliner pipeliner = handler.behaviour(Pipeliner.class);
Thomas Vachuska94c3cf42015-07-20 13:01:12 -0700407 hbTime = now();
Jonathan Hart17d00452015-04-21 17:10:00 -0700408 pipeliner.init(deviceId, context);
Yuta HIGUCHI1fb0a8c2016-08-12 10:59:24 -0700409 stopWatch();
410 return pipeliner;
alshabibaebe7752015-04-07 17:45:42 -0700411 }
alshabib77b88482015-04-07 15:47:50 -0700412
Thomas Vachuskad24ba732017-08-23 15:29:34 -0700413 private void invalidatePipelinerIfNecessary(Device device) {
414 DriverHandler handler = driverHandlers.get(device.id());
415 if (handler != null &&
416 !Objects.equals(handler.driver().name(),
417 device.annotations().value(DRIVER))) {
418 invalidatePipeliner(device.id());
419 }
420 }
421
422 private void invalidatePipeliner(DeviceId id) {
423 log.info("Invalidating cached pipeline behaviour for {}", id);
424 driverHandlers.remove(id);
425 pipeliners.remove(id);
426 if (deviceService.isAvailable(id)) {
427 getAndInitDevicePipeliner(id);
428 }
429 }
430
Thomas Vachuskaca88bb72015-04-08 19:38:02 -0700431 // Triggers driver setup when a device is (re)detected.
alshabibaebe7752015-04-07 17:45:42 -0700432 private class InnerDeviceListener implements DeviceListener {
433 @Override
434 public void event(DeviceEvent event) {
435 switch (event.type()) {
436 case DEVICE_ADDED:
437 case DEVICE_AVAILABILITY_CHANGED:
Madan Jampani0174f452015-05-29 11:52:05 -0700438 log.debug("Device either added or availability changed {}",
Thomas Vachuska9c9ff7c2015-07-20 10:38:59 -0700439 event.subject().id());
alshabib4313d102015-04-08 18:55:08 -0700440 if (deviceService.isAvailable(event.subject().id())) {
Madan Jampani0174f452015-05-29 11:52:05 -0700441 log.debug("Device is now available {}", event.subject().id());
Pier Ventre57a61cd2016-09-07 10:55:41 -0700442 getAndInitDevicePipeliner(event.subject().id());
Yuta HIGUCHI1fb0a8c2016-08-12 10:59:24 -0700443 } else {
444 log.debug("Device is no longer available {}", event.subject().id());
alshabib4313d102015-04-08 18:55:08 -0700445 }
446 break;
447 case DEVICE_UPDATED:
Thomas Vachuskad24ba732017-08-23 15:29:34 -0700448 // Invalidate pipeliner and handler caches if the driver name
449 // device annotation changed.
450 invalidatePipelinerIfNecessary(event.subject());
alshabib4313d102015-04-08 18:55:08 -0700451 break;
452 case DEVICE_REMOVED:
Yuta HIGUCHIad0c9902016-08-23 10:37:32 -0700453 // evict Pipeliner and Handler cache, when
454 // the Device was administratively removed.
455 //
456 // System expect the user to clear all existing flows,
457 // before removing device, especially if they intend to
458 // replace driver/pipeliner assigned to the device.
459 driverHandlers.remove(event.subject().id());
Yuta HIGUCHI1fb0a8c2016-08-12 10:59:24 -0700460 pipeliners.remove(event.subject().id());
alshabib4313d102015-04-08 18:55:08 -0700461 break;
Yuta HIGUCHIad0c9902016-08-23 10:37:32 -0700462 case DEVICE_SUSPENDED:
463 break;
alshabib4313d102015-04-08 18:55:08 -0700464 case PORT_ADDED:
465 break;
466 case PORT_UPDATED:
467 break;
468 case PORT_REMOVED:
alshabibaebe7752015-04-07 17:45:42 -0700469 break;
470 default:
Thomas Vachuskaca88bb72015-04-08 19:38:02 -0700471 break;
alshabibaebe7752015-04-07 17:45:42 -0700472 }
alshabib77b88482015-04-07 15:47:50 -0700473 }
474 }
alshabibaebe7752015-04-07 17:45:42 -0700475
Thomas Vachuskad24ba732017-08-23 15:29:34 -0700476 // Monitors driver configuration changes and invalidates the pipeliner cache entries.
477 // Note that this may leave stale entries on the device if the driver changes
478 // in manner where the new driver does not produce backward compatible flow objectives.
479 // In such cases, it is the operator's responsibility to force device re-connect.
480 private class InnerDriverListener implements DriverListener {
481 @Override
482 public void event(DriverEvent event) {
483 String driverName = event.subject().name();
484 driverHandlers.entrySet().stream()
485 .filter(e -> driverName.equals(e.getValue().driver().name()))
486 .map(Map.Entry::getKey)
487 .distinct()
488 .forEach(FlowObjectiveManager.this::invalidatePipeliner);
489 }
490 }
491
Thomas Vachuska174bb912015-07-16 21:27:14 -0700492 // Temporary mechanism to monitor pipeliner setup time-cost; there are
493 // intermittent time where this takes in excess of 2 seconds. Why?
Thomas Vachuska9c9ff7c2015-07-20 10:38:59 -0700494 private long start = 0, totals = 0, count = 0;
Thomas Vachuskab19bffb2015-07-22 10:56:16 -0700495 private long cTime, dTime, eTime, hTime, hbTime;
Thomas Vachuska9c9ff7c2015-07-20 10:38:59 -0700496 private static final long LIMIT = 500;
Thomas Vachuska174bb912015-07-16 21:27:14 -0700497
Thomas Vachuska9c9ff7c2015-07-20 10:38:59 -0700498 private long now() {
Thomas Vachuska174bb912015-07-16 21:27:14 -0700499 return System.currentTimeMillis();
500 }
501
Thomas Vachuska9c9ff7c2015-07-20 10:38:59 -0700502 private void stopWatch() {
Thomas Vachuska174bb912015-07-16 21:27:14 -0700503 long duration = System.currentTimeMillis() - start;
504 totals += duration;
505 count += 1;
506 if (duration > LIMIT) {
Thomas Vachuskab19bffb2015-07-22 10:56:16 -0700507 log.info("Pipeline setup took {} ms; avg {} ms; cTime={}, dTime={}, eTime={}, hTime={}, hbTime={}",
508 duration, totals / count, diff(cTime), diff(dTime), diff(eTime), diff(hTime), diff(hbTime));
Thomas Vachuska174bb912015-07-16 21:27:14 -0700509 }
510 }
511
Thomas Vachuska9c9ff7c2015-07-20 10:38:59 -0700512 private long diff(long bTime) {
Thomas Vachuskab19bffb2015-07-22 10:56:16 -0700513 long diff = bTime - start;
514 return diff < 0 ? 0 : diff;
Thomas Vachuska9c9ff7c2015-07-20 10:38:59 -0700515 }
516
Thomas Vachuskaca88bb72015-04-08 19:38:02 -0700517 // Processing context for initializing pipeline driver behaviours.
518 private class InnerPipelineContext implements PipelinerContext {
519 @Override
520 public ServiceDirectory directory() {
521 return serviceDirectory;
alshabibaebe7752015-04-07 17:45:42 -0700522 }
alshabib2a441c62015-04-13 18:39:38 -0700523
524 @Override
525 public FlowObjectiveStore store() {
526 return flowObjectiveStore;
527 }
alshabib2a441c62015-04-13 18:39:38 -0700528 }
529
530 private class InternalStoreDelegate implements FlowObjectiveStoreDelegate {
531 @Override
532 public void notify(ObjectiveEvent event) {
Saurav Das423fe2b2015-12-04 10:52:59 -0800533 if (event.type() == Type.ADD) {
534 log.debug("Received notification of obj event {}", event);
Saurav Das1547b3f2017-05-05 17:01:08 -0700535 Set<PendingFlowObjective> pending;
536
537 // first send all pending flows
Thomas Vachuskad27097c2016-06-14 19:10:41 -0700538 synchronized (pendingForwards) {
539 // needs to be synchronized for queueObjective lookup
540 pending = pendingForwards.remove(event.subject());
541 }
Saurav Das423fe2b2015-12-04 10:52:59 -0800542 if (pending == null) {
Saurav Das1547b3f2017-05-05 17:01:08 -0700543 log.debug("No forwarding objectives pending for this "
544 + "obj event {}", event);
545 } else {
546 log.debug("Processing {} pending forwarding objectives for nextId {}",
547 pending.size(), event.subject());
548 pending.forEach(p -> getDevicePipeliner(p.deviceId())
549 .forward((ForwardingObjective) p.flowObjective()));
Saurav Das423fe2b2015-12-04 10:52:59 -0800550 }
551
Saurav Das1547b3f2017-05-05 17:01:08 -0700552 // now check for pending next-objectives
553 synchronized (pendingNexts) {
554 // needs to be synchronized for queueObjective lookup
555 pending = pendingNexts.remove(event.subject());
556 }
557 if (pending == null) {
558 log.debug("No next objectives pending for this "
559 + "obj event {}", event);
560 } else {
561 log.debug("Processing {} pending next objectives for nextId {}",
562 pending.size(), event.subject());
563 pending.forEach(p -> getDevicePipeliner(p.deviceId())
564 .next((NextObjective) p.flowObjective()));
565 }
alshabib2a441c62015-04-13 18:39:38 -0700566 }
alshabib2a441c62015-04-13 18:39:38 -0700567 }
568 }
569
570 /**
Saurav Das1547b3f2017-05-05 17:01:08 -0700571 * Data class used to hold a pending flow objective that could not
alshabib2a441c62015-04-13 18:39:38 -0700572 * be processed because the associated next object was not present.
Saurav Das1547b3f2017-05-05 17:01:08 -0700573 * Note that this pending flow objective could be a forwarding objective
574 * waiting for a next objective to complete execution. Or it could a
575 * next objective (with a different operation - remove, addToExisting, or
576 * removeFromExisting) waiting for a next objective with the same id to
577 * complete execution.
alshabib2a441c62015-04-13 18:39:38 -0700578 */
Saurav Das1547b3f2017-05-05 17:01:08 -0700579 private class PendingFlowObjective {
alshabib2a441c62015-04-13 18:39:38 -0700580 private final DeviceId deviceId;
Saurav Das1547b3f2017-05-05 17:01:08 -0700581 private final Objective flowObj;
alshabib2a441c62015-04-13 18:39:38 -0700582
Saurav Das1547b3f2017-05-05 17:01:08 -0700583 public PendingFlowObjective(DeviceId deviceId, Objective flowObj) {
alshabib2a441c62015-04-13 18:39:38 -0700584 this.deviceId = deviceId;
Saurav Das1547b3f2017-05-05 17:01:08 -0700585 this.flowObj = flowObj;
alshabib2a441c62015-04-13 18:39:38 -0700586 }
587
588 public DeviceId deviceId() {
589 return deviceId;
590 }
591
Saurav Das1547b3f2017-05-05 17:01:08 -0700592 public Objective flowObjective() {
593 return flowObj;
alshabib2a441c62015-04-13 18:39:38 -0700594 }
Saurav Das8a0732e2015-11-20 15:27:53 -0800595
596 @Override
597 public int hashCode() {
Saurav Das1547b3f2017-05-05 17:01:08 -0700598 return Objects.hash(deviceId, flowObj);
Saurav Das8a0732e2015-11-20 15:27:53 -0800599 }
600
601 @Override
602 public boolean equals(final Object obj) {
603 if (this == obj) {
604 return true;
605 }
Saurav Das1547b3f2017-05-05 17:01:08 -0700606 if (!(obj instanceof PendingFlowObjective)) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800607 return false;
608 }
Saurav Das1547b3f2017-05-05 17:01:08 -0700609 final PendingFlowObjective other = (PendingFlowObjective) obj;
Saurav Das8a0732e2015-11-20 15:27:53 -0800610 if (this.deviceId.equals(other.deviceId) &&
Saurav Das1547b3f2017-05-05 17:01:08 -0700611 this.flowObj.equals(other.flowObj)) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800612 return true;
613 }
614 return false;
615 }
alshabibaebe7752015-04-07 17:45:42 -0700616 }
Saurav Das24431192016-03-07 19:13:00 -0800617
618 @Override
619 public List<String> getNextMappings() {
620 List<String> mappings = new ArrayList<>();
621 Map<Integer, NextGroup> allnexts = flowObjectiveStore.getAllGroups();
Saurav Das25190812016-05-27 13:54:07 -0700622 // XXX if the NextGroup after de-serialization actually stored info of the deviceId
Saurav Das24431192016-03-07 19:13:00 -0800623 // then info on any nextObj could be retrieved from one controller instance.
624 // Right now the drivers on one instance can only fetch for next-ids that came
625 // to them.
626 // Also, we still need to send the right next-id to the right driver as potentially
627 // there can be different drivers for different devices. But on that account,
628 // no instance should be decoding for another instance's nextIds.
629
630 for (Map.Entry<Integer, NextGroup> e : allnexts.entrySet()) {
631 // get the device this next Objective was sent to
632 DeviceId deviceId = nextToDevice.get(e.getKey());
633 mappings.add("NextId " + e.getKey() + ": " +
634 ((deviceId != null) ? deviceId : "nextId not in this onos instance"));
635 if (deviceId != null) {
636 // this instance of the controller sent the nextObj to a driver
637 Pipeliner pipeliner = getDevicePipeliner(deviceId);
638 List<String> nextMappings = pipeliner.getNextMappings(e.getValue());
639 if (nextMappings != null) {
640 mappings.addAll(nextMappings);
641 }
642 }
643 }
644 return mappings;
645 }
Saurav Dasb5c236e2016-06-07 10:08:06 -0700646
647 @Override
Saurav Das1547b3f2017-05-05 17:01:08 -0700648 public List<String> getPendingFlowObjectives() {
649 List<String> pendingFlowObjectives = new ArrayList<>();
Charles Chan54734712017-03-29 11:07:55 -0700650
Saurav Das1547b3f2017-05-05 17:01:08 -0700651 for (Integer nextId : pendingForwards.keySet()) {
652 Set<PendingFlowObjective> pfwd = pendingForwards.get(nextId);
Sho SHIMIZU81470a52016-08-12 17:24:55 -0700653 StringBuilder pend = new StringBuilder();
Charles Chan54734712017-03-29 11:07:55 -0700654 pend.append("NextId: ")
655 .append(nextId);
Saurav Das1547b3f2017-05-05 17:01:08 -0700656 for (PendingFlowObjective pf : pfwd) {
Charles Chan54734712017-03-29 11:07:55 -0700657 pend.append("\n FwdId: ")
Saurav Das1547b3f2017-05-05 17:01:08 -0700658 .append(String.format("%11s", pf.flowObjective().id()))
659 .append(", DeviceId: ")
660 .append(pf.deviceId())
661 .append(", Selector: ")
662 .append(((ForwardingObjective) pf.flowObjective())
663 .selector().criteria());
664 }
665 pendingFlowObjectives.add(pend.toString());
666 }
667
668 for (Integer nextId : pendingNexts.keySet()) {
669 Set<PendingFlowObjective> pnext = pendingNexts.get(nextId);
670 StringBuilder pend = new StringBuilder();
671 pend.append("NextId: ")
672 .append(nextId);
673 for (PendingFlowObjective pn : pnext) {
674 pend.append("\n NextOp: ")
675 .append(pn.flowObjective().op())
Charles Chan54734712017-03-29 11:07:55 -0700676 .append(", DeviceId: ")
677 .append(pn.deviceId())
Saurav Das1547b3f2017-05-05 17:01:08 -0700678 .append(", Treatments: ")
679 .append(((NextObjective) pn.flowObjective())
680 .next());
Saurav Dasb5c236e2016-06-07 10:08:06 -0700681 }
Saurav Das1547b3f2017-05-05 17:01:08 -0700682 pendingFlowObjectives.add(pend.toString());
Saurav Dasb5c236e2016-06-07 10:08:06 -0700683 }
Saurav Das1547b3f2017-05-05 17:01:08 -0700684
685 return pendingFlowObjectives;
686 }
687
688 @Override
689 public List<String> getPendingNexts() {
690 return getPendingFlowObjectives();
Saurav Dasb5c236e2016-06-07 10:08:06 -0700691 }
alshabib77b88482015-04-07 15:47:50 -0700692}