blob: 8a80cc9220e00a2a42c0d7b7a88031d321f979b2 [file] [log] [blame]
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07001/*
2 * Copyright 2014 Open Networking Laboratory
3 *
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 */
Brian O'Connorabafb502014-12-02 22:26:20 -080016package org.onosproject.store.flow.impl;
alshabib339a3d92014-09-26 17:54:32 -070017
Brian O'Connor72cb19a2015-01-16 16:14:41 -080018import com.google.common.collect.ImmutableList;
19import com.google.common.collect.Iterables;
20import com.google.common.collect.Maps;
21import com.google.common.collect.Sets;
Brian O'Connorb9a91c12015-03-10 11:19:50 -070022import org.apache.commons.lang.math.RandomUtils;
alshabib339a3d92014-09-26 17:54:32 -070023import org.apache.felix.scr.annotations.Activate;
24import org.apache.felix.scr.annotations.Component;
25import org.apache.felix.scr.annotations.Deactivate;
Madan Jampani38b250d2014-10-17 11:02:38 -070026import org.apache.felix.scr.annotations.Reference;
27import org.apache.felix.scr.annotations.ReferenceCardinality;
alshabib339a3d92014-09-26 17:54:32 -070028import org.apache.felix.scr.annotations.Service;
Jonathan Hart4fb5cde2014-12-22 12:09:07 -080029import org.onlab.util.KryoNamespace;
Brian O'Connorabafb502014-12-02 22:26:20 -080030import org.onosproject.cluster.ClusterService;
31import org.onosproject.cluster.NodeId;
Brian O'Connor72cb19a2015-01-16 16:14:41 -080032import org.onosproject.core.CoreService;
33import org.onosproject.core.IdGenerator;
Brian O'Connorabafb502014-12-02 22:26:20 -080034import org.onosproject.net.Device;
35import org.onosproject.net.DeviceId;
Madan Jampani54d34992015-03-06 17:27:52 -080036import org.onosproject.net.device.DeviceClockService;
Brian O'Connorabafb502014-12-02 22:26:20 -080037import org.onosproject.net.device.DeviceService;
38import org.onosproject.net.flow.CompletedBatchOperation;
39import org.onosproject.net.flow.DefaultFlowEntry;
40import org.onosproject.net.flow.FlowEntry;
41import org.onosproject.net.flow.FlowEntry.FlowEntryState;
42import org.onosproject.net.flow.FlowId;
43import org.onosproject.net.flow.FlowRule;
44import org.onosproject.net.flow.FlowRuleBatchEntry;
Jonathan Hart4fb5cde2014-12-22 12:09:07 -080045import org.onosproject.net.flow.FlowRuleBatchEntry.FlowRuleOperation;
Brian O'Connorabafb502014-12-02 22:26:20 -080046import org.onosproject.net.flow.FlowRuleBatchEvent;
47import org.onosproject.net.flow.FlowRuleBatchOperation;
48import org.onosproject.net.flow.FlowRuleBatchRequest;
49import org.onosproject.net.flow.FlowRuleEvent;
Brian O'Connorabafb502014-12-02 22:26:20 -080050import org.onosproject.net.flow.FlowRuleEvent.Type;
Brian O'Connor72cb19a2015-01-16 16:14:41 -080051import org.onosproject.net.flow.FlowRuleService;
Brian O'Connorabafb502014-12-02 22:26:20 -080052import org.onosproject.net.flow.FlowRuleStore;
53import org.onosproject.net.flow.FlowRuleStoreDelegate;
54import org.onosproject.net.flow.StoredFlowEntry;
Madan Jampani54d34992015-03-06 17:27:52 -080055import org.onosproject.store.AbstractStore;
Brian O'Connorabafb502014-12-02 22:26:20 -080056import org.onosproject.store.cluster.messaging.ClusterCommunicationService;
57import org.onosproject.store.cluster.messaging.ClusterMessage;
58import org.onosproject.store.cluster.messaging.ClusterMessageHandler;
Madan Jampani54d34992015-03-06 17:27:52 -080059import org.onosproject.store.ecmap.EventuallyConsistentMap;
60import org.onosproject.store.ecmap.EventuallyConsistentMapImpl;
Brian O'Connorabafb502014-12-02 22:26:20 -080061import org.onosproject.store.flow.ReplicaInfo;
62import org.onosproject.store.flow.ReplicaInfoEvent;
63import org.onosproject.store.flow.ReplicaInfoEventListener;
64import org.onosproject.store.flow.ReplicaInfoService;
Madan Jampani54d34992015-03-06 17:27:52 -080065import org.onosproject.store.impl.ClockService;
66import org.onosproject.store.impl.MastershipBasedTimestamp;
67import org.onosproject.store.serializers.KryoNamespaces;
Brian O'Connorabafb502014-12-02 22:26:20 -080068import org.onosproject.store.serializers.KryoSerializer;
69import org.onosproject.store.serializers.StoreSerializer;
70import org.onosproject.store.serializers.impl.DistributedStoreSerializers;
alshabib339a3d92014-09-26 17:54:32 -070071import org.slf4j.Logger;
72
Brian O'Connor72cb19a2015-01-16 16:14:41 -080073import java.io.IOException;
Brian O'Connor72cb19a2015-01-16 16:14:41 -080074import java.util.Arrays;
Madan Jampani54d34992015-03-06 17:27:52 -080075import java.util.Collection;
Brian O'Connor72cb19a2015-01-16 16:14:41 -080076import java.util.Collections;
Brian O'Connor72cb19a2015-01-16 16:14:41 -080077import java.util.List;
78import java.util.Map;
Brian O'Connor72cb19a2015-01-16 16:14:41 -080079import java.util.Set;
80import java.util.concurrent.ConcurrentHashMap;
81import java.util.concurrent.ConcurrentMap;
82import java.util.concurrent.CopyOnWriteArraySet;
83import java.util.concurrent.ExecutionException;
84import java.util.concurrent.ExecutorService;
85import java.util.concurrent.Executors;
86import java.util.concurrent.Future;
87import java.util.concurrent.TimeUnit;
88import java.util.concurrent.TimeoutException;
89import java.util.stream.Collectors;
90
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080091import static org.onlab.util.Tools.groupedThreads;
Brian O'Connor72cb19a2015-01-16 16:14:41 -080092import static org.onosproject.net.flow.FlowRuleEvent.Type.RULE_REMOVED;
93import static org.onosproject.store.flow.impl.FlowStoreMessageSubjects.*;
94import static org.slf4j.LoggerFactory.getLogger;
alshabib339a3d92014-09-26 17:54:32 -070095
96/**
Madan Jampani38b250d2014-10-17 11:02:38 -070097 * Manages inventory of flow rules using a distributed state management protocol.
alshabib339a3d92014-09-26 17:54:32 -070098 */
alshabib339a3d92014-09-26 17:54:32 -070099@Component(immediate = true)
100@Service
101public class DistributedFlowRuleStore
Madan Jampani54d34992015-03-06 17:27:52 -0800102 extends AbstractStore<FlowRuleBatchEvent, FlowRuleStoreDelegate>
alshabib1c319ff2014-10-04 20:29:09 -0700103 implements FlowRuleStore {
alshabib339a3d92014-09-26 17:54:32 -0700104
105 private final Logger log = getLogger(getClass());
106
Madan Jampani2af244a2015-02-22 13:12:01 -0800107 // TODO: Make configurable.
108 private static final int MESSAGE_HANDLER_THREAD_POOL_SIZE = 8;
109
Madan Jampani54d34992015-03-06 17:27:52 -0800110 private InternalFlowTable flowTable;
alshabib339a3d92014-09-26 17:54:32 -0700111
Madan Jampani38b250d2014-10-17 11:02:38 -0700112 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Yuta HIGUCHI9def0472014-10-23 15:51:10 -0700113 protected ReplicaInfoService replicaInfoManager;
Madan Jampani38b250d2014-10-17 11:02:38 -0700114
115 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Yuta HIGUCHI9def0472014-10-23 15:51:10 -0700116 protected ClusterCommunicationService clusterCommunicator;
Madan Jampani38b250d2014-10-17 11:02:38 -0700117
118 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Yuta HIGUCHI9def0472014-10-23 15:51:10 -0700119 protected ClusterService clusterService;
120
121 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
122 protected DeviceService deviceService;
123
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800124 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Madan Jampani54d34992015-03-06 17:27:52 -0800125 protected DeviceClockService deviceClockService;
126
127 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800128 protected CoreService coreService;
Yuta HIGUCHI9def0472014-10-23 15:51:10 -0700129
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800130 private Map<Long, NodeId> pendingResponses = Maps.newConcurrentMap();
Yuta HIGUCHIbf89c742014-10-27 15:10:02 -0700131
Madan Jampani2af244a2015-02-22 13:12:01 -0800132 private ExecutorService messageHandlingExecutor;
Yuta HIGUCHI9def0472014-10-23 15:51:10 -0700133
Yuta HIGUCHIea150152014-10-28 22:55:14 -0700134 protected static final StoreSerializer SERIALIZER = new KryoSerializer() {
Madan Jampani38b250d2014-10-17 11:02:38 -0700135 @Override
136 protected void setupKryoPool() {
Yuta HIGUCHI8d143d22014-10-19 23:15:09 -0700137 serializerPool = KryoNamespace.newBuilder()
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800138 .register(DistributedStoreSerializers.STORE_COMMON)
139 .nextId(DistributedStoreSerializers.STORE_CUSTOM_BEGIN)
Yuta HIGUCHI2f158332014-11-25 13:32:06 -0800140 .register(FlowRuleEvent.class)
Jonathan Hart4fb5cde2014-12-22 12:09:07 -0800141 .register(FlowRuleEvent.Type.class)
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800142 .build();
Madan Jampani38b250d2014-10-17 11:02:38 -0700143 }
144 };
145
Yuta HIGUCHIf3d51bd2014-10-21 01:05:33 -0700146 private static final long FLOW_RULE_STORE_TIMEOUT_MILLIS = 5000;
Madan Jampani38b250d2014-10-17 11:02:38 -0700147
Yuta HIGUCHI92891d12014-10-27 20:04:38 -0700148 private ReplicaInfoEventListener replicaInfoEventListener;
149
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800150 private IdGenerator idGenerator;
151
alshabib339a3d92014-09-26 17:54:32 -0700152 @Activate
153 public void activate() {
Yuta HIGUCHI92891d12014-10-27 20:04:38 -0700154
Madan Jampani54d34992015-03-06 17:27:52 -0800155 flowTable = new InternalFlowTable();
Yuta HIGUCHI92891d12014-10-27 20:04:38 -0700156
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800157 idGenerator = coreService.getIdGenerator(FlowRuleService.FLOW_OP_TOPIC);
158
Yuta HIGUCHI4b524442014-10-28 22:23:57 -0700159 final NodeId local = clusterService.getLocalNode().id();
160
Madan Jampani2af244a2015-02-22 13:12:01 -0800161 messageHandlingExecutor = Executors.newFixedThreadPool(
162 MESSAGE_HANDLER_THREAD_POOL_SIZE,
Madan Jampani6b5b7172015-02-23 13:02:26 -0800163 groupedThreads("onos/store/flow", "message-handlers"));
Madan Jampani2af244a2015-02-22 13:12:01 -0800164
165 clusterCommunicator.addSubscriber(APPLY_BATCH_FLOWS, new OnStoreBatch(local), messageHandlingExecutor);
Madan Jampani117aaae2014-10-23 10:04:05 -0700166
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800167 clusterCommunicator.addSubscriber(REMOTE_APPLY_COMPLETED, new ClusterMessageHandler() {
168 @Override
169 public void handle(ClusterMessage message) {
170 FlowRuleBatchEvent event = SERIALIZER.decode(message.payload());
171 log.trace("received completed notification for {}", event);
172 notifyDelegate(event);
173 }
Madan Jampani2af244a2015-02-22 13:12:01 -0800174 }, messageHandlingExecutor);
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800175
Madan Jampani117aaae2014-10-23 10:04:05 -0700176 clusterCommunicator.addSubscriber(GET_FLOW_ENTRY, new ClusterMessageHandler() {
177
178 @Override
179 public void handle(ClusterMessage message) {
180 FlowRule rule = SERIALIZER.decode(message.payload());
Yuta HIGUCHIfaf9e1c2014-11-20 00:31:29 -0800181 log.trace("received get flow entry request for {}", rule);
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800182 FlowEntry flowEntry = flowTable.getFlowEntry(rule); //getFlowEntryInternal(rule);
Madan Jampani117aaae2014-10-23 10:04:05 -0700183 try {
184 message.respond(SERIALIZER.encode(flowEntry));
185 } catch (IOException e) {
186 log.error("Failed to respond back", e);
187 }
188 }
Madan Jampani2af244a2015-02-22 13:12:01 -0800189 }, messageHandlingExecutor);
Madan Jampani117aaae2014-10-23 10:04:05 -0700190
Madan Jampanif5fdef02014-10-23 21:58:10 -0700191 clusterCommunicator.addSubscriber(GET_DEVICE_FLOW_ENTRIES, new ClusterMessageHandler() {
192
193 @Override
194 public void handle(ClusterMessage message) {
195 DeviceId deviceId = SERIALIZER.decode(message.payload());
Yuta HIGUCHIfaf9e1c2014-11-20 00:31:29 -0800196 log.trace("Received get flow entries request for {} from {}", deviceId, message.sender());
Madan Jampani54d34992015-03-06 17:27:52 -0800197 Set<StoredFlowEntry> flowEntries = flowTable.getFlowEntries(deviceId);
Madan Jampanif5fdef02014-10-23 21:58:10 -0700198 try {
199 message.respond(SERIALIZER.encode(flowEntries));
200 } catch (IOException e) {
201 log.error("Failed to respond to peer's getFlowEntries request", e);
202 }
203 }
Madan Jampani2af244a2015-02-22 13:12:01 -0800204 }, messageHandlingExecutor);
Madan Jampanif5fdef02014-10-23 21:58:10 -0700205
Yuta HIGUCHIdfa45c12014-11-24 18:38:53 -0800206 clusterCommunicator.addSubscriber(REMOVE_FLOW_ENTRY, new ClusterMessageHandler() {
207
208 @Override
209 public void handle(ClusterMessage message) {
210 FlowEntry rule = SERIALIZER.decode(message.payload());
211 log.trace("received get flow entry request for {}", rule);
212 FlowRuleEvent event = removeFlowRuleInternal(rule);
213 try {
214 message.respond(SERIALIZER.encode(event));
215 } catch (IOException e) {
216 log.error("Failed to respond back", e);
217 }
218 }
Madan Jampani2af244a2015-02-22 13:12:01 -0800219 }, messageHandlingExecutor);
Yuta HIGUCHIdfa45c12014-11-24 18:38:53 -0800220
Yuta HIGUCHI92891d12014-10-27 20:04:38 -0700221 replicaInfoEventListener = new InternalReplicaInfoEventListener();
222
223 replicaInfoManager.addListener(replicaInfoEventListener);
224
alshabib339a3d92014-09-26 17:54:32 -0700225 log.info("Started");
226 }
227
228 @Deactivate
229 public void deactivate() {
Yuta HIGUCHIdfa45c12014-11-24 18:38:53 -0800230 clusterCommunicator.removeSubscriber(REMOVE_FLOW_ENTRY);
231 clusterCommunicator.removeSubscriber(GET_DEVICE_FLOW_ENTRIES);
232 clusterCommunicator.removeSubscriber(GET_FLOW_ENTRY);
233 clusterCommunicator.removeSubscriber(APPLY_BATCH_FLOWS);
alshabib371abe82015-02-13 10:44:17 -0800234 clusterCommunicator.removeSubscriber(REMOTE_APPLY_COMPLETED);
Madan Jampani2af244a2015-02-22 13:12:01 -0800235 messageHandlingExecutor.shutdown();
Yuta HIGUCHI92891d12014-10-27 20:04:38 -0700236 replicaInfoManager.removeListener(replicaInfoEventListener);
alshabib339a3d92014-09-26 17:54:32 -0700237 log.info("Stopped");
238 }
239
240
Brian O'Connor44008532014-12-04 16:41:36 -0800241 // This is not a efficient operation on a distributed sharded
Madan Jampani117aaae2014-10-23 10:04:05 -0700242 // flow store. We need to revisit the need for this operation or at least
243 // make it device specific.
alshabib339a3d92014-09-26 17:54:32 -0700244 @Override
tom9b4030d2014-10-06 10:39:03 -0700245 public int getFlowRuleCount() {
Yuta HIGUCHI9def0472014-10-23 15:51:10 -0700246 // implementing in-efficient operation for debugging purpose.
247 int sum = 0;
248 for (Device device : deviceService.getDevices()) {
249 final DeviceId did = device.id();
250 sum += Iterables.size(getFlowEntries(did));
251 }
252 return sum;
tom9b4030d2014-10-06 10:39:03 -0700253 }
254
255 @Override
Yuta HIGUCHI885868f2014-11-13 19:12:07 -0800256 public FlowEntry getFlowEntry(FlowRule rule) {
Madan Jampani117aaae2014-10-23 10:04:05 -0700257 ReplicaInfo replicaInfo = replicaInfoManager.getReplicaInfoFor(rule.deviceId());
Yuta HIGUCHI4b524442014-10-28 22:23:57 -0700258
259 if (!replicaInfo.master().isPresent()) {
Yuta HIGUCHI6b98ab62014-12-03 16:08:08 -0800260 log.warn("Failed to getFlowEntry: No master for {}", rule.deviceId());
Yuta HIGUCHI885868f2014-11-13 19:12:07 -0800261 return null;
Yuta HIGUCHI4b524442014-10-28 22:23:57 -0700262 }
263
Madan Jampani117aaae2014-10-23 10:04:05 -0700264 if (replicaInfo.master().get().equals(clusterService.getLocalNode().id())) {
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800265 return flowTable.getFlowEntry(rule);
Madan Jampani117aaae2014-10-23 10:04:05 -0700266 }
267
Yuta HIGUCHIfaf9e1c2014-11-20 00:31:29 -0800268 log.trace("Forwarding getFlowEntry to {}, which is the primary (master) for device {}",
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800269 replicaInfo.master().orNull(), rule.deviceId());
Madan Jampani117aaae2014-10-23 10:04:05 -0700270
271 ClusterMessage message = new ClusterMessage(
272 clusterService.getLocalNode().id(),
273 FlowStoreMessageSubjects.GET_FLOW_ENTRY,
274 SERIALIZER.encode(rule));
275
276 try {
Madan Jampani24f9efb2014-10-24 18:56:23 -0700277 Future<byte[]> responseFuture = clusterCommunicator.sendAndReceive(message, replicaInfo.master().get());
278 return SERIALIZER.decode(responseFuture.get(FLOW_RULE_STORE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS));
279 } catch (IOException | TimeoutException | ExecutionException | InterruptedException e) {
Brian O'Connor44008532014-12-04 16:41:36 -0800280 log.warn("Unable to fetch flow store contents from {}", replicaInfo.master().get());
Madan Jampani117aaae2014-10-23 10:04:05 -0700281 }
Brian O'Connor44008532014-12-04 16:41:36 -0800282 return null;
Yuta HIGUCHIf6f50a62014-10-19 15:58:49 -0700283 }
284
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800285
alshabib339a3d92014-09-26 17:54:32 -0700286
287 @Override
Yuta HIGUCHI885868f2014-11-13 19:12:07 -0800288 public Iterable<FlowEntry> getFlowEntries(DeviceId deviceId) {
Madan Jampanif5fdef02014-10-23 21:58:10 -0700289
290 ReplicaInfo replicaInfo = replicaInfoManager.getReplicaInfoFor(deviceId);
Yuta HIGUCHI4b524442014-10-28 22:23:57 -0700291
292 if (!replicaInfo.master().isPresent()) {
Yuta HIGUCHI6b98ab62014-12-03 16:08:08 -0800293 log.warn("Failed to getFlowEntries: No master for {}", deviceId);
Yuta HIGUCHI2c1d8472014-10-31 14:13:38 -0700294 return Collections.emptyList();
Yuta HIGUCHI4b524442014-10-28 22:23:57 -0700295 }
296
Madan Jampanif5fdef02014-10-23 21:58:10 -0700297 if (replicaInfo.master().get().equals(clusterService.getLocalNode().id())) {
Madan Jampani54d34992015-03-06 17:27:52 -0800298 return flowTable.getFlowEntries(deviceId).stream().collect(Collectors.toSet());
Madan Jampanif5fdef02014-10-23 21:58:10 -0700299 }
300
Yuta HIGUCHIfaf9e1c2014-11-20 00:31:29 -0800301 log.trace("Forwarding getFlowEntries to {}, which is the primary (master) for device {}",
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800302 replicaInfo.master().orNull(), deviceId);
Madan Jampanif5fdef02014-10-23 21:58:10 -0700303
304 ClusterMessage message = new ClusterMessage(
305 clusterService.getLocalNode().id(),
306 GET_DEVICE_FLOW_ENTRIES,
307 SERIALIZER.encode(deviceId));
308
309 try {
Madan Jampani24f9efb2014-10-24 18:56:23 -0700310 Future<byte[]> responseFuture = clusterCommunicator.sendAndReceive(message, replicaInfo.master().get());
311 return SERIALIZER.decode(responseFuture.get(FLOW_RULE_STORE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS));
312 } catch (IOException | TimeoutException | ExecutionException | InterruptedException e) {
Brian O'Connor44008532014-12-04 16:41:36 -0800313 log.warn("Unable to fetch flow store contents from {}", replicaInfo.master().get());
Madan Jampanif5fdef02014-10-23 21:58:10 -0700314 }
Yuta HIGUCHI24f79eb2014-12-12 15:46:43 -0800315 return Collections.emptyList();
Madan Jampanif5fdef02014-10-23 21:58:10 -0700316 }
317
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800318
alshabib339a3d92014-09-26 17:54:32 -0700319
320 @Override
Madan Jampani117aaae2014-10-23 10:04:05 -0700321 public void storeFlowRule(FlowRule rule) {
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800322 storeBatch(new FlowRuleBatchOperation(
323 Arrays.asList(new FlowRuleBatchEntry(FlowRuleOperation.ADD, rule)),
324 rule.deviceId(), idGenerator.getNewId()));
Madan Jampani117aaae2014-10-23 10:04:05 -0700325 }
326
Yuta HIGUCHI9def0472014-10-23 15:51:10 -0700327 @Override
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800328 public void storeBatch(FlowRuleBatchOperation operation) {
329
Yuta HIGUCHI92891d12014-10-27 20:04:38 -0700330
Madan Jampani117aaae2014-10-23 10:04:05 -0700331 if (operation.getOperations().isEmpty()) {
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800332
333 notifyDelegate(FlowRuleBatchEvent.completed(
334 new FlowRuleBatchRequest(operation.id(), Collections.emptySet()),
335 new CompletedBatchOperation(true, Collections.emptySet(),
336 operation.deviceId())));
337 return;
alshabib339a3d92014-09-26 17:54:32 -0700338 }
Madan Jampani38b250d2014-10-17 11:02:38 -0700339
alshabib371abe82015-02-13 10:44:17 -0800340 DeviceId deviceId = operation.deviceId();
Madan Jampani117aaae2014-10-23 10:04:05 -0700341
342 ReplicaInfo replicaInfo = replicaInfoManager.getReplicaInfoFor(deviceId);
343
Yuta HIGUCHI4b524442014-10-28 22:23:57 -0700344 if (!replicaInfo.master().isPresent()) {
alshabib371abe82015-02-13 10:44:17 -0800345 log.warn("No master for {} : flows will be marked for removal", deviceId);
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800346
alshabib371abe82015-02-13 10:44:17 -0800347 updateStoreInternal(operation);
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800348
349 notifyDelegate(FlowRuleBatchEvent.completed(
350 new FlowRuleBatchRequest(operation.id(), Collections.emptySet()),
alshabib371abe82015-02-13 10:44:17 -0800351 new CompletedBatchOperation(true, Collections.emptySet(), operation.deviceId())));
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800352 return;
Yuta HIGUCHI4b524442014-10-28 22:23:57 -0700353 }
354
355 final NodeId local = clusterService.getLocalNode().id();
356 if (replicaInfo.master().get().equals(local)) {
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800357 storeBatchInternal(operation);
358 return;
Madan Jampani117aaae2014-10-23 10:04:05 -0700359 }
360
Yuta HIGUCHIfaf9e1c2014-11-20 00:31:29 -0800361 log.trace("Forwarding storeBatch to {}, which is the primary (master) for device {}",
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800362 replicaInfo.master().orNull(), deviceId);
Yuta HIGUCHIf3d51bd2014-10-21 01:05:33 -0700363
Madan Jampani38b250d2014-10-17 11:02:38 -0700364 ClusterMessage message = new ClusterMessage(
Yuta HIGUCHI4b524442014-10-28 22:23:57 -0700365 local,
Yuta HIGUCHI9def0472014-10-23 15:51:10 -0700366 APPLY_BATCH_FLOWS,
Madan Jampani117aaae2014-10-23 10:04:05 -0700367 SERIALIZER.encode(operation));
Madan Jampani38b250d2014-10-17 11:02:38 -0700368
alshabib371abe82015-02-13 10:44:17 -0800369
Brian O'Connor5eb77c82015-03-02 18:09:39 -0800370 if (!clusterCommunicator.unicast(message, replicaInfo.master().get())) {
371 log.warn("Failed to storeBatch: {} to {}", message, replicaInfo.master());
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800372
373 Set<FlowRule> allFailures = operation.getOperations().stream()
Ray Milkeyf7329c72015-02-17 11:37:01 -0800374 .map(op -> op.target())
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800375 .collect(Collectors.toSet());
376
377 notifyDelegate(FlowRuleBatchEvent.completed(
378 new FlowRuleBatchRequest(operation.id(), Collections.emptySet()),
379 new CompletedBatchOperation(false, allFailures, deviceId)));
380 return;
Madan Jampani38b250d2014-10-17 11:02:38 -0700381 }
382 }
383
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800384 private void storeBatchInternal(FlowRuleBatchOperation operation) {
Yuta HIGUCHIe9b2b002014-11-04 12:25:47 -0800385
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800386 final DeviceId did = operation.deviceId();
387 //final Collection<FlowEntry> ft = flowTable.getFlowEntries(did);
alshabib371abe82015-02-13 10:44:17 -0800388 Set<FlowRuleBatchEntry> currentOps = updateStoreInternal(operation);
389 if (currentOps.isEmpty()) {
390 batchOperationComplete(FlowRuleBatchEvent.completed(
391 new FlowRuleBatchRequest(operation.id(), Collections.emptySet()),
392 new CompletedBatchOperation(true, Collections.emptySet(), did)));
393 return;
394 }
Yuta HIGUCHI92891d12014-10-27 20:04:38 -0700395
alshabib371abe82015-02-13 10:44:17 -0800396 notifyDelegate(FlowRuleBatchEvent.requested(new
397 FlowRuleBatchRequest(operation.id(),
398 currentOps), operation.deviceId()));
Yuta HIGUCHI9def0472014-10-23 15:51:10 -0700399
alshabib371abe82015-02-13 10:44:17 -0800400 }
401
402 private Set<FlowRuleBatchEntry> updateStoreInternal(FlowRuleBatchOperation operation) {
403 return operation.getOperations().stream().map(
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800404 op -> {
405 StoredFlowEntry entry;
Ray Milkeyf7329c72015-02-17 11:37:01 -0800406 switch (op.operator()) {
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800407 case ADD:
Ray Milkeyf7329c72015-02-17 11:37:01 -0800408 entry = new DefaultFlowEntry(op.target());
Madan Jampani2af244a2015-02-22 13:12:01 -0800409 // always add requested FlowRule
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800410 // Note: 2 equal FlowEntry may have different treatment
411 flowTable.remove(entry.deviceId(), entry);
412 flowTable.add(entry);
413
414 return op;
415 case REMOVE:
416 entry = flowTable.getFlowEntry(op.target());
417 if (entry != null) {
418 entry.setState(FlowEntryState.PENDING_REMOVE);
419 return op;
420 }
421 break;
422 case MODIFY:
423 //TODO: figure this out at some point
424 break;
425 default:
Ray Milkeyf7329c72015-02-17 11:37:01 -0800426 log.warn("Unknown flow operation operator: {}", op.operator());
Yuta HIGUCHI885868f2014-11-13 19:12:07 -0800427 }
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800428 return null;
Yuta HIGUCHI885868f2014-11-13 19:12:07 -0800429 }
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800430 ).filter(op -> op != null).collect(Collectors.toSet());
alshabib339a3d92014-09-26 17:54:32 -0700431 }
432
433 @Override
Madan Jampani117aaae2014-10-23 10:04:05 -0700434 public void deleteFlowRule(FlowRule rule) {
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800435 storeBatch(
436 new FlowRuleBatchOperation(
437 Arrays.asList(
438 new FlowRuleBatchEntry(
439 FlowRuleOperation.REMOVE,
440 rule)), rule.deviceId(), idGenerator.getNewId()));
alshabib339a3d92014-09-26 17:54:32 -0700441 }
442
443 @Override
Madan Jampani38b250d2014-10-17 11:02:38 -0700444 public FlowRuleEvent addOrUpdateFlowRule(FlowEntry rule) {
445 ReplicaInfo replicaInfo = replicaInfoManager.getReplicaInfoFor(rule.deviceId());
Yuta HIGUCHI4b524442014-10-28 22:23:57 -0700446 final NodeId localId = clusterService.getLocalNode().id();
447 if (localId.equals(replicaInfo.master().orNull())) {
Madan Jampani54d34992015-03-06 17:27:52 -0800448 return addOrUpdateFlowRuleInternal((StoredFlowEntry) rule);
Madan Jampani38b250d2014-10-17 11:02:38 -0700449 }
450
Yuta HIGUCHIfaf9e1c2014-11-20 00:31:29 -0800451 log.warn("Tried to update FlowRule {} state,"
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800452 + " while the Node was not the master.", rule);
Yuta HIGUCHI9def0472014-10-23 15:51:10 -0700453 return null;
Madan Jampani38b250d2014-10-17 11:02:38 -0700454 }
455
Madan Jampani54d34992015-03-06 17:27:52 -0800456 private FlowRuleEvent addOrUpdateFlowRuleInternal(StoredFlowEntry rule) {
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800457 // check if this new rule is an update to an existing entry
458 StoredFlowEntry stored = flowTable.getFlowEntry(rule);
459 if (stored != null) {
460 stored.setBytes(rule.bytes());
461 stored.setLife(rule.life());
462 stored.setPackets(rule.packets());
463 if (stored.state() == FlowEntryState.PENDING_ADD) {
464 stored.setState(FlowEntryState.ADDED);
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800465 return new FlowRuleEvent(Type.RULE_ADDED, rule);
466 }
467 return new FlowRuleEvent(Type.RULE_UPDATED, rule);
Yuta HIGUCHI885868f2014-11-13 19:12:07 -0800468 }
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800469
470 // TODO: Confirm if this behavior is correct. See SimpleFlowRuleStore
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800471 flowTable.add(rule);
472
alshabib1c319ff2014-10-04 20:29:09 -0700473 return null;
alshabib339a3d92014-09-26 17:54:32 -0700474 }
475
476 @Override
Madan Jampani38b250d2014-10-17 11:02:38 -0700477 public FlowRuleEvent removeFlowRule(FlowEntry rule) {
Yuta HIGUCHIb6eb9142014-11-26 16:18:13 -0800478 final DeviceId deviceId = rule.deviceId();
479 ReplicaInfo replicaInfo = replicaInfoManager.getReplicaInfoFor(deviceId);
Yuta HIGUCHI4b524442014-10-28 22:23:57 -0700480
481 final NodeId localId = clusterService.getLocalNode().id();
482 if (localId.equals(replicaInfo.master().orNull())) {
Madan Jampani38b250d2014-10-17 11:02:38 -0700483 // bypass and handle it locally
484 return removeFlowRuleInternal(rule);
485 }
486
Yuta HIGUCHIb6eb9142014-11-26 16:18:13 -0800487 if (!replicaInfo.master().isPresent()) {
Yuta HIGUCHI6b98ab62014-12-03 16:08:08 -0800488 log.warn("Failed to removeFlowRule: No master for {}", deviceId);
Yuta HIGUCHIb6eb9142014-11-26 16:18:13 -0800489 // TODO: revisit if this should be null (="no-op") or Exception
490 return null;
491 }
492
Yuta HIGUCHIdfa45c12014-11-24 18:38:53 -0800493 log.trace("Forwarding removeFlowRule to {}, which is the primary (master) for device {}",
Yuta HIGUCHIb6eb9142014-11-26 16:18:13 -0800494 replicaInfo.master().orNull(), deviceId);
Yuta HIGUCHIdfa45c12014-11-24 18:38:53 -0800495
496 ClusterMessage message = new ClusterMessage(
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800497 clusterService.getLocalNode().id(),
498 REMOVE_FLOW_ENTRY,
499 SERIALIZER.encode(rule));
Yuta HIGUCHIdfa45c12014-11-24 18:38:53 -0800500
501 try {
502 Future<byte[]> responseFuture = clusterCommunicator.sendAndReceive(message, replicaInfo.master().get());
503 return SERIALIZER.decode(responseFuture.get(FLOW_RULE_STORE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS));
504 } catch (IOException | TimeoutException | ExecutionException | InterruptedException e) {
Yuta HIGUCHI65934892014-12-04 17:47:44 -0800505 // TODO: Retry against latest master or throw a FlowStoreException
Yuta HIGUCHIdfa45c12014-11-24 18:38:53 -0800506 throw new RuntimeException(e);
507 }
Madan Jampani38b250d2014-10-17 11:02:38 -0700508 }
509
Yuta HIGUCHI885868f2014-11-13 19:12:07 -0800510 private FlowRuleEvent removeFlowRuleInternal(FlowEntry rule) {
Yuta HIGUCHI92891d12014-10-27 20:04:38 -0700511 final DeviceId deviceId = rule.deviceId();
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800512 // This is where one could mark a rule as removed and still keep it in the store.
513 final boolean removed = flowTable.remove(deviceId, rule); //flowEntries.remove(deviceId, rule);
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800514 if (removed) {
515 return new FlowRuleEvent(RULE_REMOVED, rule);
516 } else {
517 return null;
alshabib339a3d92014-09-26 17:54:32 -0700518 }
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800519
alshabib339a3d92014-09-26 17:54:32 -0700520 }
Madan Jampani117aaae2014-10-23 10:04:05 -0700521
522 @Override
523 public void batchOperationComplete(FlowRuleBatchEvent event) {
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800524 //FIXME: need a per device pending response
525
526 NodeId nodeId = pendingResponses.remove(event.subject().batchId());
527 if (nodeId == null) {
528 notifyDelegate(event);
529 } else {
Brian O'Connor5eb77c82015-03-02 18:09:39 -0800530 ClusterMessage message = new ClusterMessage(
531 clusterService.getLocalNode().id(),
532 REMOTE_APPLY_COMPLETED,
533 SERIALIZER.encode(event));
534 // TODO check unicast return value
535 clusterCommunicator.unicast(message, nodeId);
536 //error log: log.warn("Failed to respond to peer for batch operation result");
Yuta HIGUCHI9def0472014-10-23 15:51:10 -0700537 }
Madan Jampani117aaae2014-10-23 10:04:05 -0700538 }
Yuta HIGUCHI92891d12014-10-27 20:04:38 -0700539
Yuta HIGUCHI885868f2014-11-13 19:12:07 -0800540 private void removeFromPrimary(final DeviceId did) {
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800541 flowTable.clearDevice(did);
Yuta HIGUCHI92891d12014-10-27 20:04:38 -0700542 }
543
Yuta HIGUCHIea150152014-10-28 22:55:14 -0700544 private final class OnStoreBatch implements ClusterMessageHandler {
545 private final NodeId local;
546
547 private OnStoreBatch(NodeId local) {
548 this.local = local;
549 }
550
551 @Override
552 public void handle(final ClusterMessage message) {
553 FlowRuleBatchOperation operation = SERIALIZER.decode(message.payload());
Yuta HIGUCHIfaf9e1c2014-11-20 00:31:29 -0800554 log.debug("received batch request {}", operation);
Yuta HIGUCHIea150152014-10-28 22:55:14 -0700555
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800556 final DeviceId deviceId = operation.deviceId();
Yuta HIGUCHIea150152014-10-28 22:55:14 -0700557 ReplicaInfo replicaInfo = replicaInfoManager.getReplicaInfoFor(deviceId);
558 if (!local.equals(replicaInfo.master().orNull())) {
559
Madan Jampani54d34992015-03-06 17:27:52 -0800560 Set<FlowRule> failures = operation.getOperations()
561 .stream()
562 .map(FlowRuleBatchEntry::target)
563 .collect(Collectors.toSet());
564
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800565 CompletedBatchOperation allFailed = new CompletedBatchOperation(false, failures, deviceId);
Yuta HIGUCHIea150152014-10-28 22:55:14 -0700566 // This node is no longer the master, respond as all failed.
567 // TODO: we might want to wrap response in envelope
568 // to distinguish sw programming failure and hand over
569 // it make sense in the latter case to retry immediately.
570 try {
571 message.respond(SERIALIZER.encode(allFailed));
572 } catch (IOException e) {
573 log.error("Failed to respond back", e);
574 }
575 return;
576 }
577
Yuta HIGUCHIea150152014-10-28 22:55:14 -0700578
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800579 pendingResponses.put(operation.id(), message.sender());
580 storeBatchInternal(operation);
Yuta HIGUCHIea150152014-10-28 22:55:14 -0700581
Yuta HIGUCHIea150152014-10-28 22:55:14 -0700582 }
583 }
584
Yuta HIGUCHI92891d12014-10-27 20:04:38 -0700585 private final class InternalReplicaInfoEventListener
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800586 implements ReplicaInfoEventListener {
Yuta HIGUCHI92891d12014-10-27 20:04:38 -0700587
588 @Override
589 public void event(ReplicaInfoEvent event) {
590 final NodeId local = clusterService.getLocalNode().id();
591 final DeviceId did = event.subject();
592 final ReplicaInfo rInfo = event.replicaInfo();
593
594 switch (event.type()) {
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800595 case MASTER_CHANGED:
596 if (local.equals(rInfo.master().orNull())) {
Madan Jampani54d34992015-03-06 17:27:52 -0800597 log.info("{} is now the master for {}. Will load flow rules from backup", local, did);
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800598 // This node is the new master, populate local structure
599 // from backup
Madan Jampani54d34992015-03-06 17:27:52 -0800600 flowTable.loadFromBackup(did);
alshabib93cb57f2015-02-12 17:43:26 -0800601 }
602 //else {
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800603 // This node is no longer the master holder,
604 // clean local structure
alshabib93cb57f2015-02-12 17:43:26 -0800605 //removeFromPrimary(did);
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800606 // TODO: probably should stop pending backup activities in
607 // executors to avoid overwriting with old value
alshabib93cb57f2015-02-12 17:43:26 -0800608 //}
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800609 break;
610 default:
611 break;
Yuta HIGUCHI92891d12014-10-27 20:04:38 -0700612
613 }
614 }
615 }
616
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800617 private class InternalFlowTable {
618
Madan Jampani54d34992015-03-06 17:27:52 -0800619 private final Map<DeviceId, Map<FlowId, Set<StoredFlowEntry>>>
620 flowEntries = Maps.newConcurrentMap();
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800621
Madan Jampani54d34992015-03-06 17:27:52 -0800622 private final KryoNamespace.Builder flowSerializer = KryoNamespace.newBuilder()
623 .register(KryoNamespaces.API)
624 .register(MastershipBasedTimestamp.class);
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800625
Madan Jampani54d34992015-03-06 17:27:52 -0800626 private final ClockService<FlowId, StoredFlowEntry> clockService =
Brian O'Connorb9a91c12015-03-10 11:19:50 -0700627 (flowId, flowEntry) ->
628 (flowEntry == null) ? null : deviceClockService.getTimestamp(flowEntry.deviceId());
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800629
Madan Jampani54d34992015-03-06 17:27:52 -0800630 private final EventuallyConsistentMap<FlowId, StoredFlowEntry> backupMap =
631 new EventuallyConsistentMapImpl<>("flow-backup",
632 clusterService,
633 clusterCommunicator,
634 flowSerializer,
635 clockService,
636 (key, flowEntry) -> getPeerNodes());
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800637
Madan Jampani54d34992015-03-06 17:27:52 -0800638 private Collection<NodeId> getPeerNodes() {
639 List<NodeId> nodes = clusterService.getNodes()
640 .stream()
641 .map(node -> node.id())
642 .filter(id -> !id.equals(clusterService.getLocalNode().id()))
643 .collect(Collectors.toList());
644
645 if (nodes.isEmpty()) {
646 return ImmutableList.of();
647 } else {
Brian O'Connorb9a91c12015-03-10 11:19:50 -0700648 // get a random peer
649 return ImmutableList.of(nodes.get(RandomUtils.nextInt(nodes.size())));
Madan Jampani54d34992015-03-06 17:27:52 -0800650 }
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800651 }
652
Madan Jampani54d34992015-03-06 17:27:52 -0800653 public void loadFromBackup(DeviceId deviceId) {
654 ConcurrentMap<FlowId, Set<StoredFlowEntry>> flowTable = new ConcurrentHashMap<>();
655
656 backupMap.values()
657 .stream()
658 .filter(entry -> entry.deviceId().equals(deviceId))
659 .forEach(entry -> flowTable.computeIfPresent(entry.id(), (k, v) -> {
660 if (v == null) {
661 return Sets.newHashSet(entry);
662 } else {
663 v.add(entry);
664 }
665 return v;
666 }));
667 flowEntries.putIfAbsent(deviceId, flowTable);
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800668 }
669
670 private Set<StoredFlowEntry> getFlowEntriesInternal(DeviceId deviceId, FlowId flowId) {
Madan Jampani54d34992015-03-06 17:27:52 -0800671 return flowEntries
672 .computeIfAbsent(deviceId, key -> Maps.newConcurrentMap())
673 .computeIfAbsent(flowId, k -> new CopyOnWriteArraySet<>());
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800674 }
675
676 private StoredFlowEntry getFlowEntryInternal(FlowRule rule) {
Madan Jampani54d34992015-03-06 17:27:52 -0800677 return getFlowEntriesInternal(rule.deviceId(), rule.id())
678 .stream()
679 .filter(element -> element.equals(rule))
680 .findFirst()
681 .orElse(null);
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800682 }
683
Madan Jampani54d34992015-03-06 17:27:52 -0800684 private Set<StoredFlowEntry> getFlowEntriesInternal(DeviceId deviceId) {
685 Set<StoredFlowEntry> entries = Sets.newHashSet();
686 flowEntries.computeIfAbsent(deviceId, key -> Maps.newConcurrentMap())
687 .values()
688 .forEach(entries::addAll);
689 return entries;
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800690 }
691
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800692 public StoredFlowEntry getFlowEntry(FlowRule rule) {
693 return getFlowEntryInternal(rule);
694 }
695
Madan Jampani54d34992015-03-06 17:27:52 -0800696 public Set<StoredFlowEntry> getFlowEntries(DeviceId deviceId) {
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800697 return getFlowEntriesInternal(deviceId);
698 }
699
Madan Jampani54d34992015-03-06 17:27:52 -0800700 public void add(StoredFlowEntry rule) {
701 getFlowEntriesInternal(rule.deviceId(), rule.id()).add(rule);
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800702
Madan Jampani54d34992015-03-06 17:27:52 -0800703 try {
704 backupMap.put(rule.id(), rule);
705 } catch (Exception e) {
706 log.warn("Failed to backup flow rule", e);
707 }
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800708 }
709
710 public boolean remove(DeviceId deviceId, FlowEntry rule) {
Madan Jampani54d34992015-03-06 17:27:52 -0800711 boolean status =
712 getFlowEntriesInternal(deviceId, rule.id()).remove(rule);
713 if (status) {
714 try {
715 backupMap.remove(rule.id(), (DefaultFlowEntry) rule);
716 } catch (Exception e) {
717 log.warn("Failed to remove backup of flow rule", e);
718 }
719 }
720 return status;
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800721 }
722
723 public void clearDevice(DeviceId did) {
724 flowEntries.remove(did);
Madan Jampani54d34992015-03-06 17:27:52 -0800725 // Flow entries should continue to remain in backup map.
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800726 }
727 }
Madan Jampani54d34992015-03-06 17:27:52 -0800728}