Committed by
Gerrit Code Review
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. Change-Id: I4b4e444c3a6dba7e4d3278e9469069e2dbdb9b67
Showing
5 changed files
with
70 additions
and
15 deletions
... | @@ -362,6 +362,13 @@ public class DefaultFlowRule implements FlowRule { | ... | @@ -362,6 +362,13 @@ public class DefaultFlowRule implements FlowRule { |
362 | } | 362 | } |
363 | 363 | ||
364 | @Override | 364 | @Override |
365 | + public boolean exactMatch(FlowRule rule) { | ||
366 | + return this.equals(rule) && | ||
367 | + Objects.equals(this.id, rule.id()) && | ||
368 | + Objects.equals(this.treatment, rule.treatment()); | ||
369 | + } | ||
370 | + | ||
371 | + @Override | ||
365 | public String toString() { | 372 | public String toString() { |
366 | return toStringHelper(this) | 373 | return toStringHelper(this) |
367 | .add("id", Long.toHexString(id.value())) | 374 | .add("id", Long.toHexString(id.value())) |
... | @@ -483,7 +490,7 @@ public class DefaultFlowRule implements FlowRule { | ... | @@ -483,7 +490,7 @@ public class DefaultFlowRule implements FlowRule { |
483 | } | 490 | } |
484 | 491 | ||
485 | private int hash() { | 492 | private int hash() { |
486 | - return Objects.hash(deviceId, selector, treatment, tableId); | 493 | + return Objects.hash(deviceId, priority, selector, tableId); |
487 | } | 494 | } |
488 | 495 | ||
489 | } | 496 | } | ... | ... |
... | @@ -136,6 +136,34 @@ public interface FlowRule { | ... | @@ -136,6 +136,34 @@ public interface FlowRule { |
136 | int tableId(); | 136 | int tableId(); |
137 | 137 | ||
138 | /** | 138 | /** |
139 | + * {@inheritDoc} | ||
140 | + * | ||
141 | + * Equality for flow rules only considers 'match equality'. This means that | ||
142 | + * two flow rules with the same match conditions will be equal, regardless | ||
143 | + * of the treatment or other characteristics of the flow. | ||
144 | + * | ||
145 | + * @param obj the reference object with which to compare. | ||
146 | + * @return {@code true} if this object is the same as the obj | ||
147 | + * argument; {@code false} otherwise. | ||
148 | + */ | ||
149 | + boolean equals(Object obj); | ||
150 | + | ||
151 | + /** | ||
152 | + * Returns whether this flow rule is an exact match to the flow rule given | ||
153 | + * in the argument. | ||
154 | + * <p> | ||
155 | + * Exact match means that deviceId, priority, selector, | ||
156 | + * tableId, flowId and treatment are equal. Note that this differs from | ||
157 | + * the notion of object equality for flow rules, which does not consider the | ||
158 | + * flowId or treatment when testing equality. | ||
159 | + * </p> | ||
160 | + * | ||
161 | + * @param rule other rule to match against | ||
162 | + * @return true if the rules are an exact match, otherwise false | ||
163 | + */ | ||
164 | + boolean exactMatch(FlowRule rule); | ||
165 | + | ||
166 | + /** | ||
139 | * A flowrule builder. | 167 | * A flowrule builder. |
140 | */ | 168 | */ |
141 | interface Builder { | 169 | interface Builder { | ... | ... |
... | @@ -426,6 +426,11 @@ public class IntentTestsMocks { | ... | @@ -426,6 +426,11 @@ public class IntentTestsMocks { |
426 | } | 426 | } |
427 | 427 | ||
428 | @Override | 428 | @Override |
429 | + public boolean exactMatch(FlowRule rule) { | ||
430 | + return this.equals(rule); | ||
431 | + } | ||
432 | + | ||
433 | + @Override | ||
429 | public int tableId() { | 434 | public int tableId() { |
430 | return tableId; | 435 | return tableId; |
431 | } | 436 | } | ... | ... |
... | @@ -417,12 +417,22 @@ public class FlowRuleManager | ... | @@ -417,12 +417,22 @@ public class FlowRuleManager |
417 | 417 | ||
418 | @Override | 418 | @Override |
419 | public void pushFlowMetrics(DeviceId deviceId, Iterable<FlowEntry> flowEntries) { | 419 | public void pushFlowMetrics(DeviceId deviceId, Iterable<FlowEntry> flowEntries) { |
420 | - Set<FlowEntry> storedRules = Sets.newHashSet(store.getFlowEntries(deviceId)); | 420 | + Map<FlowEntry, FlowEntry> storedRules = Maps.newHashMap(); |
421 | + store.getFlowEntries(deviceId).forEach(f -> storedRules.put(f, f)); | ||
422 | + | ||
421 | for (FlowEntry rule : flowEntries) { | 423 | for (FlowEntry rule : flowEntries) { |
422 | try { | 424 | try { |
423 | - if (storedRules.remove(rule)) { | 425 | + FlowEntry storedRule = storedRules.remove(rule); |
424 | - // we both have the rule, let's update some info then. | 426 | + if (storedRule != null) { |
425 | - flowAdded(rule); | 427 | + if (storedRule.exactMatch(rule)) { |
428 | + // we both have the rule, let's update some info then. | ||
429 | + flowAdded(rule); | ||
430 | + } else { | ||
431 | + // the two rules are not an exact match - remove the | ||
432 | + // switch's rule and install our rule | ||
433 | + extraneousFlow(rule); | ||
434 | + flowMissing(storedRule); | ||
435 | + } | ||
426 | } else { | 436 | } else { |
427 | // the device has a rule the store does not have | 437 | // the device has a rule the store does not have |
428 | if (!allowExtraneousRules) { | 438 | if (!allowExtraneousRules) { |
... | @@ -434,7 +444,7 @@ public class FlowRuleManager | ... | @@ -434,7 +444,7 @@ public class FlowRuleManager |
434 | continue; | 444 | continue; |
435 | } | 445 | } |
436 | } | 446 | } |
437 | - for (FlowEntry rule : storedRules) { | 447 | + for (FlowEntry rule : storedRules.keySet()) { |
438 | try { | 448 | try { |
439 | // there are rules in the store that aren't on the switch | 449 | // there are rules in the store that aren't on the switch |
440 | log.debug("Adding rule in store, but not on switch {}", rule); | 450 | log.debug("Adding rule in store, but not on switch {}", rule); | ... | ... |
... | @@ -15,10 +15,11 @@ | ... | @@ -15,10 +15,11 @@ |
15 | */ | 15 | */ |
16 | package org.onosproject.rest; | 16 | package org.onosproject.rest; |
17 | 17 | ||
18 | -import java.util.HashMap; | 18 | +import com.eclipsesource.json.JsonArray; |
19 | -import java.util.HashSet; | 19 | +import com.eclipsesource.json.JsonObject; |
20 | -import java.util.Set; | 20 | +import com.google.common.collect.ImmutableSet; |
21 | - | 21 | +import com.sun.jersey.api.client.UniformInterfaceException; |
22 | +import com.sun.jersey.api.client.WebResource; | ||
22 | import org.hamcrest.Description; | 23 | import org.hamcrest.Description; |
23 | import org.hamcrest.TypeSafeMatcher; | 24 | import org.hamcrest.TypeSafeMatcher; |
24 | import org.junit.After; | 25 | import org.junit.After; |
... | @@ -40,6 +41,7 @@ import org.onosproject.net.flow.DefaultTrafficSelector; | ... | @@ -40,6 +41,7 @@ import org.onosproject.net.flow.DefaultTrafficSelector; |
40 | import org.onosproject.net.flow.DefaultTrafficTreatment; | 41 | import org.onosproject.net.flow.DefaultTrafficTreatment; |
41 | import org.onosproject.net.flow.FlowEntry; | 42 | import org.onosproject.net.flow.FlowEntry; |
42 | import org.onosproject.net.flow.FlowId; | 43 | import org.onosproject.net.flow.FlowId; |
44 | +import org.onosproject.net.flow.FlowRule; | ||
43 | import org.onosproject.net.flow.FlowRuleExtPayLoad; | 45 | import org.onosproject.net.flow.FlowRuleExtPayLoad; |
44 | import org.onosproject.net.flow.FlowRuleService; | 46 | import org.onosproject.net.flow.FlowRuleService; |
45 | import org.onosproject.net.flow.TrafficSelector; | 47 | import org.onosproject.net.flow.TrafficSelector; |
... | @@ -48,11 +50,9 @@ import org.onosproject.net.flow.criteria.Criterion; | ... | @@ -48,11 +50,9 @@ import org.onosproject.net.flow.criteria.Criterion; |
48 | import org.onosproject.net.flow.instructions.Instruction; | 50 | import org.onosproject.net.flow.instructions.Instruction; |
49 | import org.onosproject.net.flow.instructions.Instructions; | 51 | import org.onosproject.net.flow.instructions.Instructions; |
50 | 52 | ||
51 | -import com.eclipsesource.json.JsonArray; | 53 | +import java.util.HashMap; |
52 | -import com.eclipsesource.json.JsonObject; | 54 | +import java.util.HashSet; |
53 | -import com.google.common.collect.ImmutableSet; | 55 | +import java.util.Set; |
54 | -import com.sun.jersey.api.client.UniformInterfaceException; | ||
55 | -import com.sun.jersey.api.client.WebResource; | ||
56 | 56 | ||
57 | import static org.easymock.EasyMock.anyObject; | 57 | import static org.easymock.EasyMock.anyObject; |
58 | import static org.easymock.EasyMock.createMock; | 58 | import static org.easymock.EasyMock.createMock; |
... | @@ -194,6 +194,11 @@ public class FlowsResourceTest extends ResourceTest { | ... | @@ -194,6 +194,11 @@ public class FlowsResourceTest extends ResourceTest { |
194 | } | 194 | } |
195 | 195 | ||
196 | @Override | 196 | @Override |
197 | + public boolean exactMatch(FlowRule rule) { | ||
198 | + return false; | ||
199 | + } | ||
200 | + | ||
201 | + @Override | ||
197 | public FlowRuleExtPayLoad payLoad() { | 202 | public FlowRuleExtPayLoad payLoad() { |
198 | return null; | 203 | return null; |
199 | } | 204 | } | ... | ... |
-
Please register or login to post a comment