blob: e09f8a1bb983402a80a261fbac9552c2a4fadcc0 [file] [log] [blame]
Xin Jin313708b2015-07-09 13:43:04 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2015-present Open Networking Foundation
Xin Jin313708b2015-07-09 13:43:04 -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.composition;
17
Saurav Dasb5c236e2016-06-07 10:08:06 -070018import com.google.common.collect.ImmutableList;
Xin Jin313708b2015-07-09 13:43:04 -070019import com.google.common.collect.Maps;
20import com.google.common.collect.Sets;
Xin Jin313708b2015-07-09 13:43:04 -070021import org.onlab.osgi.DefaultServiceDirectory;
22import org.onlab.osgi.ServiceDirectory;
23import org.onlab.util.ItemNotFoundException;
24import org.onosproject.cluster.ClusterService;
Xin Jin313708b2015-07-09 13:43:04 -070025import org.onosproject.mastership.MastershipEvent;
26import org.onosproject.mastership.MastershipListener;
27import org.onosproject.mastership.MastershipService;
28import org.onosproject.net.DeviceId;
29import org.onosproject.net.behaviour.Pipeliner;
30import org.onosproject.net.behaviour.PipelinerContext;
31import org.onosproject.net.device.DeviceEvent;
32import org.onosproject.net.device.DeviceListener;
33import org.onosproject.net.device.DeviceService;
Xin Jin313708b2015-07-09 13:43:04 -070034import org.onosproject.net.driver.DriverHandler;
35import org.onosproject.net.driver.DriverService;
36import org.onosproject.net.flow.FlowRuleService;
37import org.onosproject.net.flow.criteria.Criterion;
38import org.onosproject.net.flow.instructions.Instruction;
39import org.onosproject.net.flowobjective.FilteringObjective;
40import org.onosproject.net.flowobjective.FlowObjectiveService;
41import org.onosproject.net.flowobjective.FlowObjectiveStore;
42import org.onosproject.net.flowobjective.FlowObjectiveStoreDelegate;
43import org.onosproject.net.flowobjective.ForwardingObjective;
44import org.onosproject.net.flowobjective.NextObjective;
45import org.onosproject.net.flowobjective.Objective;
46import org.onosproject.net.flowobjective.ObjectiveError;
47import org.onosproject.net.flowobjective.ObjectiveEvent;
48import org.onosproject.net.group.GroupService;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070049import org.osgi.service.component.annotations.Activate;
50import org.osgi.service.component.annotations.Deactivate;
51import org.osgi.service.component.annotations.Reference;
52import org.osgi.service.component.annotations.ReferenceCardinality;
Xin Jin313708b2015-07-09 13:43:04 -070053import org.slf4j.Logger;
54import org.slf4j.LoggerFactory;
55
56import java.util.List;
57import java.util.Map;
58import java.util.Set;
59import java.util.concurrent.ExecutorService;
60
61import static com.google.common.base.Preconditions.checkNotNull;
62import static java.util.concurrent.Executors.newFixedThreadPool;
63import static org.onlab.util.Tools.groupedThreads;
64import static org.onosproject.security.AppGuard.checkPermission;
Jian Li11599162016-01-15 15:46:16 -080065import static org.onosproject.security.AppPermission.Type.FLOWRULE_WRITE;
Xin Jin313708b2015-07-09 13:43:04 -070066
67
68/**
69 * Provides implementation of the flow objective programming service with composition feature.
70 *
71 * Note: This is an experimental, alternative implementation of the FlowObjectiveManager
72 * that supports composition. It can be enabled by setting the enable flag below to true,
73 * and you should also add "enabled = false" to the FlowObjectiveManager.
74 *
75 * The implementation relies a FlowObjectiveCompositionTree that is not yet distributed,
76 * so it will not have high availability and may break if device mastership changes.
77 * Therefore, it is safest to use this component in a single instance scenario.
78 * This comment will be removed when a distributed implementation is available.
79 */
Jian Li11599162016-01-15 15:46:16 -080080//@Component(immediate = true, enabled = false)
Xin Jin313708b2015-07-09 13:43:04 -070081public class FlowObjectiveCompositionManager implements FlowObjectiveService {
82
83 public enum PolicyOperator {
84 Parallel,
85 Sequential,
86 Override,
87 Application
88 }
89
90 public static final int INSTALL_RETRY_ATTEMPTS = 5;
91 public static final long INSTALL_RETRY_INTERVAL = 1000; // ms
92
93 private final Logger log = LoggerFactory.getLogger(getClass());
94
Ray Milkeyd84f89b2018-08-17 14:54:17 -070095 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Xin Jin313708b2015-07-09 13:43:04 -070096 protected DriverService driverService;
97
Ray Milkeyd84f89b2018-08-17 14:54:17 -070098 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Xin Jin313708b2015-07-09 13:43:04 -070099 protected DeviceService deviceService;
100
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700101 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Xin Jin313708b2015-07-09 13:43:04 -0700102 protected MastershipService mastershipService;
103
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700104 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Xin Jin313708b2015-07-09 13:43:04 -0700105 protected ClusterService clusterService;
106
107 // Note: The following dependencies are added on behalf of the pipeline
108 // driver behaviours to assure these services are available for their
109 // initialization.
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700110 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Xin Jin313708b2015-07-09 13:43:04 -0700111 protected FlowRuleService flowRuleService;
112
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700113 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Xin Jin313708b2015-07-09 13:43:04 -0700114 protected GroupService groupService;
115
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700116 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Xin Jin313708b2015-07-09 13:43:04 -0700117 protected FlowObjectiveStore flowObjectiveStore;
118
Xin Jin313708b2015-07-09 13:43:04 -0700119 private final FlowObjectiveStoreDelegate delegate = new InternalStoreDelegate();
120
121 private final Map<DeviceId, DriverHandler> driverHandlers = Maps.newConcurrentMap();
122 private final Map<DeviceId, Pipeliner> pipeliners = Maps.newConcurrentMap();
123
124 private final PipelinerContext context = new InnerPipelineContext();
125 private final MastershipListener mastershipListener = new InnerMastershipListener();
126 private final DeviceListener deviceListener = new InnerDeviceListener();
127
128 protected ServiceDirectory serviceDirectory = new DefaultServiceDirectory();
129
130 private Map<Integer, Set<PendingNext>> pendingForwards = Maps.newConcurrentMap();
131
132 private ExecutorService executorService;
133
134 private String policy;
135 private Map<DeviceId, FlowObjectiveCompositionTree> deviceCompositionTreeMap;
136
137 @Activate
138 protected void activate() {
Yuta HIGUCHI1624df12016-07-21 16:54:33 -0700139 executorService = newFixedThreadPool(4, groupedThreads("onos/objective-installer", "%d", log));
Xin Jin313708b2015-07-09 13:43:04 -0700140 flowObjectiveStore.setDelegate(delegate);
141 mastershipService.addListener(mastershipListener);
142 deviceService.addListener(deviceListener);
143 deviceService.getDevices().forEach(device -> setupPipelineHandler(device.id()));
144 deviceCompositionTreeMap = Maps.newConcurrentMap();
145 log.info("Started");
146 }
147
148 @Deactivate
149 protected void deactivate() {
150 flowObjectiveStore.unsetDelegate(delegate);
151 mastershipService.removeListener(mastershipListener);
152 deviceService.removeListener(deviceListener);
153 executorService.shutdown();
154 pipeliners.clear();
155 driverHandlers.clear();
156 deviceCompositionTreeMap.clear();
157 log.info("Stopped");
158 }
159
160 /**
161 * Task that passes the flow objective down to the driver. The task will
162 * make a few attempts to find the appropriate driver, then eventually give
163 * up and report an error if no suitable driver could be found.
164 */
165 private class ObjectiveInstaller implements Runnable {
166 private final DeviceId deviceId;
167 private final Objective objective;
168
169 private final int numAttempts;
170
171 public ObjectiveInstaller(DeviceId deviceId, Objective objective) {
172 this(deviceId, objective, 1);
173 }
174
175 public ObjectiveInstaller(DeviceId deviceId, Objective objective, int attemps) {
176 this.deviceId = checkNotNull(deviceId);
177 this.objective = checkNotNull(objective);
Yuta HIGUCHIfbd9ae92018-01-24 23:39:06 -0800178 this.numAttempts = attemps;
Xin Jin313708b2015-07-09 13:43:04 -0700179 }
180
181 @Override
182 public void run() {
183 try {
184 Pipeliner pipeliner = getDevicePipeliner(deviceId);
185
186 if (pipeliner != null) {
187 if (objective instanceof NextObjective) {
188 pipeliner.next((NextObjective) objective);
189 } else if (objective instanceof ForwardingObjective) {
190 pipeliner.forward((ForwardingObjective) objective);
191 } else {
192 pipeliner.filter((FilteringObjective) objective);
193 }
194 } else if (numAttempts < INSTALL_RETRY_ATTEMPTS) {
195 Thread.sleep(INSTALL_RETRY_INTERVAL);
HIGUCHI Yutad9e01052016-04-14 09:31:42 -0700196 executorService.execute(new ObjectiveInstaller(deviceId, objective, numAttempts + 1));
Xin Jin313708b2015-07-09 13:43:04 -0700197 } else {
198 // Otherwise we've tried a few times and failed, report an
199 // error back to the user.
200 objective.context().ifPresent(
Andrea Campanella1f8188d2016-02-29 13:24:54 -0800201 c -> c.onError(objective, ObjectiveError.NOPIPELINER));
Xin Jin313708b2015-07-09 13:43:04 -0700202 }
203 } catch (Exception e) {
204 log.warn("Exception while installing flow objective", e);
205 }
206 }
207 }
208
209 @Override
210 public void filter(DeviceId deviceId, FilteringObjective filteringObjective) {
Changhoon Yoonb856b812015-08-10 03:47:19 +0900211 checkPermission(FLOWRULE_WRITE);
Xin Jin313708b2015-07-09 13:43:04 -0700212
213 List<FilteringObjective> filteringObjectives
214 = this.deviceCompositionTreeMap.get(deviceId).updateFilter(filteringObjective);
215 for (FilteringObjective tmp : filteringObjectives) {
HIGUCHI Yutad9e01052016-04-14 09:31:42 -0700216 executorService.execute(new ObjectiveInstaller(deviceId, tmp));
Xin Jin313708b2015-07-09 13:43:04 -0700217 }
218 }
219
220 @Override
221 public void forward(DeviceId deviceId, ForwardingObjective forwardingObjective) {
Changhoon Yoonb856b812015-08-10 03:47:19 +0900222 checkPermission(FLOWRULE_WRITE);
Xin Jin313708b2015-07-09 13:43:04 -0700223
224 if (queueObjective(deviceId, forwardingObjective)) {
225 return;
226 }
227 List<ForwardingObjective> forwardingObjectives
228 = this.deviceCompositionTreeMap.get(deviceId).updateForward(forwardingObjective);
229 for (ForwardingObjective tmp : forwardingObjectives) {
HIGUCHI Yutad9e01052016-04-14 09:31:42 -0700230 executorService.execute(new ObjectiveInstaller(deviceId, tmp));
Xin Jin313708b2015-07-09 13:43:04 -0700231 }
232 }
233
234 @Override
235 public void next(DeviceId deviceId, NextObjective nextObjective) {
Changhoon Yoonb856b812015-08-10 03:47:19 +0900236 checkPermission(FLOWRULE_WRITE);
Xin Jin313708b2015-07-09 13:43:04 -0700237
238 List<NextObjective> nextObjectives = this.deviceCompositionTreeMap.get(deviceId).updateNext(nextObjective);
239 for (NextObjective tmp : nextObjectives) {
HIGUCHI Yutad9e01052016-04-14 09:31:42 -0700240 executorService.execute(new ObjectiveInstaller(deviceId, tmp));
Xin Jin313708b2015-07-09 13:43:04 -0700241 }
242 }
243
244 @Override
245 public int allocateNextId() {
Changhoon Yoonb856b812015-08-10 03:47:19 +0900246 checkPermission(FLOWRULE_WRITE);
Xin Jin313708b2015-07-09 13:43:04 -0700247
248 return flowObjectiveStore.allocateNextId();
249 }
250
251 private boolean queueObjective(DeviceId deviceId, ForwardingObjective fwd) {
252 if (fwd.nextId() != null &&
253 flowObjectiveStore.getNextGroup(fwd.nextId()) == null) {
254 log.trace("Queuing forwarding objective for nextId {}", fwd.nextId());
255 if (pendingForwards.putIfAbsent(fwd.nextId(),
256 Sets.newHashSet(new PendingNext(deviceId, fwd))) != null) {
257 Set<PendingNext> pending = pendingForwards.get(fwd.nextId());
258 pending.add(new PendingNext(deviceId, fwd));
259 }
260 return true;
261 }
262 return false;
263 }
264
265 @Override
266 public void initPolicy(String policy) {
Heedo Kang4a47a302016-02-29 17:40:23 +0900267 checkPermission(FLOWRULE_WRITE);
Xin Jin313708b2015-07-09 13:43:04 -0700268 this.policy = policy;
269 deviceService.getDevices().forEach(device ->
270 this.deviceCompositionTreeMap.put(device.id(), FlowObjectiveCompositionUtil.parsePolicyString(policy)));
271 log.info("Initialize policy {}", policy);
272 }
273
274 // Retrieves the device pipeline behaviour from the cache.
275 private Pipeliner getDevicePipeliner(DeviceId deviceId) {
Thomas Vachuska11b99fc2017-04-27 12:51:04 -0700276 return pipeliners.get(deviceId);
Xin Jin313708b2015-07-09 13:43:04 -0700277 }
278
279 private void setupPipelineHandler(DeviceId deviceId) {
Xin Jin313708b2015-07-09 13:43:04 -0700280 // Attempt to lookup the handler in the cache
281 DriverHandler handler = driverHandlers.get(deviceId);
282 if (handler == null) {
283 try {
284 // Otherwise create it and if it has pipeline behaviour, cache it
285 handler = driverService.createHandler(deviceId);
286 if (!handler.driver().hasBehaviour(Pipeliner.class)) {
287 log.warn("Pipeline behaviour not supported for device {}",
288 deviceId);
289 return;
290 }
291 } catch (ItemNotFoundException e) {
292 log.warn("No applicable driver for device {}", deviceId);
293 return;
294 }
295
296 driverHandlers.put(deviceId, handler);
297 }
298
299 // Always (re)initialize the pipeline behaviour
300 log.info("Driver {} bound to device {} ... initializing driver",
301 handler.driver().name(), deviceId);
302 Pipeliner pipeliner = handler.behaviour(Pipeliner.class);
303 pipeliner.init(deviceId, context);
304 pipeliners.putIfAbsent(deviceId, pipeliner);
305 }
306
307 // Triggers driver setup when the local node becomes a device master.
308 private class InnerMastershipListener implements MastershipListener {
309 @Override
310 public void event(MastershipEvent event) {
311 switch (event.type()) {
312 case MASTER_CHANGED:
313 log.debug("mastership changed on device {}", event.subject());
314 if (deviceService.isAvailable(event.subject())) {
315 setupPipelineHandler(event.subject());
316 }
317 break;
318 case BACKUPS_CHANGED:
319 break;
320 default:
321 break;
322 }
323 }
324 }
325
326 // Triggers driver setup when a device is (re)detected.
327 private class InnerDeviceListener implements DeviceListener {
328 @Override
329 public void event(DeviceEvent event) {
330 switch (event.type()) {
331 case DEVICE_ADDED:
332 case DEVICE_AVAILABILITY_CHANGED:
333 log.debug("Device either added or availability changed {}",
334 event.subject().id());
335 if (deviceService.isAvailable(event.subject().id())) {
336 log.debug("Device is now available {}", event.subject().id());
337 setupPipelineHandler(event.subject().id());
338 }
339 break;
340 case DEVICE_UPDATED:
341 break;
342 case DEVICE_REMOVED:
343 break;
344 case DEVICE_SUSPENDED:
345 break;
346 case PORT_ADDED:
347 break;
348 case PORT_UPDATED:
349 break;
350 case PORT_REMOVED:
351 break;
352 default:
353 break;
354 }
355 }
356 }
357
358 // Processing context for initializing pipeline driver behaviours.
359 private class InnerPipelineContext implements PipelinerContext {
360 @Override
361 public ServiceDirectory directory() {
362 return serviceDirectory;
363 }
364
365 @Override
366 public FlowObjectiveStore store() {
367 return flowObjectiveStore;
368 }
369 }
370
371 private class InternalStoreDelegate implements FlowObjectiveStoreDelegate {
372 @Override
373 public void notify(ObjectiveEvent event) {
374 log.debug("Received notification of obj event {}", event);
375 Set<PendingNext> pending = pendingForwards.remove(event.subject());
376
377 if (pending == null) {
378 log.debug("Nothing pending for this obj event");
379 return;
380 }
381
382 log.debug("Processing pending forwarding objectives {}", pending.size());
383
384 pending.forEach(p -> getDevicePipeliner(p.deviceId())
385 .forward(p.forwardingObjective()));
386
387 }
388 }
389
390 /**
391 * Data class used to hold a pending forwarding objective that could not
392 * be processed because the associated next object was not present.
393 */
394 private class PendingNext {
395 private final DeviceId deviceId;
396 private final ForwardingObjective fwd;
397
398 public PendingNext(DeviceId deviceId, ForwardingObjective fwd) {
399 this.deviceId = deviceId;
400 this.fwd = fwd;
401 }
402
403 public DeviceId deviceId() {
404 return deviceId;
405 }
406
407 public ForwardingObjective forwardingObjective() {
408 return fwd;
409 }
410 }
411
412 public static String forwardingObjectiveToString(ForwardingObjective forwardingObjective) {
413 String str = forwardingObjective.priority() + " ";
414 str += "selector( ";
415 for (Criterion criterion : forwardingObjective.selector().criteria()) {
416 str += criterion + " ";
417 }
418 str += ") treatment( ";
419 for (Instruction instruction : forwardingObjective.treatment().allInstructions()) {
420 str += instruction + " ";
421 }
422 str += ")";
423 return str;
424 }
Saurav Das24431192016-03-07 19:13:00 -0800425
426 @Override
427 public List<String> getNextMappings() {
428 // TODO Implementation deferred as this is an experimental component.
Saurav Dasb5c236e2016-06-07 10:08:06 -0700429 return ImmutableList.of();
430 }
431
432 @Override
Saurav Das1547b3f2017-05-05 17:01:08 -0700433 public List<String> getPendingFlowObjectives() {
Saurav Dasb5c236e2016-06-07 10:08:06 -0700434 // TODO Implementation deferred as this is an experimental component.
435 return ImmutableList.of();
Saurav Das24431192016-03-07 19:13:00 -0800436 }
Xin Jin313708b2015-07-09 13:43:04 -0700437}