Jonathan Hart

Fix for flow equality bug which can cause:

 * multiple flows with the same match to be simultaneously present in the flow
   store, and
 * similar but different rule in switch and store, resulting in flows stuck in
   PENDING_ADD state.

Fixes ONOS-2426.

Ported from onos-1.2 branch.

Change-Id: I4b4e444c3a6dba7e4d3278e9469069e2dbdb9b67
...@@ -249,6 +249,13 @@ public class DefaultFlowRule implements FlowRule { ...@@ -249,6 +249,13 @@ public class DefaultFlowRule implements FlowRule {
249 } 249 }
250 250
251 @Override 251 @Override
252 + public boolean exactMatch(FlowRule rule) {
253 + return this.equals(rule) &&
254 + Objects.equals(this.id, rule.id()) &&
255 + Objects.equals(this.treatment, rule.treatment());
256 + }
257 +
258 + @Override
252 public String toString() { 259 public String toString() {
253 return toStringHelper(this) 260 return toStringHelper(this)
254 .add("id", Long.toHexString(id.value())) 261 .add("id", Long.toHexString(id.value()))
...@@ -370,7 +377,7 @@ public class DefaultFlowRule implements FlowRule { ...@@ -370,7 +377,7 @@ public class DefaultFlowRule implements FlowRule {
370 } 377 }
371 378
372 private int hash() { 379 private int hash() {
373 - return Objects.hash(deviceId, selector, treatment, tableId); 380 + return Objects.hash(deviceId, priority, selector, tableId);
374 } 381 }
375 382
376 } 383 }
......
...@@ -138,6 +138,34 @@ public interface FlowRule { ...@@ -138,6 +138,34 @@ public interface FlowRule {
138 int tableId(); 138 int tableId();
139 139
140 /** 140 /**
141 + * {@inheritDoc}
142 + *
143 + * Equality for flow rules only considers 'match equality'. This means that
144 + * two flow rules with the same match conditions will be equal, regardless
145 + * of the treatment or other characteristics of the flow.
146 + *
147 + * @param obj the reference object with which to compare.
148 + * @return {@code true} if this object is the same as the obj
149 + * argument; {@code false} otherwise.
150 + */
151 + boolean equals(Object obj);
152 +
153 + /**
154 + * Returns whether this flow rule is an exact match to the flow rule given
155 + * in the argument.
156 + * <p>
157 + * Exact match means that deviceId, priority, selector,
158 + * tableId, flowId and treatment are equal. Note that this differs from
159 + * the notion of object equality for flow rules, which does not consider the
160 + * flowId or treatment when testing equality.
161 + * </p>
162 + *
163 + * @param rule other rule to match against
164 + * @return true if the rules are an exact match, otherwise false
165 + */
166 + boolean exactMatch(FlowRule rule);
167 +
168 + /**
141 * A flowrule builder. 169 * A flowrule builder.
142 */ 170 */
143 interface Builder { 171 interface Builder {
......
...@@ -423,6 +423,11 @@ public class IntentTestsMocks { ...@@ -423,6 +423,11 @@ public class IntentTestsMocks {
423 } 423 }
424 424
425 @Override 425 @Override
426 + public boolean exactMatch(FlowRule rule) {
427 + return this.equals(rule);
428 + }
429 +
430 + @Override
426 public int tableId() { 431 public int tableId() {
427 return tableId; 432 return tableId;
428 } 433 }
......
...@@ -387,13 +387,23 @@ public class FlowRuleManager ...@@ -387,13 +387,23 @@ public class FlowRuleManager
387 387
388 @Override 388 @Override
389 public void pushFlowMetrics(DeviceId deviceId, Iterable<FlowEntry> flowEntries) { 389 public void pushFlowMetrics(DeviceId deviceId, Iterable<FlowEntry> flowEntries) {
390 - Set<FlowEntry> storedRules = Sets.newHashSet(store.getFlowEntries(deviceId)); 390 + Map<FlowEntry, FlowEntry> storedRules = Maps.newHashMap();
391 + store.getFlowEntries(deviceId).forEach(f -> storedRules.put(f, f));
392 +
391 for (FlowEntry rule : flowEntries) { 393 for (FlowEntry rule : flowEntries) {
392 try { 394 try {
393 - if (storedRules.remove(rule)) { 395 + FlowEntry storedRule = storedRules.remove(rule);
396 + if (storedRule != null) {
397 + if (storedRule.exactMatch(rule)) {
394 // we both have the rule, let's update some info then. 398 // we both have the rule, let's update some info then.
395 flowAdded(rule); 399 flowAdded(rule);
396 } else { 400 } else {
401 + // the two rules are not an exact match - remove the
402 + // switch's rule and install our rule
403 + extraneousFlow(rule);
404 + flowMissing(storedRule);
405 + }
406 + } else {
397 // the device has a rule the store does not have 407 // the device has a rule the store does not have
398 if (!allowExtraneousRules) { 408 if (!allowExtraneousRules) {
399 extraneousFlow(rule); 409 extraneousFlow(rule);
...@@ -404,7 +414,7 @@ public class FlowRuleManager ...@@ -404,7 +414,7 @@ public class FlowRuleManager
404 continue; 414 continue;
405 } 415 }
406 } 416 }
407 - for (FlowEntry rule : storedRules) { 417 + for (FlowEntry rule : storedRules.keySet()) {
408 try { 418 try {
409 // there are rules in the store that aren't on the switch 419 // there are rules in the store that aren't on the switch
410 log.debug("Adding rule in store, but not on switch {}", rule); 420 log.debug("Adding rule in store, but not on switch {}", rule);
......
...@@ -15,14 +15,12 @@ ...@@ -15,14 +15,12 @@
15 */ 15 */
16 package org.onosproject.rest; 16 package org.onosproject.rest;
17 17
18 -import java.io.InputStream; 18 +import com.eclipsesource.json.JsonArray;
19 -import java.net.HttpURLConnection; 19 +import com.eclipsesource.json.JsonObject;
20 -import java.util.HashMap; 20 +import com.google.common.collect.ImmutableSet;
21 -import java.util.HashSet; 21 +import com.sun.jersey.api.client.ClientResponse;
22 -import java.util.Set; 22 +import com.sun.jersey.api.client.UniformInterfaceException;
23 - 23 +import com.sun.jersey.api.client.WebResource;
24 -import javax.ws.rs.core.MediaType;
25 -
26 import org.hamcrest.Description; 24 import org.hamcrest.Description;
27 import org.hamcrest.Matchers; 25 import org.hamcrest.Matchers;
28 import org.hamcrest.TypeSafeMatcher; 26 import org.hamcrest.TypeSafeMatcher;
...@@ -49,6 +47,7 @@ import org.onosproject.net.flow.DefaultTrafficSelector; ...@@ -49,6 +47,7 @@ import org.onosproject.net.flow.DefaultTrafficSelector;
49 import org.onosproject.net.flow.DefaultTrafficTreatment; 47 import org.onosproject.net.flow.DefaultTrafficTreatment;
50 import org.onosproject.net.flow.FlowEntry; 48 import org.onosproject.net.flow.FlowEntry;
51 import org.onosproject.net.flow.FlowId; 49 import org.onosproject.net.flow.FlowId;
50 +import org.onosproject.net.flow.FlowRule;
52 import org.onosproject.net.flow.FlowRuleExtPayLoad; 51 import org.onosproject.net.flow.FlowRuleExtPayLoad;
53 import org.onosproject.net.flow.FlowRuleService; 52 import org.onosproject.net.flow.FlowRuleService;
54 import org.onosproject.net.flow.TrafficSelector; 53 import org.onosproject.net.flow.TrafficSelector;
...@@ -57,12 +56,12 @@ import org.onosproject.net.flow.criteria.Criterion; ...@@ -57,12 +56,12 @@ import org.onosproject.net.flow.criteria.Criterion;
57 import org.onosproject.net.flow.instructions.Instruction; 56 import org.onosproject.net.flow.instructions.Instruction;
58 import org.onosproject.net.flow.instructions.Instructions; 57 import org.onosproject.net.flow.instructions.Instructions;
59 58
60 -import com.eclipsesource.json.JsonArray; 59 +import javax.ws.rs.core.MediaType;
61 -import com.eclipsesource.json.JsonObject; 60 +import java.io.InputStream;
62 -import com.google.common.collect.ImmutableSet; 61 +import java.net.HttpURLConnection;
63 -import com.sun.jersey.api.client.ClientResponse; 62 +import java.util.HashMap;
64 -import com.sun.jersey.api.client.UniformInterfaceException; 63 +import java.util.HashSet;
65 -import com.sun.jersey.api.client.WebResource; 64 +import java.util.Set;
66 65
67 import static org.easymock.EasyMock.anyObject; 66 import static org.easymock.EasyMock.anyObject;
68 import static org.easymock.EasyMock.anyShort; 67 import static org.easymock.EasyMock.anyShort;
...@@ -209,6 +208,11 @@ public class FlowsResourceTest extends ResourceTest { ...@@ -209,6 +208,11 @@ public class FlowsResourceTest extends ResourceTest {
209 } 208 }
210 209
211 @Override 210 @Override
211 + public boolean exactMatch(FlowRule rule) {
212 + return false;
213 + }
214 +
215 + @Override
212 public FlowRuleExtPayLoad payLoad() { 216 public FlowRuleExtPayLoad payLoad() {
213 return null; 217 return null;
214 } 218 }
......