blob: 8a3d724bc11f070a17eb435b339a6d882dec17f3 [file] [log] [blame]
Thomas Vachuskac4ee7372016-02-02 16:10:09 -08001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2016-present Open Networking Foundation
Thomas Vachuskac4ee7372016-02-02 16:10:09 -08003 *
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 */
16
17package org.onosproject.net.flow.impl;
18
hjtsao1e8a1bd2018-10-22 11:02:00 -070019import static com.google.common.collect.Lists.newArrayList;
Thomas Vachuskac4ee7372016-02-02 16:10:09 -080020import com.google.common.collect.ImmutableList;
21import com.google.common.collect.LinkedListMultimap;
22import com.google.common.collect.Multimap;
23import com.google.common.collect.Sets;
Madan Jampani71c32ca2016-06-22 08:23:18 -070024
Thomas Vachuskac4ee7372016-02-02 16:10:09 -080025import org.onosproject.core.ApplicationId;
26import org.onosproject.mastership.MastershipService;
Andrea Campanellae4273902016-02-08 13:43:30 -080027import org.onosproject.net.Device;
Thomas Vachuskac4ee7372016-02-02 16:10:09 -080028import org.onosproject.net.DeviceId;
hjtsao1e8a1bd2018-10-22 11:02:00 -070029import org.onosproject.net.behaviour.TableStatisticsDiscovery;
Carmelo Casconef4363a02016-06-02 09:45:47 -070030import org.onosproject.net.device.DeviceEvent;
31import org.onosproject.net.device.DeviceListener;
Thomas Vachuskac4ee7372016-02-02 16:10:09 -080032import org.onosproject.net.device.DeviceService;
33import org.onosproject.net.flow.CompletedBatchOperation;
34import org.onosproject.net.flow.FlowRule;
Ray Milkey7bf273c2017-09-27 16:15:15 -070035import org.onosproject.net.flow.oldbatch.FlowRuleBatchEntry;
36import org.onosproject.net.flow.oldbatch.FlowRuleBatchOperation;
Thomas Vachuskac4ee7372016-02-02 16:10:09 -080037import org.onosproject.net.flow.FlowRuleProgrammable;
38import org.onosproject.net.flow.FlowRuleProvider;
39import org.onosproject.net.flow.FlowRuleProviderService;
hjtsao1e8a1bd2018-10-22 11:02:00 -070040import org.onosproject.net.flow.TableStatisticsEntry;
Thomas Vachuskac4ee7372016-02-02 16:10:09 -080041import org.onosproject.net.provider.AbstractProvider;
42import org.onosproject.net.provider.ProviderId;
43import org.slf4j.Logger;
44import org.slf4j.LoggerFactory;
45
46import java.util.Collection;
47import java.util.Set;
Thomas Vachuskac4ee7372016-02-02 16:10:09 -080048import java.util.concurrent.ScheduledExecutorService;
49import java.util.concurrent.ScheduledFuture;
50import java.util.concurrent.TimeUnit;
hjtsao1e8a1bd2018-10-22 11:02:00 -070051import java.util.List;
Thomas Vachuskac4ee7372016-02-02 16:10:09 -080052
53import static com.google.common.collect.ImmutableSet.copyOf;
Yuta HIGUCHI1624df12016-07-21 16:54:33 -070054import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
55import static org.onlab.util.Tools.groupedThreads;
Carmelo Casconef4363a02016-06-02 09:45:47 -070056import static org.onosproject.net.device.DeviceEvent.Type.*;
Ray Milkey7bf273c2017-09-27 16:15:15 -070057import static org.onosproject.net.flow.oldbatch.FlowRuleBatchEntry.FlowRuleOperation.*;
Thomas Vachuskac4ee7372016-02-02 16:10:09 -080058
59/**
60 * Driver-based flow rule provider.
61 */
62class FlowRuleDriverProvider extends AbstractProvider implements FlowRuleProvider {
63
64 private final Logger log = LoggerFactory.getLogger(getClass());
65
66 // Perhaps to be extracted for better reuse as we deal with other.
67 public static final String SCHEME = "default";
68 public static final String PROVIDER_NAME = "org.onosproject.provider";
69
70 FlowRuleProviderService providerService;
71 private DeviceService deviceService;
72 private MastershipService mastershipService;
73
Carmelo Casconef4363a02016-06-02 09:45:47 -070074 private InternalDeviceListener deviceListener = new InternalDeviceListener();
Yuta HIGUCHI1624df12016-07-21 16:54:33 -070075 private ScheduledExecutorService executor
76 = newSingleThreadScheduledExecutor(groupedThreads("FlowRuleDriverProvider", "%d", log));
Thomas Vachuskac4ee7372016-02-02 16:10:09 -080077 private ScheduledFuture<?> poller = null;
78
79 /**
80 * Creates a new fallback flow rule provider.
81 */
82 FlowRuleDriverProvider() {
83 super(new ProviderId(SCHEME, PROVIDER_NAME));
84 }
85
86 /**
87 * Initializes the provider with necessary supporting services.
88 *
89 * @param providerService flow rule provider service
90 * @param deviceService device service
91 * @param mastershipService mastership service
92 * @param pollFrequency flow entry poll frequency
93 */
94 void init(FlowRuleProviderService providerService,
95 DeviceService deviceService, MastershipService mastershipService,
96 int pollFrequency) {
97 this.providerService = providerService;
98 this.deviceService = deviceService;
99 this.mastershipService = mastershipService;
100
Carmelo Casconef4363a02016-06-02 09:45:47 -0700101 deviceService.addListener(deviceListener);
102
Thomas Vachuskac4ee7372016-02-02 16:10:09 -0800103 if (poller != null && !poller.isCancelled()) {
104 poller.cancel(false);
105 }
106
107 poller = executor.scheduleAtFixedRate(this::pollFlowEntries, pollFrequency,
108 pollFrequency, TimeUnit.SECONDS);
109 }
110
Andrea Campanella5a3c09c2017-12-01 13:57:48 +0100111 void terminate() {
112 deviceService.removeListener(deviceListener);
113 deviceService = null;
114 providerService = null;
115 mastershipService = null;
116 poller.cancel(true);
117 executor.shutdown();
118 }
119
Thomas Vachuskac4ee7372016-02-02 16:10:09 -0800120 @Override
121 public void applyFlowRule(FlowRule... flowRules) {
122 rulesByDevice(flowRules).asMap().forEach(this::applyFlowRules);
123 }
124
125 @Override
126 public void removeFlowRule(FlowRule... flowRules) {
127 rulesByDevice(flowRules).asMap().forEach(this::removeFlowRules);
128 }
129
130 @Override
131 public void removeRulesById(ApplicationId id, FlowRule... flowRules) {
132 removeFlowRule(flowRules);
133 }
134
135 @Override
136 public void executeBatch(FlowRuleBatchOperation batch) {
137 ImmutableList.Builder<FlowRule> toAdd = ImmutableList.builder();
138 ImmutableList.Builder<FlowRule> toRemove = ImmutableList.builder();
139 for (FlowRuleBatchEntry fbe : batch.getOperations()) {
140 if (fbe.operator() == ADD || fbe.operator() == MODIFY) {
141 toAdd.add(fbe.target());
142 } else if (fbe.operator() == REMOVE) {
143 toRemove.add(fbe.target());
144 }
145 }
146
147 ImmutableList<FlowRule> rulesToAdd = toAdd.build();
148 ImmutableList<FlowRule> rulesToRemove = toRemove.build();
149
Yuta HIGUCHI660aedf2017-05-01 20:23:25 -0700150 Collection<FlowRule> added = ImmutableList.of();
151 if (!rulesToAdd.isEmpty()) {
152 added = applyFlowRules(batch.deviceId(), rulesToAdd);
153 }
154 Collection<FlowRule> removed = ImmutableList.of();
155 if (!rulesToRemove.isEmpty()) {
156 removed = removeFlowRules(batch.deviceId(), rulesToRemove);
157 }
Thomas Vachuskac4ee7372016-02-02 16:10:09 -0800158
159 Set<FlowRule> failedRules = Sets.union(Sets.difference(copyOf(rulesToAdd), copyOf(added)),
160 Sets.difference(copyOf(rulesToRemove), copyOf(removed)));
161 CompletedBatchOperation status =
162 new CompletedBatchOperation(failedRules.isEmpty(), failedRules, batch.deviceId());
163 providerService.batchOperationCompleted(batch.id(), status);
164 }
165
166 private Multimap<DeviceId, FlowRule> rulesByDevice(FlowRule[] flowRules) {
167 // Sort the flow rules by device id
168 Multimap<DeviceId, FlowRule> rulesByDevice = LinkedListMultimap.create();
169 for (FlowRule rule : flowRules) {
170 rulesByDevice.put(rule.deviceId(), rule);
171 }
172 return rulesByDevice;
173 }
174
175 private Collection<FlowRule> applyFlowRules(DeviceId deviceId, Collection<FlowRule> flowRules) {
176 FlowRuleProgrammable programmer = getFlowRuleProgrammable(deviceId);
177 return programmer != null ? programmer.applyFlowRules(flowRules) : ImmutableList.of();
178 }
179
180 private Collection<FlowRule> removeFlowRules(DeviceId deviceId, Collection<FlowRule> flowRules) {
181 FlowRuleProgrammable programmer = getFlowRuleProgrammable(deviceId);
182 return programmer != null ? programmer.removeFlowRules(flowRules) : ImmutableList.of();
183 }
184
185 private FlowRuleProgrammable getFlowRuleProgrammable(DeviceId deviceId) {
Andrea Campanellae4273902016-02-08 13:43:30 -0800186 Device device = deviceService.getDevice(deviceId);
187 if (device.is(FlowRuleProgrammable.class)) {
188 return device.as(FlowRuleProgrammable.class);
189 } else {
Andrea Campanellafe1d4732016-02-08 16:45:27 -0800190 log.debug("Device {} is not flow rule programmable", deviceId);
Andrea Campanellae4273902016-02-08 13:43:30 -0800191 return null;
Thomas Vachuskac4ee7372016-02-02 16:10:09 -0800192 }
Thomas Vachuskac4ee7372016-02-02 16:10:09 -0800193 }
194
Carmelo Casconef4363a02016-06-02 09:45:47 -0700195 private void pollDeviceFlowEntries(Device device) {
Yuta HIGUCHId4e08c42017-03-09 13:24:25 -0800196 try {
197 providerService.pushFlowMetrics(device.id(), device.as(FlowRuleProgrammable.class).getFlowEntries());
198 } catch (Exception e) {
199 log.warn("Exception thrown while polling {}", device.id(), e);
200 }
Carmelo Casconef4363a02016-06-02 09:45:47 -0700201 }
Thomas Vachuskac4ee7372016-02-02 16:10:09 -0800202
hjtsao1e8a1bd2018-10-22 11:02:00 -0700203 private void pollTableStatistics(Device device) {
204 try {
205 List<TableStatisticsEntry> tableStatsList = newArrayList(device.as(TableStatisticsDiscovery.class)
206 .getTableStatistics());
207 providerService.pushTableStatistics(device.id(), tableStatsList);
208 } catch (Exception e) {
209 log.warn("Exception thrown while polling table statistics for {}", device.id(), e);
210 }
211 }
212
Thomas Vachuskac4ee7372016-02-02 16:10:09 -0800213 private void pollFlowEntries() {
Yuta HIGUCHId4e08c42017-03-09 13:24:25 -0800214 try {
215 deviceService.getAvailableDevices().forEach(device -> {
216 if (mastershipService.isLocalMaster(device.id()) && device.is(FlowRuleProgrammable.class)) {
217 pollDeviceFlowEntries(device);
218 }
hjtsao1e8a1bd2018-10-22 11:02:00 -0700219 if (mastershipService.isLocalMaster(device.id()) && device.is(TableStatisticsDiscovery.class)) {
220 pollTableStatistics(device);
221 }
Yuta HIGUCHId4e08c42017-03-09 13:24:25 -0800222 });
223 } catch (Exception e) {
224 log.warn("Exception thrown while polling flows", e);
225 }
Thomas Vachuskac4ee7372016-02-02 16:10:09 -0800226 }
227
Yuta HIGUCHI076a7882017-04-21 16:38:51 -0700228 // potentially positive device event
229 private static final Set<DeviceEvent.Type> POSITIVE_DEVICE_EVENT =
230 Sets.immutableEnumSet(DEVICE_ADDED,
231 DEVICE_AVAILABILITY_CHANGED);
232
Carmelo Casconef4363a02016-06-02 09:45:47 -0700233 private class InternalDeviceListener implements DeviceListener {
234
235 @Override
236 public void event(DeviceEvent event) {
Madan Jampani71c32ca2016-06-22 08:23:18 -0700237 executor.execute(() -> handleEvent(event));
Carmelo Casconef4363a02016-06-02 09:45:47 -0700238 }
239
Yuta HIGUCHI076a7882017-04-21 16:38:51 -0700240 @Override
241 public boolean isRelevant(DeviceEvent event) {
242 Device device = event.subject();
243 return POSITIVE_DEVICE_EVENT.contains(event.type()) &&
244 device.is(FlowRuleProgrammable.class);
245 }
246
Madan Jampani71c32ca2016-06-22 08:23:18 -0700247 private void handleEvent(DeviceEvent event) {
Carmelo Casconef4363a02016-06-02 09:45:47 -0700248 Device device = event.subject();
Yuta HIGUCHI076a7882017-04-21 16:38:51 -0700249 boolean isRelevant = mastershipService.isLocalMaster(device.id()) &&
250 deviceService.isAvailable(device.id());
251
Madan Jampani71c32ca2016-06-22 08:23:18 -0700252 if (isRelevant) {
Yuta HIGUCHI076a7882017-04-21 16:38:51 -0700253 pollDeviceFlowEntries(device);
Madan Jampani71c32ca2016-06-22 08:23:18 -0700254 }
Carmelo Casconef4363a02016-06-02 09:45:47 -0700255 }
256 }
257
Thomas Vachuskac4ee7372016-02-02 16:10:09 -0800258}