blob: ccfe5c29c3518df1bd5919b2e3fe6c7764098e00 [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;
Madan Jampani54d34992015-03-06 17:27:52 -080022
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;
56import org.onosproject.store.Timestamp;
Brian O'Connorabafb502014-12-02 22:26:20 -080057import org.onosproject.store.cluster.messaging.ClusterCommunicationService;
58import org.onosproject.store.cluster.messaging.ClusterMessage;
59import org.onosproject.store.cluster.messaging.ClusterMessageHandler;
Madan Jampani54d34992015-03-06 17:27:52 -080060import org.onosproject.store.ecmap.EventuallyConsistentMap;
61import org.onosproject.store.ecmap.EventuallyConsistentMapImpl;
Brian O'Connorabafb502014-12-02 22:26:20 -080062import org.onosproject.store.flow.ReplicaInfo;
63import org.onosproject.store.flow.ReplicaInfoEvent;
64import org.onosproject.store.flow.ReplicaInfoEventListener;
65import org.onosproject.store.flow.ReplicaInfoService;
Madan Jampani54d34992015-03-06 17:27:52 -080066import org.onosproject.store.impl.ClockService;
67import org.onosproject.store.impl.MastershipBasedTimestamp;
68import org.onosproject.store.serializers.KryoNamespaces;
Brian O'Connorabafb502014-12-02 22:26:20 -080069import org.onosproject.store.serializers.KryoSerializer;
70import org.onosproject.store.serializers.StoreSerializer;
71import org.onosproject.store.serializers.impl.DistributedStoreSerializers;
alshabib339a3d92014-09-26 17:54:32 -070072import org.slf4j.Logger;
73
Brian O'Connor72cb19a2015-01-16 16:14:41 -080074import java.io.IOException;
Brian O'Connor72cb19a2015-01-16 16:14:41 -080075import java.util.Arrays;
Madan Jampani54d34992015-03-06 17:27:52 -080076import java.util.Collection;
Brian O'Connor72cb19a2015-01-16 16:14:41 -080077import java.util.Collections;
Brian O'Connor72cb19a2015-01-16 16:14:41 -080078import java.util.List;
79import java.util.Map;
Brian O'Connor72cb19a2015-01-16 16:14:41 -080080import java.util.Set;
81import java.util.concurrent.ConcurrentHashMap;
82import java.util.concurrent.ConcurrentMap;
83import java.util.concurrent.CopyOnWriteArraySet;
84import java.util.concurrent.ExecutionException;
85import java.util.concurrent.ExecutorService;
86import java.util.concurrent.Executors;
87import java.util.concurrent.Future;
88import java.util.concurrent.TimeUnit;
89import java.util.concurrent.TimeoutException;
90import java.util.stream.Collectors;
91
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080092import static org.onlab.util.Tools.groupedThreads;
Brian O'Connor72cb19a2015-01-16 16:14:41 -080093import static org.onosproject.net.flow.FlowRuleEvent.Type.RULE_REMOVED;
94import static org.onosproject.store.flow.impl.FlowStoreMessageSubjects.*;
95import static org.slf4j.LoggerFactory.getLogger;
alshabib339a3d92014-09-26 17:54:32 -070096
97/**
Madan Jampani38b250d2014-10-17 11:02:38 -070098 * Manages inventory of flow rules using a distributed state management protocol.
alshabib339a3d92014-09-26 17:54:32 -070099 */
alshabib339a3d92014-09-26 17:54:32 -0700100@Component(immediate = true)
101@Service
102public class DistributedFlowRuleStore
Madan Jampani54d34992015-03-06 17:27:52 -0800103 extends AbstractStore<FlowRuleBatchEvent, FlowRuleStoreDelegate>
alshabib1c319ff2014-10-04 20:29:09 -0700104 implements FlowRuleStore {
alshabib339a3d92014-09-26 17:54:32 -0700105
106 private final Logger log = getLogger(getClass());
107
Madan Jampani2af244a2015-02-22 13:12:01 -0800108 // TODO: Make configurable.
109 private static final int MESSAGE_HANDLER_THREAD_POOL_SIZE = 8;
110
Madan Jampani54d34992015-03-06 17:27:52 -0800111 private InternalFlowTable flowTable;
alshabib339a3d92014-09-26 17:54:32 -0700112
Madan Jampani38b250d2014-10-17 11:02:38 -0700113 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Yuta HIGUCHI9def0472014-10-23 15:51:10 -0700114 protected ReplicaInfoService replicaInfoManager;
Madan Jampani38b250d2014-10-17 11:02:38 -0700115
116 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Yuta HIGUCHI9def0472014-10-23 15:51:10 -0700117 protected ClusterCommunicationService clusterCommunicator;
Madan Jampani38b250d2014-10-17 11:02:38 -0700118
119 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Yuta HIGUCHI9def0472014-10-23 15:51:10 -0700120 protected ClusterService clusterService;
121
122 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
123 protected DeviceService deviceService;
124
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800125 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Madan Jampani54d34992015-03-06 17:27:52 -0800126 protected DeviceClockService deviceClockService;
127
128 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800129 protected CoreService coreService;
Yuta HIGUCHI9def0472014-10-23 15:51:10 -0700130
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800131 private Map<Long, NodeId> pendingResponses = Maps.newConcurrentMap();
Yuta HIGUCHIbf89c742014-10-27 15:10:02 -0700132
Madan Jampani2af244a2015-02-22 13:12:01 -0800133 private ExecutorService messageHandlingExecutor;
Yuta HIGUCHI9def0472014-10-23 15:51:10 -0700134
Yuta HIGUCHIea150152014-10-28 22:55:14 -0700135 protected static final StoreSerializer SERIALIZER = new KryoSerializer() {
Madan Jampani38b250d2014-10-17 11:02:38 -0700136 @Override
137 protected void setupKryoPool() {
Yuta HIGUCHI8d143d22014-10-19 23:15:09 -0700138 serializerPool = KryoNamespace.newBuilder()
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800139 .register(DistributedStoreSerializers.STORE_COMMON)
140 .nextId(DistributedStoreSerializers.STORE_CUSTOM_BEGIN)
Yuta HIGUCHI2f158332014-11-25 13:32:06 -0800141 .register(FlowRuleEvent.class)
Jonathan Hart4fb5cde2014-12-22 12:09:07 -0800142 .register(FlowRuleEvent.Type.class)
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800143 .build();
Madan Jampani38b250d2014-10-17 11:02:38 -0700144 }
145 };
146
Yuta HIGUCHIf3d51bd2014-10-21 01:05:33 -0700147 private static final long FLOW_RULE_STORE_TIMEOUT_MILLIS = 5000;
Madan Jampani38b250d2014-10-17 11:02:38 -0700148
Yuta HIGUCHI92891d12014-10-27 20:04:38 -0700149 private ReplicaInfoEventListener replicaInfoEventListener;
150
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800151 private IdGenerator idGenerator;
152
alshabib339a3d92014-09-26 17:54:32 -0700153 @Activate
154 public void activate() {
Yuta HIGUCHI92891d12014-10-27 20:04:38 -0700155
Madan Jampani54d34992015-03-06 17:27:52 -0800156 flowTable = new InternalFlowTable();
Yuta HIGUCHI92891d12014-10-27 20:04:38 -0700157
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800158 idGenerator = coreService.getIdGenerator(FlowRuleService.FLOW_OP_TOPIC);
159
Yuta HIGUCHI4b524442014-10-28 22:23:57 -0700160 final NodeId local = clusterService.getLocalNode().id();
161
Madan Jampani2af244a2015-02-22 13:12:01 -0800162 messageHandlingExecutor = Executors.newFixedThreadPool(
163 MESSAGE_HANDLER_THREAD_POOL_SIZE,
Madan Jampani6b5b7172015-02-23 13:02:26 -0800164 groupedThreads("onos/store/flow", "message-handlers"));
Madan Jampani2af244a2015-02-22 13:12:01 -0800165
166 clusterCommunicator.addSubscriber(APPLY_BATCH_FLOWS, new OnStoreBatch(local), messageHandlingExecutor);
Madan Jampani117aaae2014-10-23 10:04:05 -0700167
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800168 clusterCommunicator.addSubscriber(REMOTE_APPLY_COMPLETED, new ClusterMessageHandler() {
169 @Override
170 public void handle(ClusterMessage message) {
171 FlowRuleBatchEvent event = SERIALIZER.decode(message.payload());
172 log.trace("received completed notification for {}", event);
173 notifyDelegate(event);
174 }
Madan Jampani2af244a2015-02-22 13:12:01 -0800175 }, messageHandlingExecutor);
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800176
Madan Jampani117aaae2014-10-23 10:04:05 -0700177 clusterCommunicator.addSubscriber(GET_FLOW_ENTRY, new ClusterMessageHandler() {
178
179 @Override
180 public void handle(ClusterMessage message) {
181 FlowRule rule = SERIALIZER.decode(message.payload());
Yuta HIGUCHIfaf9e1c2014-11-20 00:31:29 -0800182 log.trace("received get flow entry request for {}", rule);
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800183 FlowEntry flowEntry = flowTable.getFlowEntry(rule); //getFlowEntryInternal(rule);
Madan Jampani117aaae2014-10-23 10:04:05 -0700184 try {
185 message.respond(SERIALIZER.encode(flowEntry));
186 } catch (IOException e) {
187 log.error("Failed to respond back", e);
188 }
189 }
Madan Jampani2af244a2015-02-22 13:12:01 -0800190 }, messageHandlingExecutor);
Madan Jampani117aaae2014-10-23 10:04:05 -0700191
Madan Jampanif5fdef02014-10-23 21:58:10 -0700192 clusterCommunicator.addSubscriber(GET_DEVICE_FLOW_ENTRIES, new ClusterMessageHandler() {
193
194 @Override
195 public void handle(ClusterMessage message) {
196 DeviceId deviceId = SERIALIZER.decode(message.payload());
Yuta HIGUCHIfaf9e1c2014-11-20 00:31:29 -0800197 log.trace("Received get flow entries request for {} from {}", deviceId, message.sender());
Madan Jampani54d34992015-03-06 17:27:52 -0800198 Set<StoredFlowEntry> flowEntries = flowTable.getFlowEntries(deviceId);
Madan Jampanif5fdef02014-10-23 21:58:10 -0700199 try {
200 message.respond(SERIALIZER.encode(flowEntries));
201 } catch (IOException e) {
202 log.error("Failed to respond to peer's getFlowEntries request", e);
203 }
204 }
Madan Jampani2af244a2015-02-22 13:12:01 -0800205 }, messageHandlingExecutor);
Madan Jampanif5fdef02014-10-23 21:58:10 -0700206
Yuta HIGUCHIdfa45c12014-11-24 18:38:53 -0800207 clusterCommunicator.addSubscriber(REMOVE_FLOW_ENTRY, new ClusterMessageHandler() {
208
209 @Override
210 public void handle(ClusterMessage message) {
211 FlowEntry rule = SERIALIZER.decode(message.payload());
212 log.trace("received get flow entry request for {}", rule);
213 FlowRuleEvent event = removeFlowRuleInternal(rule);
214 try {
215 message.respond(SERIALIZER.encode(event));
216 } catch (IOException e) {
217 log.error("Failed to respond back", e);
218 }
219 }
Madan Jampani2af244a2015-02-22 13:12:01 -0800220 }, messageHandlingExecutor);
Yuta HIGUCHIdfa45c12014-11-24 18:38:53 -0800221
Yuta HIGUCHI92891d12014-10-27 20:04:38 -0700222 replicaInfoEventListener = new InternalReplicaInfoEventListener();
223
224 replicaInfoManager.addListener(replicaInfoEventListener);
225
alshabib339a3d92014-09-26 17:54:32 -0700226 log.info("Started");
227 }
228
229 @Deactivate
230 public void deactivate() {
Yuta HIGUCHIdfa45c12014-11-24 18:38:53 -0800231 clusterCommunicator.removeSubscriber(REMOVE_FLOW_ENTRY);
232 clusterCommunicator.removeSubscriber(GET_DEVICE_FLOW_ENTRIES);
233 clusterCommunicator.removeSubscriber(GET_FLOW_ENTRY);
234 clusterCommunicator.removeSubscriber(APPLY_BATCH_FLOWS);
alshabib371abe82015-02-13 10:44:17 -0800235 clusterCommunicator.removeSubscriber(REMOTE_APPLY_COMPLETED);
Madan Jampani2af244a2015-02-22 13:12:01 -0800236 messageHandlingExecutor.shutdown();
Yuta HIGUCHI92891d12014-10-27 20:04:38 -0700237 replicaInfoManager.removeListener(replicaInfoEventListener);
alshabib339a3d92014-09-26 17:54:32 -0700238 log.info("Stopped");
239 }
240
241
Brian O'Connor44008532014-12-04 16:41:36 -0800242 // This is not a efficient operation on a distributed sharded
Madan Jampani117aaae2014-10-23 10:04:05 -0700243 // flow store. We need to revisit the need for this operation or at least
244 // make it device specific.
alshabib339a3d92014-09-26 17:54:32 -0700245 @Override
tom9b4030d2014-10-06 10:39:03 -0700246 public int getFlowRuleCount() {
Yuta HIGUCHI9def0472014-10-23 15:51:10 -0700247 // implementing in-efficient operation for debugging purpose.
248 int sum = 0;
249 for (Device device : deviceService.getDevices()) {
250 final DeviceId did = device.id();
251 sum += Iterables.size(getFlowEntries(did));
252 }
253 return sum;
tom9b4030d2014-10-06 10:39:03 -0700254 }
255
256 @Override
Yuta HIGUCHI885868f2014-11-13 19:12:07 -0800257 public FlowEntry getFlowEntry(FlowRule rule) {
Madan Jampani117aaae2014-10-23 10:04:05 -0700258 ReplicaInfo replicaInfo = replicaInfoManager.getReplicaInfoFor(rule.deviceId());
Yuta HIGUCHI4b524442014-10-28 22:23:57 -0700259
260 if (!replicaInfo.master().isPresent()) {
Yuta HIGUCHI6b98ab62014-12-03 16:08:08 -0800261 log.warn("Failed to getFlowEntry: No master for {}", rule.deviceId());
Yuta HIGUCHI885868f2014-11-13 19:12:07 -0800262 return null;
Yuta HIGUCHI4b524442014-10-28 22:23:57 -0700263 }
264
Madan Jampani117aaae2014-10-23 10:04:05 -0700265 if (replicaInfo.master().get().equals(clusterService.getLocalNode().id())) {
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800266 return flowTable.getFlowEntry(rule);
Madan Jampani117aaae2014-10-23 10:04:05 -0700267 }
268
Yuta HIGUCHIfaf9e1c2014-11-20 00:31:29 -0800269 log.trace("Forwarding getFlowEntry to {}, which is the primary (master) for device {}",
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800270 replicaInfo.master().orNull(), rule.deviceId());
Madan Jampani117aaae2014-10-23 10:04:05 -0700271
272 ClusterMessage message = new ClusterMessage(
273 clusterService.getLocalNode().id(),
274 FlowStoreMessageSubjects.GET_FLOW_ENTRY,
275 SERIALIZER.encode(rule));
276
277 try {
Madan Jampani24f9efb2014-10-24 18:56:23 -0700278 Future<byte[]> responseFuture = clusterCommunicator.sendAndReceive(message, replicaInfo.master().get());
279 return SERIALIZER.decode(responseFuture.get(FLOW_RULE_STORE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS));
280 } catch (IOException | TimeoutException | ExecutionException | InterruptedException e) {
Brian O'Connor44008532014-12-04 16:41:36 -0800281 log.warn("Unable to fetch flow store contents from {}", replicaInfo.master().get());
Madan Jampani117aaae2014-10-23 10:04:05 -0700282 }
Brian O'Connor44008532014-12-04 16:41:36 -0800283 return null;
Yuta HIGUCHIf6f50a62014-10-19 15:58:49 -0700284 }
285
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800286
alshabib339a3d92014-09-26 17:54:32 -0700287
288 @Override
Yuta HIGUCHI885868f2014-11-13 19:12:07 -0800289 public Iterable<FlowEntry> getFlowEntries(DeviceId deviceId) {
Madan Jampanif5fdef02014-10-23 21:58:10 -0700290
291 ReplicaInfo replicaInfo = replicaInfoManager.getReplicaInfoFor(deviceId);
Yuta HIGUCHI4b524442014-10-28 22:23:57 -0700292
293 if (!replicaInfo.master().isPresent()) {
Yuta HIGUCHI6b98ab62014-12-03 16:08:08 -0800294 log.warn("Failed to getFlowEntries: No master for {}", deviceId);
Yuta HIGUCHI2c1d8472014-10-31 14:13:38 -0700295 return Collections.emptyList();
Yuta HIGUCHI4b524442014-10-28 22:23:57 -0700296 }
297
Madan Jampanif5fdef02014-10-23 21:58:10 -0700298 if (replicaInfo.master().get().equals(clusterService.getLocalNode().id())) {
Madan Jampani54d34992015-03-06 17:27:52 -0800299 return flowTable.getFlowEntries(deviceId).stream().collect(Collectors.toSet());
Madan Jampanif5fdef02014-10-23 21:58:10 -0700300 }
301
Yuta HIGUCHIfaf9e1c2014-11-20 00:31:29 -0800302 log.trace("Forwarding getFlowEntries to {}, which is the primary (master) for device {}",
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800303 replicaInfo.master().orNull(), deviceId);
Madan Jampanif5fdef02014-10-23 21:58:10 -0700304
305 ClusterMessage message = new ClusterMessage(
306 clusterService.getLocalNode().id(),
307 GET_DEVICE_FLOW_ENTRIES,
308 SERIALIZER.encode(deviceId));
309
310 try {
Madan Jampani24f9efb2014-10-24 18:56:23 -0700311 Future<byte[]> responseFuture = clusterCommunicator.sendAndReceive(message, replicaInfo.master().get());
312 return SERIALIZER.decode(responseFuture.get(FLOW_RULE_STORE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS));
313 } catch (IOException | TimeoutException | ExecutionException | InterruptedException e) {
Brian O'Connor44008532014-12-04 16:41:36 -0800314 log.warn("Unable to fetch flow store contents from {}", replicaInfo.master().get());
Madan Jampanif5fdef02014-10-23 21:58:10 -0700315 }
Yuta HIGUCHI24f79eb2014-12-12 15:46:43 -0800316 return Collections.emptyList();
Madan Jampanif5fdef02014-10-23 21:58:10 -0700317 }
318
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800319
alshabib339a3d92014-09-26 17:54:32 -0700320
321 @Override
Madan Jampani117aaae2014-10-23 10:04:05 -0700322 public void storeFlowRule(FlowRule rule) {
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800323 storeBatch(new FlowRuleBatchOperation(
324 Arrays.asList(new FlowRuleBatchEntry(FlowRuleOperation.ADD, rule)),
325 rule.deviceId(), idGenerator.getNewId()));
Madan Jampani117aaae2014-10-23 10:04:05 -0700326 }
327
Yuta HIGUCHI9def0472014-10-23 15:51:10 -0700328 @Override
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800329 public void storeBatch(FlowRuleBatchOperation operation) {
330
Yuta HIGUCHI92891d12014-10-27 20:04:38 -0700331
Madan Jampani117aaae2014-10-23 10:04:05 -0700332 if (operation.getOperations().isEmpty()) {
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800333
334 notifyDelegate(FlowRuleBatchEvent.completed(
335 new FlowRuleBatchRequest(operation.id(), Collections.emptySet()),
336 new CompletedBatchOperation(true, Collections.emptySet(),
337 operation.deviceId())));
338 return;
alshabib339a3d92014-09-26 17:54:32 -0700339 }
Madan Jampani38b250d2014-10-17 11:02:38 -0700340
alshabib371abe82015-02-13 10:44:17 -0800341 DeviceId deviceId = operation.deviceId();
Madan Jampani117aaae2014-10-23 10:04:05 -0700342
343 ReplicaInfo replicaInfo = replicaInfoManager.getReplicaInfoFor(deviceId);
344
Yuta HIGUCHI4b524442014-10-28 22:23:57 -0700345 if (!replicaInfo.master().isPresent()) {
alshabib371abe82015-02-13 10:44:17 -0800346 log.warn("No master for {} : flows will be marked for removal", deviceId);
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800347
alshabib371abe82015-02-13 10:44:17 -0800348 updateStoreInternal(operation);
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800349
350 notifyDelegate(FlowRuleBatchEvent.completed(
351 new FlowRuleBatchRequest(operation.id(), Collections.emptySet()),
alshabib371abe82015-02-13 10:44:17 -0800352 new CompletedBatchOperation(true, Collections.emptySet(), operation.deviceId())));
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800353 return;
Yuta HIGUCHI4b524442014-10-28 22:23:57 -0700354 }
355
356 final NodeId local = clusterService.getLocalNode().id();
357 if (replicaInfo.master().get().equals(local)) {
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800358 storeBatchInternal(operation);
359 return;
Madan Jampani117aaae2014-10-23 10:04:05 -0700360 }
361
Yuta HIGUCHIfaf9e1c2014-11-20 00:31:29 -0800362 log.trace("Forwarding storeBatch to {}, which is the primary (master) for device {}",
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800363 replicaInfo.master().orNull(), deviceId);
Yuta HIGUCHIf3d51bd2014-10-21 01:05:33 -0700364
Madan Jampani38b250d2014-10-17 11:02:38 -0700365 ClusterMessage message = new ClusterMessage(
Yuta HIGUCHI4b524442014-10-28 22:23:57 -0700366 local,
Yuta HIGUCHI9def0472014-10-23 15:51:10 -0700367 APPLY_BATCH_FLOWS,
Madan Jampani117aaae2014-10-23 10:04:05 -0700368 SERIALIZER.encode(operation));
Madan Jampani38b250d2014-10-17 11:02:38 -0700369
alshabib371abe82015-02-13 10:44:17 -0800370
Brian O'Connor5eb77c82015-03-02 18:09:39 -0800371 if (!clusterCommunicator.unicast(message, replicaInfo.master().get())) {
372 log.warn("Failed to storeBatch: {} to {}", message, replicaInfo.master());
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800373
374 Set<FlowRule> allFailures = operation.getOperations().stream()
Ray Milkeyf7329c72015-02-17 11:37:01 -0800375 .map(op -> op.target())
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800376 .collect(Collectors.toSet());
377
378 notifyDelegate(FlowRuleBatchEvent.completed(
379 new FlowRuleBatchRequest(operation.id(), Collections.emptySet()),
380 new CompletedBatchOperation(false, allFailures, deviceId)));
381 return;
Madan Jampani38b250d2014-10-17 11:02:38 -0700382 }
383 }
384
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800385 private void storeBatchInternal(FlowRuleBatchOperation operation) {
Yuta HIGUCHIe9b2b002014-11-04 12:25:47 -0800386
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800387 final DeviceId did = operation.deviceId();
388 //final Collection<FlowEntry> ft = flowTable.getFlowEntries(did);
alshabib371abe82015-02-13 10:44:17 -0800389 Set<FlowRuleBatchEntry> currentOps = updateStoreInternal(operation);
390 if (currentOps.isEmpty()) {
391 batchOperationComplete(FlowRuleBatchEvent.completed(
392 new FlowRuleBatchRequest(operation.id(), Collections.emptySet()),
393 new CompletedBatchOperation(true, Collections.emptySet(), did)));
394 return;
395 }
Yuta HIGUCHI92891d12014-10-27 20:04:38 -0700396
alshabib371abe82015-02-13 10:44:17 -0800397 notifyDelegate(FlowRuleBatchEvent.requested(new
398 FlowRuleBatchRequest(operation.id(),
399 currentOps), operation.deviceId()));
Yuta HIGUCHI9def0472014-10-23 15:51:10 -0700400
alshabib371abe82015-02-13 10:44:17 -0800401 }
402
403 private Set<FlowRuleBatchEntry> updateStoreInternal(FlowRuleBatchOperation operation) {
404 return operation.getOperations().stream().map(
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800405 op -> {
406 StoredFlowEntry entry;
Ray Milkeyf7329c72015-02-17 11:37:01 -0800407 switch (op.operator()) {
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800408 case ADD:
Ray Milkeyf7329c72015-02-17 11:37:01 -0800409 entry = new DefaultFlowEntry(op.target());
Madan Jampani2af244a2015-02-22 13:12:01 -0800410 // always add requested FlowRule
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800411 // Note: 2 equal FlowEntry may have different treatment
412 flowTable.remove(entry.deviceId(), entry);
413 flowTable.add(entry);
414
415 return op;
416 case REMOVE:
417 entry = flowTable.getFlowEntry(op.target());
418 if (entry != null) {
419 entry.setState(FlowEntryState.PENDING_REMOVE);
420 return op;
421 }
422 break;
423 case MODIFY:
424 //TODO: figure this out at some point
425 break;
426 default:
Ray Milkeyf7329c72015-02-17 11:37:01 -0800427 log.warn("Unknown flow operation operator: {}", op.operator());
Yuta HIGUCHI885868f2014-11-13 19:12:07 -0800428 }
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800429 return null;
Yuta HIGUCHI885868f2014-11-13 19:12:07 -0800430 }
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800431 ).filter(op -> op != null).collect(Collectors.toSet());
alshabib339a3d92014-09-26 17:54:32 -0700432 }
433
434 @Override
Madan Jampani117aaae2014-10-23 10:04:05 -0700435 public void deleteFlowRule(FlowRule rule) {
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800436 storeBatch(
437 new FlowRuleBatchOperation(
438 Arrays.asList(
439 new FlowRuleBatchEntry(
440 FlowRuleOperation.REMOVE,
441 rule)), rule.deviceId(), idGenerator.getNewId()));
alshabib339a3d92014-09-26 17:54:32 -0700442 }
443
444 @Override
Madan Jampani38b250d2014-10-17 11:02:38 -0700445 public FlowRuleEvent addOrUpdateFlowRule(FlowEntry rule) {
446 ReplicaInfo replicaInfo = replicaInfoManager.getReplicaInfoFor(rule.deviceId());
Yuta HIGUCHI4b524442014-10-28 22:23:57 -0700447 final NodeId localId = clusterService.getLocalNode().id();
448 if (localId.equals(replicaInfo.master().orNull())) {
Madan Jampani54d34992015-03-06 17:27:52 -0800449 return addOrUpdateFlowRuleInternal((StoredFlowEntry) rule);
Madan Jampani38b250d2014-10-17 11:02:38 -0700450 }
451
Yuta HIGUCHIfaf9e1c2014-11-20 00:31:29 -0800452 log.warn("Tried to update FlowRule {} state,"
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800453 + " while the Node was not the master.", rule);
Yuta HIGUCHI9def0472014-10-23 15:51:10 -0700454 return null;
Madan Jampani38b250d2014-10-17 11:02:38 -0700455 }
456
Madan Jampani54d34992015-03-06 17:27:52 -0800457 private FlowRuleEvent addOrUpdateFlowRuleInternal(StoredFlowEntry rule) {
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800458 // check if this new rule is an update to an existing entry
459 StoredFlowEntry stored = flowTable.getFlowEntry(rule);
460 if (stored != null) {
461 stored.setBytes(rule.bytes());
462 stored.setLife(rule.life());
463 stored.setPackets(rule.packets());
464 if (stored.state() == FlowEntryState.PENDING_ADD) {
465 stored.setState(FlowEntryState.ADDED);
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800466 return new FlowRuleEvent(Type.RULE_ADDED, rule);
467 }
468 return new FlowRuleEvent(Type.RULE_UPDATED, rule);
Yuta HIGUCHI885868f2014-11-13 19:12:07 -0800469 }
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800470
471 // TODO: Confirm if this behavior is correct. See SimpleFlowRuleStore
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800472 flowTable.add(rule);
473
alshabib1c319ff2014-10-04 20:29:09 -0700474 return null;
alshabib339a3d92014-09-26 17:54:32 -0700475 }
476
477 @Override
Madan Jampani38b250d2014-10-17 11:02:38 -0700478 public FlowRuleEvent removeFlowRule(FlowEntry rule) {
Yuta HIGUCHIb6eb9142014-11-26 16:18:13 -0800479 final DeviceId deviceId = rule.deviceId();
480 ReplicaInfo replicaInfo = replicaInfoManager.getReplicaInfoFor(deviceId);
Yuta HIGUCHI4b524442014-10-28 22:23:57 -0700481
482 final NodeId localId = clusterService.getLocalNode().id();
483 if (localId.equals(replicaInfo.master().orNull())) {
Madan Jampani38b250d2014-10-17 11:02:38 -0700484 // bypass and handle it locally
485 return removeFlowRuleInternal(rule);
486 }
487
Yuta HIGUCHIb6eb9142014-11-26 16:18:13 -0800488 if (!replicaInfo.master().isPresent()) {
Yuta HIGUCHI6b98ab62014-12-03 16:08:08 -0800489 log.warn("Failed to removeFlowRule: No master for {}", deviceId);
Yuta HIGUCHIb6eb9142014-11-26 16:18:13 -0800490 // TODO: revisit if this should be null (="no-op") or Exception
491 return null;
492 }
493
Yuta HIGUCHIdfa45c12014-11-24 18:38:53 -0800494 log.trace("Forwarding removeFlowRule to {}, which is the primary (master) for device {}",
Yuta HIGUCHIb6eb9142014-11-26 16:18:13 -0800495 replicaInfo.master().orNull(), deviceId);
Yuta HIGUCHIdfa45c12014-11-24 18:38:53 -0800496
497 ClusterMessage message = new ClusterMessage(
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800498 clusterService.getLocalNode().id(),
499 REMOVE_FLOW_ENTRY,
500 SERIALIZER.encode(rule));
Yuta HIGUCHIdfa45c12014-11-24 18:38:53 -0800501
502 try {
503 Future<byte[]> responseFuture = clusterCommunicator.sendAndReceive(message, replicaInfo.master().get());
504 return SERIALIZER.decode(responseFuture.get(FLOW_RULE_STORE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS));
505 } catch (IOException | TimeoutException | ExecutionException | InterruptedException e) {
Yuta HIGUCHI65934892014-12-04 17:47:44 -0800506 // TODO: Retry against latest master or throw a FlowStoreException
Yuta HIGUCHIdfa45c12014-11-24 18:38:53 -0800507 throw new RuntimeException(e);
508 }
Madan Jampani38b250d2014-10-17 11:02:38 -0700509 }
510
Yuta HIGUCHI885868f2014-11-13 19:12:07 -0800511 private FlowRuleEvent removeFlowRuleInternal(FlowEntry rule) {
Yuta HIGUCHI92891d12014-10-27 20:04:38 -0700512 final DeviceId deviceId = rule.deviceId();
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800513 // This is where one could mark a rule as removed and still keep it in the store.
514 final boolean removed = flowTable.remove(deviceId, rule); //flowEntries.remove(deviceId, rule);
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800515 if (removed) {
516 return new FlowRuleEvent(RULE_REMOVED, rule);
517 } else {
518 return null;
alshabib339a3d92014-09-26 17:54:32 -0700519 }
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800520
alshabib339a3d92014-09-26 17:54:32 -0700521 }
Madan Jampani117aaae2014-10-23 10:04:05 -0700522
523 @Override
524 public void batchOperationComplete(FlowRuleBatchEvent event) {
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800525 //FIXME: need a per device pending response
526
527 NodeId nodeId = pendingResponses.remove(event.subject().batchId());
528 if (nodeId == null) {
529 notifyDelegate(event);
530 } else {
Brian O'Connor5eb77c82015-03-02 18:09:39 -0800531 ClusterMessage message = new ClusterMessage(
532 clusterService.getLocalNode().id(),
533 REMOTE_APPLY_COMPLETED,
534 SERIALIZER.encode(event));
535 // TODO check unicast return value
536 clusterCommunicator.unicast(message, nodeId);
537 //error log: log.warn("Failed to respond to peer for batch operation result");
Yuta HIGUCHI9def0472014-10-23 15:51:10 -0700538 }
Madan Jampani117aaae2014-10-23 10:04:05 -0700539 }
Yuta HIGUCHI92891d12014-10-27 20:04:38 -0700540
Yuta HIGUCHI885868f2014-11-13 19:12:07 -0800541 private void removeFromPrimary(final DeviceId did) {
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800542 flowTable.clearDevice(did);
Yuta HIGUCHI92891d12014-10-27 20:04:38 -0700543 }
544
Yuta HIGUCHIea150152014-10-28 22:55:14 -0700545 private final class OnStoreBatch implements ClusterMessageHandler {
546 private final NodeId local;
547
548 private OnStoreBatch(NodeId local) {
549 this.local = local;
550 }
551
552 @Override
553 public void handle(final ClusterMessage message) {
554 FlowRuleBatchOperation operation = SERIALIZER.decode(message.payload());
Yuta HIGUCHIfaf9e1c2014-11-20 00:31:29 -0800555 log.debug("received batch request {}", operation);
Yuta HIGUCHIea150152014-10-28 22:55:14 -0700556
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800557 final DeviceId deviceId = operation.deviceId();
Yuta HIGUCHIea150152014-10-28 22:55:14 -0700558 ReplicaInfo replicaInfo = replicaInfoManager.getReplicaInfoFor(deviceId);
559 if (!local.equals(replicaInfo.master().orNull())) {
560
Madan Jampani54d34992015-03-06 17:27:52 -0800561 Set<FlowRule> failures = operation.getOperations()
562 .stream()
563 .map(FlowRuleBatchEntry::target)
564 .collect(Collectors.toSet());
565
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800566 CompletedBatchOperation allFailed = new CompletedBatchOperation(false, failures, deviceId);
Yuta HIGUCHIea150152014-10-28 22:55:14 -0700567 // This node is no longer the master, respond as all failed.
568 // TODO: we might want to wrap response in envelope
569 // to distinguish sw programming failure and hand over
570 // it make sense in the latter case to retry immediately.
571 try {
572 message.respond(SERIALIZER.encode(allFailed));
573 } catch (IOException e) {
574 log.error("Failed to respond back", e);
575 }
576 return;
577 }
578
Yuta HIGUCHIea150152014-10-28 22:55:14 -0700579
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800580 pendingResponses.put(operation.id(), message.sender());
581 storeBatchInternal(operation);
Yuta HIGUCHIea150152014-10-28 22:55:14 -0700582
Yuta HIGUCHIea150152014-10-28 22:55:14 -0700583 }
584 }
585
Yuta HIGUCHI92891d12014-10-27 20:04:38 -0700586 private final class InternalReplicaInfoEventListener
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800587 implements ReplicaInfoEventListener {
Yuta HIGUCHI92891d12014-10-27 20:04:38 -0700588
589 @Override
590 public void event(ReplicaInfoEvent event) {
591 final NodeId local = clusterService.getLocalNode().id();
592 final DeviceId did = event.subject();
593 final ReplicaInfo rInfo = event.replicaInfo();
594
595 switch (event.type()) {
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800596 case MASTER_CHANGED:
597 if (local.equals(rInfo.master().orNull())) {
Madan Jampani54d34992015-03-06 17:27:52 -0800598 log.info("{} is now the master for {}. Will load flow rules from backup", local, did);
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800599 // This node is the new master, populate local structure
600 // from backup
Madan Jampani54d34992015-03-06 17:27:52 -0800601 flowTable.loadFromBackup(did);
alshabib93cb57f2015-02-12 17:43:26 -0800602 }
603 //else {
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800604 // This node is no longer the master holder,
605 // clean local structure
alshabib93cb57f2015-02-12 17:43:26 -0800606 //removeFromPrimary(did);
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800607 // TODO: probably should stop pending backup activities in
608 // executors to avoid overwriting with old value
alshabib93cb57f2015-02-12 17:43:26 -0800609 //}
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800610 break;
611 default:
612 break;
Yuta HIGUCHI92891d12014-10-27 20:04:38 -0700613
614 }
615 }
616 }
617
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800618 private class InternalFlowTable {
619
Madan Jampani54d34992015-03-06 17:27:52 -0800620 private final Map<DeviceId, Map<FlowId, Set<StoredFlowEntry>>>
621 flowEntries = Maps.newConcurrentMap();
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800622
Madan Jampani54d34992015-03-06 17:27:52 -0800623 private final KryoNamespace.Builder flowSerializer = KryoNamespace.newBuilder()
624 .register(KryoNamespaces.API)
625 .register(MastershipBasedTimestamp.class);
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800626
Madan Jampani54d34992015-03-06 17:27:52 -0800627 private final ClockService<FlowId, StoredFlowEntry> clockService =
628 new ClockService<FlowId, StoredFlowEntry>() {
629 @Override
630 public Timestamp getTimestamp(FlowId flowId, StoredFlowEntry flowEntry) {
631 if (flowEntry == null) {
632 return null;
633 }
634 return deviceClockService.getTimestamp(flowEntry.deviceId());
635 }
636 };
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800637
Madan Jampani54d34992015-03-06 17:27:52 -0800638 private final EventuallyConsistentMap<FlowId, StoredFlowEntry> backupMap =
639 new EventuallyConsistentMapImpl<>("flow-backup",
640 clusterService,
641 clusterCommunicator,
642 flowSerializer,
643 clockService,
644 (key, flowEntry) -> getPeerNodes());
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800645
Madan Jampani54d34992015-03-06 17:27:52 -0800646 private Collection<NodeId> getPeerNodes() {
647 List<NodeId> nodes = clusterService.getNodes()
648 .stream()
649 .map(node -> node.id())
650 .filter(id -> !id.equals(clusterService.getLocalNode().id()))
651 .collect(Collectors.toList());
652
653 if (nodes.isEmpty()) {
654 return ImmutableList.of();
655 } else {
656 Collections.shuffle(nodes);
657 return ImmutableList.of(nodes.get(0));
658 }
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800659 }
660
Madan Jampani54d34992015-03-06 17:27:52 -0800661 public void loadFromBackup(DeviceId deviceId) {
662 ConcurrentMap<FlowId, Set<StoredFlowEntry>> flowTable = new ConcurrentHashMap<>();
663
664 backupMap.values()
665 .stream()
666 .filter(entry -> entry.deviceId().equals(deviceId))
667 .forEach(entry -> flowTable.computeIfPresent(entry.id(), (k, v) -> {
668 if (v == null) {
669 return Sets.newHashSet(entry);
670 } else {
671 v.add(entry);
672 }
673 return v;
674 }));
675 flowEntries.putIfAbsent(deviceId, flowTable);
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800676 }
677
678 private Set<StoredFlowEntry> getFlowEntriesInternal(DeviceId deviceId, FlowId flowId) {
Madan Jampani54d34992015-03-06 17:27:52 -0800679 return flowEntries
680 .computeIfAbsent(deviceId, key -> Maps.newConcurrentMap())
681 .computeIfAbsent(flowId, k -> new CopyOnWriteArraySet<>());
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800682 }
683
684 private StoredFlowEntry getFlowEntryInternal(FlowRule rule) {
Madan Jampani54d34992015-03-06 17:27:52 -0800685 return getFlowEntriesInternal(rule.deviceId(), rule.id())
686 .stream()
687 .filter(element -> element.equals(rule))
688 .findFirst()
689 .orElse(null);
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800690 }
691
Madan Jampani54d34992015-03-06 17:27:52 -0800692 private Set<StoredFlowEntry> getFlowEntriesInternal(DeviceId deviceId) {
693 Set<StoredFlowEntry> entries = Sets.newHashSet();
694 flowEntries.computeIfAbsent(deviceId, key -> Maps.newConcurrentMap())
695 .values()
696 .forEach(entries::addAll);
697 return entries;
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800698 }
699
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800700 public StoredFlowEntry getFlowEntry(FlowRule rule) {
701 return getFlowEntryInternal(rule);
702 }
703
Madan Jampani54d34992015-03-06 17:27:52 -0800704 public Set<StoredFlowEntry> getFlowEntries(DeviceId deviceId) {
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800705 return getFlowEntriesInternal(deviceId);
706 }
707
Madan Jampani54d34992015-03-06 17:27:52 -0800708 public void add(StoredFlowEntry rule) {
709 getFlowEntriesInternal(rule.deviceId(), rule.id()).add(rule);
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800710
Madan Jampani54d34992015-03-06 17:27:52 -0800711 try {
712 backupMap.put(rule.id(), rule);
713 } catch (Exception e) {
714 log.warn("Failed to backup flow rule", e);
715 }
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800716 }
717
718 public boolean remove(DeviceId deviceId, FlowEntry rule) {
Madan Jampani54d34992015-03-06 17:27:52 -0800719 boolean status =
720 getFlowEntriesInternal(deviceId, rule.id()).remove(rule);
721 if (status) {
722 try {
723 backupMap.remove(rule.id(), (DefaultFlowEntry) rule);
724 } catch (Exception e) {
725 log.warn("Failed to remove backup of flow rule", e);
726 }
727 }
728 return status;
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800729 }
730
731 public void clearDevice(DeviceId did) {
732 flowEntries.remove(did);
Madan Jampani54d34992015-03-06 17:27:52 -0800733 // Flow entries should continue to remain in backup map.
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800734 }
735 }
Madan Jampani54d34992015-03-06 17:27:52 -0800736}