blob: e458f71233df29a085a74a1c02cb5c8219cee07d [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
Pier Luigi97893112018-03-05 11:09:42 +010018import com.google.common.collect.ImmutableSet;
Saurav Dasf14d9ef2017-12-05 15:00:23 -080019import com.google.common.collect.Lists;
alshabib77b88482015-04-07 15:47:50 -070020import com.google.common.collect.Maps;
Thomas Vachuskad27097c2016-06-14 19:10:41 -070021import com.google.common.collect.Sets;
alshabib77b88482015-04-07 15:47:50 -070022import org.apache.felix.scr.annotations.Activate;
23import org.apache.felix.scr.annotations.Component;
24import org.apache.felix.scr.annotations.Deactivate;
Yi Tseng374c5f32017-03-05 22:51:35 -080025import org.apache.felix.scr.annotations.Modified;
26import org.apache.felix.scr.annotations.Property;
alshabib77b88482015-04-07 15:47:50 -070027import org.apache.felix.scr.annotations.Reference;
28import org.apache.felix.scr.annotations.ReferenceCardinality;
29import org.apache.felix.scr.annotations.Service;
30import org.onlab.osgi.DefaultServiceDirectory;
31import org.onlab.osgi.ServiceDirectory;
Thomas Vachuskaca88bb72015-04-08 19:38:02 -070032import org.onlab.util.ItemNotFoundException;
Yi Tseng374c5f32017-03-05 22:51:35 -080033import org.onlab.util.Tools;
34import org.onosproject.cfg.ComponentConfigService;
alshabib77b88482015-04-07 15:47:50 -070035import org.onosproject.cluster.ClusterService;
Thomas Vachuskacfeff192017-08-23 15:29:34 -070036import org.onosproject.net.Device;
alshabib77b88482015-04-07 15:47:50 -070037import org.onosproject.net.DeviceId;
Saurav Das24431192016-03-07 19:13:00 -080038import org.onosproject.net.behaviour.NextGroup;
alshabib77b88482015-04-07 15:47:50 -070039import org.onosproject.net.behaviour.Pipeliner;
Thomas Vachuskaca88bb72015-04-08 19:38:02 -070040import org.onosproject.net.behaviour.PipelinerContext;
alshabibaebe7752015-04-07 17:45:42 -070041import org.onosproject.net.device.DeviceEvent;
42import org.onosproject.net.device.DeviceListener;
alshabib77b88482015-04-07 15:47:50 -070043import org.onosproject.net.device.DeviceService;
Thomas Vachuskacfeff192017-08-23 15:29:34 -070044import org.onosproject.net.driver.DriverEvent;
alshabib77b88482015-04-07 15:47:50 -070045import org.onosproject.net.driver.DriverHandler;
Thomas Vachuskacfeff192017-08-23 15:29:34 -070046import org.onosproject.net.driver.DriverListener;
alshabib77b88482015-04-07 15:47:50 -070047import org.onosproject.net.driver.DriverService;
Thomas Vachuskaca88bb72015-04-08 19:38:02 -070048import org.onosproject.net.flow.FlowRuleService;
alshabib77b88482015-04-07 15:47:50 -070049import org.onosproject.net.flowobjective.FilteringObjective;
50import org.onosproject.net.flowobjective.FlowObjectiveService;
alshabib2a441c62015-04-13 18:39:38 -070051import org.onosproject.net.flowobjective.FlowObjectiveStore;
52import org.onosproject.net.flowobjective.FlowObjectiveStoreDelegate;
alshabib77b88482015-04-07 15:47:50 -070053import org.onosproject.net.flowobjective.ForwardingObjective;
54import org.onosproject.net.flowobjective.NextObjective;
alshabib910aff12015-04-09 16:55:57 -070055import org.onosproject.net.flowobjective.Objective;
Saurav Das1547b3f2017-05-05 17:01:08 -070056import org.onosproject.net.flowobjective.Objective.Operation;
Jonathan Hart17d00452015-04-21 17:10:00 -070057import org.onosproject.net.flowobjective.ObjectiveError;
alshabib2a441c62015-04-13 18:39:38 -070058import org.onosproject.net.flowobjective.ObjectiveEvent;
Saurav Das423fe2b2015-12-04 10:52:59 -080059import org.onosproject.net.flowobjective.ObjectiveEvent.Type;
Thomas Vachuskaca88bb72015-04-08 19:38:02 -070060import org.onosproject.net.group.GroupService;
Yi Tseng374c5f32017-03-05 22:51:35 -080061import org.osgi.service.component.ComponentContext;
alshabib77b88482015-04-07 15:47:50 -070062import org.slf4j.Logger;
63import org.slf4j.LoggerFactory;
64
Saurav Das24431192016-03-07 19:13:00 -080065import java.util.ArrayList;
Saurav Das24431192016-03-07 19:13:00 -080066import java.util.List;
alshabib77b88482015-04-07 15:47:50 -070067import java.util.Map;
Saurav Das8a0732e2015-11-20 15:27:53 -080068import java.util.Objects;
alshabib2a441c62015-04-13 18:39:38 -070069import java.util.Set;
Jonathan Hart17d00452015-04-21 17:10:00 -070070import java.util.concurrent.ExecutorService;
alshabib77b88482015-04-07 15:47:50 -070071
Sho SHIMIZUf45d85d2015-07-01 14:39:11 -070072import static com.google.common.base.Preconditions.checkNotNull;
Yi Tseng374c5f32017-03-05 22:51:35 -080073import static com.google.common.base.Strings.isNullOrEmpty;
Thomas Vachuska866b46a2015-04-30 00:26:55 -070074import static java.util.concurrent.Executors.newFixedThreadPool;
Ruchi Sahota66b01292019-03-01 16:56:07 +000075import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
Jonathan Hart17d00452015-04-21 17:10:00 -070076import static org.onlab.util.Tools.groupedThreads;
Thomas Vachuskacfeff192017-08-23 15:29:34 -070077import static org.onosproject.net.AnnotationKeys.DRIVER;
Changhoon Yoon541ef712015-05-23 17:18:34 +090078import static org.onosproject.security.AppGuard.checkPermission;
Thomas Vachuskad27097c2016-06-14 19:10:41 -070079import static org.onosproject.security.AppPermission.Type.FLOWRULE_WRITE;
alshabib77b88482015-04-07 15:47:50 -070080
81/**
Thomas Vachuskaca88bb72015-04-08 19:38:02 -070082 * Provides implementation of the flow objective programming service.
alshabib77b88482015-04-07 15:47:50 -070083 */
Charles Chana7903c82018-03-15 20:14:16 -070084@Component(enabled = false)
alshabib77b88482015-04-07 15:47:50 -070085@Service
86public class FlowObjectiveManager implements FlowObjectiveService {
87
Charles Chana7903c82018-03-15 20:14:16 -070088 private static final int INSTALL_RETRY_ATTEMPTS = 5;
89 private static final long INSTALL_RETRY_INTERVAL = 1000; // ms
alshabib77b88482015-04-07 15:47:50 -070090
Yi Tseng374c5f32017-03-05 22:51:35 -080091 private static final String WORKER_PATTERN = "objective-installer-%d";
92 private static final String GROUP_THREAD_NAME = "onos/objective-installer";
93 private static final String NUM_THREAD = "numThreads";
94
Jonathan Hart17d00452015-04-21 17:10:00 -070095 private final Logger log = LoggerFactory.getLogger(getClass());
Thomas Vachuskaca88bb72015-04-08 19:38:02 -070096
pier93702472019-03-11 15:14:02 -070097 // Parameters for the accumulator, each pipeline can implement
98 // its own accumulation logic. The following parameters are used
99 // to control the accumulator.
100 private static final String ACCUMULATOR_MAX_OBJECTIVES = "accumulatorMaxObjectives";
101 // Maximum number of objectives to accumulate before processing is triggered
102 private static final int DEFAULT_ACCUMULATOR_MAX_OBJECTIVES = 1000;
103 @Property(name = ACCUMULATOR_MAX_OBJECTIVES,
104 intValue = DEFAULT_ACCUMULATOR_MAX_OBJECTIVES,
105 label = "Maximum number of objectives to accumulate")
106 private int accumulatorMaxObjectives = DEFAULT_ACCUMULATOR_MAX_OBJECTIVES;
107
108 private static final String ACCUMULATOR_MAX_IDLE_MILLIS = "accumulatorMaxIdleMillis";
109 // Maximum number of millis between objectives before processing is triggered
110 private static final int DEFAULT_ACCUMULATOR_MAX_IDLE_MILLIS = 10;
111 @Property(name = ACCUMULATOR_MAX_IDLE_MILLIS,
112 intValue = DEFAULT_ACCUMULATOR_MAX_IDLE_MILLIS,
113 label = "Maximum number of millis between objectives")
114 private int accumulatorMaxIdleMillis = DEFAULT_ACCUMULATOR_MAX_IDLE_MILLIS;
115
116 private static final String ACCUMULATOR_MAX_BATCH_MILLIS = "accumulatorMaxBatchMillis";
117 // Maximum number of millis allowed since the first objective before processing is triggered
118 private static final int DEFAULT_ACCUMULATOR_MAX_BATCH_MILLIS = 500;
119 @Property(name = ACCUMULATOR_MAX_BATCH_MILLIS,
120 intValue = DEFAULT_ACCUMULATOR_MAX_BATCH_MILLIS,
121 label = "Maximum number of millis allowed since the first objective")
122 private int accumulatorMaxBatchMillis = DEFAULT_ACCUMULATOR_MAX_BATCH_MILLIS;
123
Yi Tseng9753fc12017-03-10 18:42:46 -0800124 private static final int DEFAULT_NUM_THREADS = 4;
Yi Tseng374c5f32017-03-05 22:51:35 -0800125 @Property(name = NUM_THREAD,
126 intValue = DEFAULT_NUM_THREADS,
127 label = "Number of worker threads")
128 private int numThreads = DEFAULT_NUM_THREADS;
129
alshabib77b88482015-04-07 15:47:50 -0700130 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
131 protected DriverService driverService;
132
133 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
134 protected DeviceService deviceService;
135
136 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
alshabib77b88482015-04-07 15:47:50 -0700137 protected ClusterService clusterService;
138
Thomas Vachuskaca88bb72015-04-08 19:38:02 -0700139 // Note: The following dependencies are added on behalf of the pipeline
140 // driver behaviours to assure these services are available for their
141 // initialization.
Charles Chana7903c82018-03-15 20:14:16 -0700142 @SuppressWarnings("unused")
Thomas Vachuskaca88bb72015-04-08 19:38:02 -0700143 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
144 protected FlowRuleService flowRuleService;
145
Charles Chana7903c82018-03-15 20:14:16 -0700146 @SuppressWarnings("unused")
Thomas Vachuskaca88bb72015-04-08 19:38:02 -0700147 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
148 protected GroupService groupService;
149
alshabib2a441c62015-04-13 18:39:38 -0700150 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
151 protected FlowObjectiveStore flowObjectiveStore;
152
Yi Tseng374c5f32017-03-05 22:51:35 -0800153 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
154 protected ComponentConfigService cfgService;
155
Charles Chana7903c82018-03-15 20:14:16 -0700156 final FlowObjectiveStoreDelegate delegate = new InternalStoreDelegate();
Thomas Vachuskaca88bb72015-04-08 19:38:02 -0700157
158 private final Map<DeviceId, DriverHandler> driverHandlers = Maps.newConcurrentMap();
Charles Chana7903c82018-03-15 20:14:16 -0700159 protected final Map<DeviceId, Pipeliner> pipeliners = Maps.newConcurrentMap();
Thomas Vachuskaca88bb72015-04-08 19:38:02 -0700160
161 private final PipelinerContext context = new InnerPipelineContext();
Thomas Vachuskaca88bb72015-04-08 19:38:02 -0700162 private final DeviceListener deviceListener = new InnerDeviceListener();
Thomas Vachuskacfeff192017-08-23 15:29:34 -0700163 private final DriverListener driverListener = new InnerDriverListener();
Thomas Vachuskaca88bb72015-04-08 19:38:02 -0700164
Charles Chana7903c82018-03-15 20:14:16 -0700165 private ServiceDirectory serviceDirectory = new DefaultServiceDirectory();
alshabib910aff12015-04-09 16:55:57 -0700166
Saurav Das1547b3f2017-05-05 17:01:08 -0700167 // local stores for queuing fwd and next objectives that are waiting for an
168 // associated next objective execution to complete. The signal for completed
169 // execution comes from a pipeline driver, in this or another controller
170 // instance, via the DistributedFlowObjectiveStore.
Charles Chana7903c82018-03-15 20:14:16 -0700171 // TODO Making these cache and timeout the entries
172 final Map<Integer, Set<PendingFlowObjective>> pendingForwards = Maps.newConcurrentMap();
173 final Map<Integer, List<PendingFlowObjective>> pendingNexts = Maps.newConcurrentMap();
alshabib2a441c62015-04-13 18:39:38 -0700174
Saurav Das24431192016-03-07 19:13:00 -0800175 // local store to track which nextObjectives were sent to which device
176 // for debugging purposes
177 private Map<Integer, DeviceId> nextToDevice = Maps.newConcurrentMap();
178
Charles Chana7903c82018-03-15 20:14:16 -0700179 ExecutorService executorService;
Ruchi Sahota66b01292019-03-01 16:56:07 +0000180 protected ExecutorService devEventExecutor;
alshabib2a441c62015-04-13 18:39:38 -0700181
alshabib77b88482015-04-07 15:47:50 -0700182 @Activate
pier93702472019-03-11 15:14:02 -0700183 protected void activate(ComponentContext context) {
Yi Tseng374c5f32017-03-05 22:51:35 -0800184 cfgService.registerProperties(getClass());
185 executorService = newFixedThreadPool(numThreads,
186 groupedThreads(GROUP_THREAD_NAME, WORKER_PATTERN, log));
pier93702472019-03-11 15:14:02 -0700187 modified(context);
Ruchi Sahota66b01292019-03-01 16:56:07 +0000188 devEventExecutor = newSingleThreadScheduledExecutor(
189 groupedThreads("onos/flowobj-dev-events", "events-%d", log));
alshabib2a441c62015-04-13 18:39:38 -0700190 flowObjectiveStore.setDelegate(delegate);
alshabibaebe7752015-04-07 17:45:42 -0700191 deviceService.addListener(deviceListener);
Thomas Vachuskacfeff192017-08-23 15:29:34 -0700192 driverService.addListener(driverListener);
alshabib77b88482015-04-07 15:47:50 -0700193 log.info("Started");
194 }
195
196 @Deactivate
197 protected void deactivate() {
Yi Tseng374c5f32017-03-05 22:51:35 -0800198 cfgService.unregisterProperties(getClass(), false);
alshabib2a441c62015-04-13 18:39:38 -0700199 flowObjectiveStore.unsetDelegate(delegate);
alshabibaebe7752015-04-07 17:45:42 -0700200 deviceService.removeListener(deviceListener);
Thomas Vachuskacfeff192017-08-23 15:29:34 -0700201 driverService.removeListener(driverListener);
Thomas Vachuska866b46a2015-04-30 00:26:55 -0700202 executorService.shutdown();
Ruchi Sahota66b01292019-03-01 16:56:07 +0000203 devEventExecutor.shutdownNow();
204 devEventExecutor = null;
Thomas Vachuska866b46a2015-04-30 00:26:55 -0700205 pipeliners.clear();
206 driverHandlers.clear();
Saurav Das24431192016-03-07 19:13:00 -0800207 nextToDevice.clear();
alshabib77b88482015-04-07 15:47:50 -0700208 log.info("Stopped");
209 }
210
Yi Tseng374c5f32017-03-05 22:51:35 -0800211 @Modified
212 protected void modified(ComponentContext context) {
pier93702472019-03-11 15:14:02 -0700213 if (context != null) {
214 readComponentConfiguration(context);
215 }
216 }
217
218 /**
219 * Extracts properties from the component configuration context.
220 *
221 * @param context the component context
222 */
223 private void readComponentConfiguration(ComponentContext context) {
224 String propertyValue = Tools.get(context.getProperties(), NUM_THREAD);
Yi Tseng374c5f32017-03-05 22:51:35 -0800225 int newNumThreads = isNullOrEmpty(propertyValue) ? numThreads : Integer.parseInt(propertyValue);
226
227 if (newNumThreads != numThreads && newNumThreads > 0) {
228 numThreads = newNumThreads;
229 ExecutorService oldWorkerExecutor = executorService;
230 executorService = newFixedThreadPool(numThreads,
231 groupedThreads(GROUP_THREAD_NAME, WORKER_PATTERN, log));
232 if (oldWorkerExecutor != null) {
233 oldWorkerExecutor.shutdown();
234 }
235 log.info("Reconfigured number of worker threads to {}", numThreads);
236 }
pier93702472019-03-11 15:14:02 -0700237
238 // Reconfiguration of the accumulator parameters is allowed
239 // Note: it will affect only pipelines going through init method
240 propertyValue = Tools.get(context.getProperties(), ACCUMULATOR_MAX_OBJECTIVES);
241 int newMaxObjs = isNullOrEmpty(propertyValue) ?
242 accumulatorMaxObjectives : Integer.parseInt(propertyValue);
243 if (newMaxObjs != accumulatorMaxObjectives && newMaxObjs > 0) {
244 accumulatorMaxObjectives = newMaxObjs;
245 log.info("Reconfigured maximum number of objectives to accumulate to {}",
246 accumulatorMaxObjectives);
247 }
248
249 propertyValue = Tools.get(context.getProperties(), ACCUMULATOR_MAX_IDLE_MILLIS);
250 int newMaxIdleMS = isNullOrEmpty(propertyValue) ?
251 accumulatorMaxIdleMillis : Integer.parseInt(propertyValue);
252 if (newMaxIdleMS != accumulatorMaxIdleMillis && newMaxIdleMS > 0) {
253 accumulatorMaxIdleMillis = newMaxIdleMS;
254 log.info("Reconfigured maximum number of millis between objectives to {}",
255 accumulatorMaxIdleMillis);
256 }
257
258 propertyValue = Tools.get(context.getProperties(), ACCUMULATOR_MAX_BATCH_MILLIS);
259 int newMaxBatchMS = isNullOrEmpty(propertyValue) ?
260 accumulatorMaxBatchMillis : Integer.parseInt(propertyValue);
261 if (newMaxBatchMS != accumulatorMaxBatchMillis && newMaxBatchMS > 0) {
262 accumulatorMaxBatchMillis = newMaxBatchMS;
263 log.info("Reconfigured maximum number of millis allowed since the first objective to {}",
264 accumulatorMaxBatchMillis);
265 }
266
Yi Tseng374c5f32017-03-05 22:51:35 -0800267 }
268
Jonathan Hart17d00452015-04-21 17:10:00 -0700269 /**
270 * Task that passes the flow objective down to the driver. The task will
271 * make a few attempts to find the appropriate driver, then eventually give
272 * up and report an error if no suitable driver could be found.
273 */
Charles Chana7903c82018-03-15 20:14:16 -0700274 class ObjectiveInstaller implements Runnable {
275 final DeviceId deviceId;
276 final Objective objective;
Jonathan Hart17d00452015-04-21 17:10:00 -0700277
Sho SHIMIZUf45d85d2015-07-01 14:39:11 -0700278 private final int numAttempts;
Jonathan Hart17d00452015-04-21 17:10:00 -0700279
Charles Chana7903c82018-03-15 20:14:16 -0700280 ObjectiveInstaller(DeviceId deviceId, Objective objective) {
Sho SHIMIZUf45d85d2015-07-01 14:39:11 -0700281 this(deviceId, objective, 1);
282 }
283
Charles Chana7903c82018-03-15 20:14:16 -0700284 ObjectiveInstaller(DeviceId deviceId, Objective objective, int attemps) {
Sho SHIMIZUf45d85d2015-07-01 14:39:11 -0700285 this.deviceId = checkNotNull(deviceId);
286 this.objective = checkNotNull(objective);
Yuta HIGUCHIfbd9ae92018-01-24 23:39:06 -0800287 this.numAttempts = attemps;
alshabib910aff12015-04-09 16:55:57 -0700288 }
alshabib77b88482015-04-07 15:47:50 -0700289
Jonathan Hart17d00452015-04-21 17:10:00 -0700290 @Override
291 public void run() {
292 try {
Jonathan Hart17d00452015-04-21 17:10:00 -0700293 Pipeliner pipeliner = getDevicePipeliner(deviceId);
294
295 if (pipeliner != null) {
296 if (objective instanceof NextObjective) {
Saurav Das1547b3f2017-05-05 17:01:08 -0700297 nextToDevice.put(objective.id(), deviceId);
Jonathan Hart17d00452015-04-21 17:10:00 -0700298 pipeliner.next((NextObjective) objective);
299 } else if (objective instanceof ForwardingObjective) {
300 pipeliner.forward((ForwardingObjective) objective);
301 } else {
302 pipeliner.filter((FilteringObjective) objective);
303 }
Andrea Campanella1f8188d2016-02-29 13:24:54 -0800304 //Attempts to check if pipeliner is null for retry attempts
Jonathan Hart17d00452015-04-21 17:10:00 -0700305 } else if (numAttempts < INSTALL_RETRY_ATTEMPTS) {
Saurav Das3d038262015-04-23 12:36:58 -0700306 Thread.sleep(INSTALL_RETRY_INTERVAL);
HIGUCHI Yutad9e01052016-04-14 09:31:42 -0700307 executorService.execute(new ObjectiveInstaller(deviceId, objective, numAttempts + 1));
Jonathan Hart17d00452015-04-21 17:10:00 -0700308 } else {
309 // Otherwise we've tried a few times and failed, report an
310 // error back to the user.
311 objective.context().ifPresent(
Andrea Campanella1f8188d2016-02-29 13:24:54 -0800312 c -> c.onError(objective, ObjectiveError.NOPIPELINER));
Jonathan Hart17d00452015-04-21 17:10:00 -0700313 }
Saurav Das1547b3f2017-05-05 17:01:08 -0700314 //Exception thrown
Jonathan Hart17d00452015-04-21 17:10:00 -0700315 } catch (Exception e) {
316 log.warn("Exception while installing flow objective", e);
317 }
318 }
319 }
320
321 @Override
322 public void filter(DeviceId deviceId, FilteringObjective filteringObjective) {
Changhoon Yoonb856b812015-08-10 03:47:19 +0900323 checkPermission(FLOWRULE_WRITE);
HIGUCHI Yutad9e01052016-04-14 09:31:42 -0700324 executorService.execute(new ObjectiveInstaller(deviceId, filteringObjective));
alshabib77b88482015-04-07 15:47:50 -0700325 }
326
327 @Override
Thomas Vachuska866b46a2015-04-30 00:26:55 -0700328 public void forward(DeviceId deviceId, ForwardingObjective forwardingObjective) {
Changhoon Yoonb856b812015-08-10 03:47:19 +0900329 checkPermission(FLOWRULE_WRITE);
Yi Tseng1666b502017-05-17 11:05:18 -0700330 if (forwardingObjective.nextId() == null ||
Yi Tseng1666b502017-05-17 11:05:18 -0700331 flowObjectiveStore.getNextGroup(forwardingObjective.nextId()) != null ||
332 !queueFwdObjective(deviceId, forwardingObjective)) {
333 // fast path
334 executorService.execute(new ObjectiveInstaller(deviceId, forwardingObjective));
alshabib910aff12015-04-09 16:55:57 -0700335 }
Thomas Vachuskaca88bb72015-04-08 19:38:02 -0700336 }
337
alshabib2a441c62015-04-13 18:39:38 -0700338 @Override
Jonathan Hart17d00452015-04-21 17:10:00 -0700339 public void next(DeviceId deviceId, NextObjective nextObjective) {
Changhoon Yoonb856b812015-08-10 03:47:19 +0900340 checkPermission(FLOWRULE_WRITE);
Yi Tseng1666b502017-05-17 11:05:18 -0700341 if (nextObjective.op() == Operation.ADD ||
Charles Chanc96042e2018-08-16 21:02:34 -0700342 nextObjective.op() == Operation.VERIFY ||
Yi Tseng1666b502017-05-17 11:05:18 -0700343 flowObjectiveStore.getNextGroup(nextObjective.id()) != null ||
344 !queueNextObjective(deviceId, nextObjective)) {
345 // either group exists or we are trying to create it - let it through
346 executorService.execute(new ObjectiveInstaller(deviceId, nextObjective));
Saurav Das1547b3f2017-05-05 17:01:08 -0700347 }
alshabib2a441c62015-04-13 18:39:38 -0700348 }
349
alshabibf6ea9e62015-04-21 17:08:26 -0700350 @Override
351 public int allocateNextId() {
Changhoon Yoonb856b812015-08-10 03:47:19 +0900352 checkPermission(FLOWRULE_WRITE);
alshabibf6ea9e62015-04-21 17:08:26 -0700353 return flowObjectiveStore.allocateNextId();
354 }
355
Xin Jin313708b2015-07-09 13:43:04 -0700356 @Override
Thomas Vachuskacfeff192017-08-23 15:29:34 -0700357 public void initPolicy(String policy) {
358 }
Xin Jin313708b2015-07-09 13:43:04 -0700359
Charles Chana7903c82018-03-15 20:14:16 -0700360 boolean queueFwdObjective(DeviceId deviceId, ForwardingObjective fwd) {
Thomas Vachuskad27097c2016-06-14 19:10:41 -0700361 boolean queued = false;
362 synchronized (pendingForwards) {
363 // double check the flow objective store, because this block could run
364 // after a notification arrives
365 if (flowObjectiveStore.getNextGroup(fwd.nextId()) == null) {
366 pendingForwards.compute(fwd.nextId(), (id, pending) -> {
Saurav Das1547b3f2017-05-05 17:01:08 -0700367 PendingFlowObjective pendfo = new PendingFlowObjective(deviceId, fwd);
Thomas Vachuskad27097c2016-06-14 19:10:41 -0700368 if (pending == null) {
Pier Luigi97893112018-03-05 11:09:42 +0100369 return Sets.newLinkedHashSet(ImmutableSet.of(pendfo));
Thomas Vachuskad27097c2016-06-14 19:10:41 -0700370 } else {
Saurav Das1547b3f2017-05-05 17:01:08 -0700371 pending.add(pendfo);
Thomas Vachuskad27097c2016-06-14 19:10:41 -0700372 return pending;
373 }
374 });
375 queued = true;
376 }
377 }
378 if (queued) {
Saurav Dasc568c342018-01-25 09:49:01 -0800379 log.debug("Queued forwarding objective {} for nextId {} meant for device {}",
Thomas Vachuskad27097c2016-06-14 19:10:41 -0700380 fwd.id(), fwd.nextId(), deviceId);
381 }
382 return queued;
alshabib2a441c62015-04-13 18:39:38 -0700383 }
384
Charles Chana7903c82018-03-15 20:14:16 -0700385 boolean queueNextObjective(DeviceId deviceId, NextObjective next) {
Saurav Das1547b3f2017-05-05 17:01:08 -0700386 // we need to hold off on other operations till we get notified that the
387 // initial group creation has succeeded
388 boolean queued = false;
389 synchronized (pendingNexts) {
390 // double check the flow objective store, because this block could run
391 // after a notification arrives
392 if (flowObjectiveStore.getNextGroup(next.id()) == null) {
393 pendingNexts.compute(next.id(), (id, pending) -> {
394 PendingFlowObjective pendfo = new PendingFlowObjective(deviceId, next);
395 if (pending == null) {
Saurav Dasf14d9ef2017-12-05 15:00:23 -0800396 return Lists.newArrayList(pendfo);
Saurav Das1547b3f2017-05-05 17:01:08 -0700397 } else {
398 pending.add(pendfo);
399 return pending;
400 }
401 });
402 queued = true;
403 }
404 }
405 if (queued) {
Saurav Dasc568c342018-01-25 09:49:01 -0800406 log.debug("Queued next objective {} with operation {} meant for device {}",
Saurav Das1547b3f2017-05-05 17:01:08 -0700407 next.id(), next.op(), deviceId);
408 }
409 return queued;
410 }
411
Pier Ventre57a61cd2016-09-07 10:55:41 -0700412 /**
413 * Retrieves (if it exists) the device pipeline behaviour from the cache.
414 * Otherwise it warms the caches and triggers the init method of the Pipeline.
415 *
416 * @param deviceId the id of the device associated to the pipeline
417 * @return the implementation of the Pipeliner behaviour
418 */
Thomas Vachuskaca88bb72015-04-08 19:38:02 -0700419 private Pipeliner getDevicePipeliner(DeviceId deviceId) {
Yuta HIGUCHI1fb0a8c2016-08-12 10:59:24 -0700420 return pipeliners.computeIfAbsent(deviceId, this::initPipelineHandler);
alshabib77b88482015-04-07 15:47:50 -0700421 }
422
Yuta HIGUCHI1fb0a8c2016-08-12 10:59:24 -0700423 /**
Pier Ventre57a61cd2016-09-07 10:55:41 -0700424 * Retrieves (if it exists) the device pipeline behaviour from the cache and
425 * and triggers the init method of the pipeline. Otherwise (DEVICE_ADDED) it warms
426 * the caches and triggers the init method of the Pipeline. The rationale of this
427 * method is for managing the scenario of a switch that goes down for a failure
428 * and goes up after a while.
429 *
430 * @param deviceId the id of the device associated to the pipeline
431 * @return the implementation of the Pipeliner behaviour
432 */
433 private Pipeliner getAndInitDevicePipeliner(DeviceId deviceId) {
434 return pipeliners.compute(deviceId, (deviceIdValue, pipelinerValue) -> {
435 if (pipelinerValue != null) {
436 pipelinerValue.init(deviceId, context);
437 return pipelinerValue;
438 }
439 return this.initPipelineHandler(deviceId);
440 });
441 }
442
443 /**
Yuta HIGUCHI1fb0a8c2016-08-12 10:59:24 -0700444 * Creates and initialize {@link Pipeliner}.
445 * <p>
446 * Note: Expected to be called under per-Device lock.
447 * e.g., {@code pipeliners}' Map#compute family methods
448 *
449 * @param deviceId Device to initialize pipeliner
450 * @return {@link Pipeliner} instance or null
451 */
452 private Pipeliner initPipelineHandler(DeviceId deviceId) {
453 start = now();
Thomas Vachuska866b46a2015-04-30 00:26:55 -0700454
Jonathan Hart17d00452015-04-21 17:10:00 -0700455 // Attempt to lookup the handler in the cache
456 DriverHandler handler = driverHandlers.get(deviceId);
Thomas Vachuskab19bffb2015-07-22 10:56:16 -0700457 cTime = now();
458
Jonathan Hart17d00452015-04-21 17:10:00 -0700459 if (handler == null) {
460 try {
461 // Otherwise create it and if it has pipeline behaviour, cache it
462 handler = driverService.createHandler(deviceId);
Thomas Vachuskab19bffb2015-07-22 10:56:16 -0700463 dTime = now();
Jonathan Hart17d00452015-04-21 17:10:00 -0700464 if (!handler.driver().hasBehaviour(Pipeliner.class)) {
Yuta HIGUCHIa2a4f342017-03-17 11:38:57 -0700465 log.debug("Pipeline behaviour not supported for device {}",
Jonathan Hart17d00452015-04-21 17:10:00 -0700466 deviceId);
Yuta HIGUCHI1fb0a8c2016-08-12 10:59:24 -0700467 return null;
alshabib2a441c62015-04-13 18:39:38 -0700468 }
Jonathan Hart17d00452015-04-21 17:10:00 -0700469 } catch (ItemNotFoundException e) {
470 log.warn("No applicable driver for device {}", deviceId);
Yuta HIGUCHI1fb0a8c2016-08-12 10:59:24 -0700471 return null;
alshabib2a441c62015-04-13 18:39:38 -0700472 }
473
Jonathan Hart17d00452015-04-21 17:10:00 -0700474 driverHandlers.put(deviceId, handler);
Thomas Vachuskab19bffb2015-07-22 10:56:16 -0700475 eTime = now();
alshabib2a441c62015-04-13 18:39:38 -0700476 }
Jonathan Hart17d00452015-04-21 17:10:00 -0700477
478 // Always (re)initialize the pipeline behaviour
479 log.info("Driver {} bound to device {} ... initializing driver",
480 handler.driver().name(), deviceId);
Thomas Vachuska0121a612015-07-21 11:18:09 -0700481 hTime = now();
Jonathan Hart17d00452015-04-21 17:10:00 -0700482 Pipeliner pipeliner = handler.behaviour(Pipeliner.class);
Thomas Vachuska94c3cf42015-07-20 13:01:12 -0700483 hbTime = now();
Jonathan Hart17d00452015-04-21 17:10:00 -0700484 pipeliner.init(deviceId, context);
Yuta HIGUCHI1fb0a8c2016-08-12 10:59:24 -0700485 stopWatch();
486 return pipeliner;
alshabibaebe7752015-04-07 17:45:42 -0700487 }
alshabib77b88482015-04-07 15:47:50 -0700488
Thomas Vachuskacfeff192017-08-23 15:29:34 -0700489 private void invalidatePipelinerIfNecessary(Device device) {
490 DriverHandler handler = driverHandlers.get(device.id());
491 if (handler != null &&
492 !Objects.equals(handler.driver().name(),
493 device.annotations().value(DRIVER))) {
494 invalidatePipeliner(device.id());
495 }
496 }
497
498 private void invalidatePipeliner(DeviceId id) {
499 log.info("Invalidating cached pipeline behaviour for {}", id);
500 driverHandlers.remove(id);
501 pipeliners.remove(id);
502 if (deviceService.isAvailable(id)) {
503 getAndInitDevicePipeliner(id);
504 }
505 }
506
Thomas Vachuskaca88bb72015-04-08 19:38:02 -0700507 // Triggers driver setup when a device is (re)detected.
alshabibaebe7752015-04-07 17:45:42 -0700508 private class InnerDeviceListener implements DeviceListener {
509 @Override
510 public void event(DeviceEvent event) {
Ruchi Sahota66b01292019-03-01 16:56:07 +0000511 if (devEventExecutor != null) {
alshabibaebe7752015-04-07 17:45:42 -0700512 switch (event.type()) {
513 case DEVICE_ADDED:
514 case DEVICE_AVAILABILITY_CHANGED:
Madan Jampani0174f452015-05-29 11:52:05 -0700515 log.debug("Device either added or availability changed {}",
Thomas Vachuska9c9ff7c2015-07-20 10:38:59 -0700516 event.subject().id());
Ruchi Sahota66b01292019-03-01 16:56:07 +0000517 devEventExecutor.execute(() -> {
518 if (deviceService.isAvailable(event.subject().id())) {
Madan Jampani0174f452015-05-29 11:52:05 -0700519 log.debug("Device is now available {}", event.subject().id());
Pier Ventre57a61cd2016-09-07 10:55:41 -0700520 getAndInitDevicePipeliner(event.subject().id());
Ruchi Sahota66b01292019-03-01 16:56:07 +0000521 } else {
Yuta HIGUCHI1fb0a8c2016-08-12 10:59:24 -0700522 log.debug("Device is no longer available {}", event.subject().id());
Ruchi Sahota66b01292019-03-01 16:56:07 +0000523 }
524 });
alshabib4313d102015-04-08 18:55:08 -0700525 break;
526 case DEVICE_UPDATED:
Thomas Vachuskacfeff192017-08-23 15:29:34 -0700527 // Invalidate pipeliner and handler caches if the driver name
528 // device annotation changed.
Ruchi Sahota66b01292019-03-01 16:56:07 +0000529 devEventExecutor.execute(() -> invalidatePipelinerIfNecessary(event.subject()));
alshabib4313d102015-04-08 18:55:08 -0700530 break;
531 case DEVICE_REMOVED:
Yuta HIGUCHIad0c9902016-08-23 10:37:32 -0700532 // evict Pipeliner and Handler cache, when
533 // the Device was administratively removed.
534 //
535 // System expect the user to clear all existing flows,
536 // before removing device, especially if they intend to
537 // replace driver/pipeliner assigned to the device.
Ruchi Sahota66b01292019-03-01 16:56:07 +0000538 devEventExecutor.execute(() -> {
539 driverHandlers.remove(event.subject().id());
540 pipeliners.remove(event.subject().id());
541 });
alshabib4313d102015-04-08 18:55:08 -0700542 break;
Yuta HIGUCHIad0c9902016-08-23 10:37:32 -0700543 case DEVICE_SUSPENDED:
544 break;
alshabib4313d102015-04-08 18:55:08 -0700545 case PORT_ADDED:
546 break;
547 case PORT_UPDATED:
548 break;
549 case PORT_REMOVED:
alshabibaebe7752015-04-07 17:45:42 -0700550 break;
551 default:
Thomas Vachuskaca88bb72015-04-08 19:38:02 -0700552 break;
alshabibaebe7752015-04-07 17:45:42 -0700553 }
Ruchi Sahota66b01292019-03-01 16:56:07 +0000554 }
alshabib77b88482015-04-07 15:47:50 -0700555 }
556 }
alshabibaebe7752015-04-07 17:45:42 -0700557
Thomas Vachuskacfeff192017-08-23 15:29:34 -0700558 // Monitors driver configuration changes and invalidates the pipeliner cache entries.
559 // Note that this may leave stale entries on the device if the driver changes
560 // in manner where the new driver does not produce backward compatible flow objectives.
561 // In such cases, it is the operator's responsibility to force device re-connect.
562 private class InnerDriverListener implements DriverListener {
563 @Override
564 public void event(DriverEvent event) {
565 String driverName = event.subject().name();
566 driverHandlers.entrySet().stream()
567 .filter(e -> driverName.equals(e.getValue().driver().name()))
568 .map(Map.Entry::getKey)
569 .distinct()
570 .forEach(FlowObjectiveManager.this::invalidatePipeliner);
571 }
572 }
573
Thomas Vachuska174bb912015-07-16 21:27:14 -0700574 // Temporary mechanism to monitor pipeliner setup time-cost; there are
575 // intermittent time where this takes in excess of 2 seconds. Why?
Thomas Vachuska9c9ff7c2015-07-20 10:38:59 -0700576 private long start = 0, totals = 0, count = 0;
Thomas Vachuskab19bffb2015-07-22 10:56:16 -0700577 private long cTime, dTime, eTime, hTime, hbTime;
Thomas Vachuska9c9ff7c2015-07-20 10:38:59 -0700578 private static final long LIMIT = 500;
Thomas Vachuska174bb912015-07-16 21:27:14 -0700579
Thomas Vachuska9c9ff7c2015-07-20 10:38:59 -0700580 private long now() {
Thomas Vachuska174bb912015-07-16 21:27:14 -0700581 return System.currentTimeMillis();
582 }
583
Thomas Vachuska9c9ff7c2015-07-20 10:38:59 -0700584 private void stopWatch() {
Thomas Vachuska174bb912015-07-16 21:27:14 -0700585 long duration = System.currentTimeMillis() - start;
586 totals += duration;
587 count += 1;
588 if (duration > LIMIT) {
Thomas Vachuskab19bffb2015-07-22 10:56:16 -0700589 log.info("Pipeline setup took {} ms; avg {} ms; cTime={}, dTime={}, eTime={}, hTime={}, hbTime={}",
590 duration, totals / count, diff(cTime), diff(dTime), diff(eTime), diff(hTime), diff(hbTime));
Thomas Vachuska174bb912015-07-16 21:27:14 -0700591 }
592 }
593
Thomas Vachuska9c9ff7c2015-07-20 10:38:59 -0700594 private long diff(long bTime) {
Thomas Vachuskab19bffb2015-07-22 10:56:16 -0700595 long diff = bTime - start;
596 return diff < 0 ? 0 : diff;
Thomas Vachuska9c9ff7c2015-07-20 10:38:59 -0700597 }
598
Thomas Vachuskaca88bb72015-04-08 19:38:02 -0700599 // Processing context for initializing pipeline driver behaviours.
600 private class InnerPipelineContext implements PipelinerContext {
pier93702472019-03-11 15:14:02 -0700601
Thomas Vachuskaca88bb72015-04-08 19:38:02 -0700602 @Override
603 public ServiceDirectory directory() {
604 return serviceDirectory;
alshabibaebe7752015-04-07 17:45:42 -0700605 }
alshabib2a441c62015-04-13 18:39:38 -0700606
607 @Override
608 public FlowObjectiveStore store() {
609 return flowObjectiveStore;
610 }
pier93702472019-03-11 15:14:02 -0700611
612 @Override
613 public int accumulatorMaxObjectives() {
614 return accumulatorMaxObjectives;
615 }
616
617 @Override
618 public int accumulatorMaxIdleMillis() {
619 return accumulatorMaxIdleMillis;
620 }
621
622 @Override
623 public int accumulatorMaxBatchMillis() {
624 return accumulatorMaxBatchMillis;
625 }
alshabib2a441c62015-04-13 18:39:38 -0700626 }
627
628 private class InternalStoreDelegate implements FlowObjectiveStoreDelegate {
629 @Override
630 public void notify(ObjectiveEvent event) {
Saurav Das423fe2b2015-12-04 10:52:59 -0800631 if (event.type() == Type.ADD) {
632 log.debug("Received notification of obj event {}", event);
Saurav Das1547b3f2017-05-05 17:01:08 -0700633 Set<PendingFlowObjective> pending;
634
635 // first send all pending flows
Thomas Vachuskad27097c2016-06-14 19:10:41 -0700636 synchronized (pendingForwards) {
637 // needs to be synchronized for queueObjective lookup
638 pending = pendingForwards.remove(event.subject());
639 }
Saurav Das423fe2b2015-12-04 10:52:59 -0800640 if (pending == null) {
Saurav Das1547b3f2017-05-05 17:01:08 -0700641 log.debug("No forwarding objectives pending for this "
642 + "obj event {}", event);
643 } else {
644 log.debug("Processing {} pending forwarding objectives for nextId {}",
645 pending.size(), event.subject());
646 pending.forEach(p -> getDevicePipeliner(p.deviceId())
647 .forward((ForwardingObjective) p.flowObjective()));
Saurav Das423fe2b2015-12-04 10:52:59 -0800648 }
649
Saurav Das1547b3f2017-05-05 17:01:08 -0700650 // now check for pending next-objectives
Saurav Dasf14d9ef2017-12-05 15:00:23 -0800651 List<PendingFlowObjective> pendNexts;
Saurav Das1547b3f2017-05-05 17:01:08 -0700652 synchronized (pendingNexts) {
653 // needs to be synchronized for queueObjective lookup
Saurav Dasf14d9ef2017-12-05 15:00:23 -0800654 pendNexts = pendingNexts.remove(event.subject());
Saurav Das1547b3f2017-05-05 17:01:08 -0700655 }
Saurav Dasf14d9ef2017-12-05 15:00:23 -0800656 if (pendNexts == null) {
Saurav Das1547b3f2017-05-05 17:01:08 -0700657 log.debug("No next objectives pending for this "
658 + "obj event {}", event);
659 } else {
660 log.debug("Processing {} pending next objectives for nextId {}",
Saurav Dasf14d9ef2017-12-05 15:00:23 -0800661 pendNexts.size(), event.subject());
662 pendNexts.forEach(p -> getDevicePipeliner(p.deviceId())
Saurav Das1547b3f2017-05-05 17:01:08 -0700663 .next((NextObjective) p.flowObjective()));
664 }
alshabib2a441c62015-04-13 18:39:38 -0700665 }
alshabib2a441c62015-04-13 18:39:38 -0700666 }
667 }
668
669 /**
Saurav Das1547b3f2017-05-05 17:01:08 -0700670 * Data class used to hold a pending flow objective that could not
alshabib2a441c62015-04-13 18:39:38 -0700671 * be processed because the associated next object was not present.
Saurav Das1547b3f2017-05-05 17:01:08 -0700672 * Note that this pending flow objective could be a forwarding objective
673 * waiting for a next objective to complete execution. Or it could a
674 * next objective (with a different operation - remove, addToExisting, or
675 * removeFromExisting) waiting for a next objective with the same id to
676 * complete execution.
alshabib2a441c62015-04-13 18:39:38 -0700677 */
Charles Chana7903c82018-03-15 20:14:16 -0700678 protected class PendingFlowObjective {
alshabib2a441c62015-04-13 18:39:38 -0700679 private final DeviceId deviceId;
Saurav Das1547b3f2017-05-05 17:01:08 -0700680 private final Objective flowObj;
alshabib2a441c62015-04-13 18:39:38 -0700681
Charles Chana7903c82018-03-15 20:14:16 -0700682 PendingFlowObjective(DeviceId deviceId, Objective flowObj) {
alshabib2a441c62015-04-13 18:39:38 -0700683 this.deviceId = deviceId;
Saurav Das1547b3f2017-05-05 17:01:08 -0700684 this.flowObj = flowObj;
alshabib2a441c62015-04-13 18:39:38 -0700685 }
686
687 public DeviceId deviceId() {
688 return deviceId;
689 }
690
Saurav Das1547b3f2017-05-05 17:01:08 -0700691 public Objective flowObjective() {
692 return flowObj;
alshabib2a441c62015-04-13 18:39:38 -0700693 }
Saurav Das8a0732e2015-11-20 15:27:53 -0800694
695 @Override
696 public int hashCode() {
Saurav Das1547b3f2017-05-05 17:01:08 -0700697 return Objects.hash(deviceId, flowObj);
Saurav Das8a0732e2015-11-20 15:27:53 -0800698 }
699
700 @Override
701 public boolean equals(final Object obj) {
702 if (this == obj) {
703 return true;
704 }
Saurav Das1547b3f2017-05-05 17:01:08 -0700705 if (!(obj instanceof PendingFlowObjective)) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800706 return false;
707 }
Saurav Das1547b3f2017-05-05 17:01:08 -0700708 final PendingFlowObjective other = (PendingFlowObjective) obj;
Charles Chana7903c82018-03-15 20:14:16 -0700709
710 return this.deviceId.equals(other.deviceId) &&
711 this.flowObj.equals(other.flowObj);
Saurav Das8a0732e2015-11-20 15:27:53 -0800712 }
alshabibaebe7752015-04-07 17:45:42 -0700713 }
Saurav Das24431192016-03-07 19:13:00 -0800714
715 @Override
716 public List<String> getNextMappings() {
717 List<String> mappings = new ArrayList<>();
718 Map<Integer, NextGroup> allnexts = flowObjectiveStore.getAllGroups();
Saurav Das25190812016-05-27 13:54:07 -0700719 // XXX if the NextGroup after de-serialization actually stored info of the deviceId
Saurav Das24431192016-03-07 19:13:00 -0800720 // then info on any nextObj could be retrieved from one controller instance.
721 // Right now the drivers on one instance can only fetch for next-ids that came
722 // to them.
723 // Also, we still need to send the right next-id to the right driver as potentially
724 // there can be different drivers for different devices. But on that account,
725 // no instance should be decoding for another instance's nextIds.
726
727 for (Map.Entry<Integer, NextGroup> e : allnexts.entrySet()) {
728 // get the device this next Objective was sent to
729 DeviceId deviceId = nextToDevice.get(e.getKey());
730 mappings.add("NextId " + e.getKey() + ": " +
731 ((deviceId != null) ? deviceId : "nextId not in this onos instance"));
732 if (deviceId != null) {
733 // this instance of the controller sent the nextObj to a driver
734 Pipeliner pipeliner = getDevicePipeliner(deviceId);
735 List<String> nextMappings = pipeliner.getNextMappings(e.getValue());
736 if (nextMappings != null) {
737 mappings.addAll(nextMappings);
738 }
739 }
740 }
741 return mappings;
742 }
Saurav Dasb5c236e2016-06-07 10:08:06 -0700743
744 @Override
Saurav Das1547b3f2017-05-05 17:01:08 -0700745 public List<String> getPendingFlowObjectives() {
746 List<String> pendingFlowObjectives = new ArrayList<>();
Charles Chan54734712017-03-29 11:07:55 -0700747
Saurav Das1547b3f2017-05-05 17:01:08 -0700748 for (Integer nextId : pendingForwards.keySet()) {
749 Set<PendingFlowObjective> pfwd = pendingForwards.get(nextId);
Sho SHIMIZU81470a52016-08-12 17:24:55 -0700750 StringBuilder pend = new StringBuilder();
Charles Chan54734712017-03-29 11:07:55 -0700751 pend.append("NextId: ")
752 .append(nextId);
Saurav Das1547b3f2017-05-05 17:01:08 -0700753 for (PendingFlowObjective pf : pfwd) {
Charles Chan54734712017-03-29 11:07:55 -0700754 pend.append("\n FwdId: ")
Saurav Das1547b3f2017-05-05 17:01:08 -0700755 .append(String.format("%11s", pf.flowObjective().id()))
756 .append(", DeviceId: ")
757 .append(pf.deviceId())
758 .append(", Selector: ")
759 .append(((ForwardingObjective) pf.flowObjective())
760 .selector().criteria());
761 }
762 pendingFlowObjectives.add(pend.toString());
763 }
764
765 for (Integer nextId : pendingNexts.keySet()) {
Saurav Dasf14d9ef2017-12-05 15:00:23 -0800766 List<PendingFlowObjective> pnext = pendingNexts.get(nextId);
Saurav Das1547b3f2017-05-05 17:01:08 -0700767 StringBuilder pend = new StringBuilder();
768 pend.append("NextId: ")
769 .append(nextId);
770 for (PendingFlowObjective pn : pnext) {
771 pend.append("\n NextOp: ")
772 .append(pn.flowObjective().op())
Charles Chan54734712017-03-29 11:07:55 -0700773 .append(", DeviceId: ")
774 .append(pn.deviceId())
Saurav Das1547b3f2017-05-05 17:01:08 -0700775 .append(", Treatments: ")
776 .append(((NextObjective) pn.flowObjective())
777 .next());
Saurav Dasb5c236e2016-06-07 10:08:06 -0700778 }
Saurav Das1547b3f2017-05-05 17:01:08 -0700779 pendingFlowObjectives.add(pend.toString());
Saurav Dasb5c236e2016-06-07 10:08:06 -0700780 }
Saurav Das1547b3f2017-05-05 17:01:08 -0700781
782 return pendingFlowObjectives;
783 }
alshabib77b88482015-04-07 15:47:50 -0700784}