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
Showing
5 changed files
with
72 additions
and
18 deletions
... | @@ -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 | } | ... | ... |
-
Please register or login to post a comment