Committed by
Yuta HIGUCHI
Fix ONOS-4570
Changes: - Adds RandomVLAN behavior as described in ONOS-4570; - Adds a unit test for RandomVLAN; - Fixes the VLAN rewriting; - Updates the unit tests relative to VLAN encapsulation; Change-Id: I52ab2f40a30f3be617606b2b0bb7a89d48414138
Showing
3 changed files
with
147 additions
and
10 deletions
| ... | @@ -16,7 +16,9 @@ | ... | @@ -16,7 +16,9 @@ |
| 16 | package org.onosproject.net.intent.impl.compiler; | 16 | package org.onosproject.net.intent.impl.compiler; |
| 17 | 17 | ||
| 18 | import com.google.common.collect.ImmutableList; | 18 | import com.google.common.collect.ImmutableList; |
| 19 | +import com.google.common.collect.Iterables; | ||
| 19 | import com.google.common.collect.Sets; | 20 | import com.google.common.collect.Sets; |
| 21 | +import org.apache.commons.lang.math.RandomUtils; | ||
| 20 | import org.onlab.packet.EthType; | 22 | import org.onlab.packet.EthType; |
| 21 | import org.onlab.packet.Ethernet; | 23 | import org.onlab.packet.Ethernet; |
| 22 | import org.onlab.packet.MplsLabel; | 24 | import org.onlab.packet.MplsLabel; |
| ... | @@ -62,6 +64,8 @@ import static org.onosproject.net.LinkKey.linkKey; | ... | @@ -62,6 +64,8 @@ import static org.onosproject.net.LinkKey.linkKey; |
| 62 | 64 | ||
| 63 | public class PathCompiler<T> { | 65 | public class PathCompiler<T> { |
| 64 | 66 | ||
| 67 | + public static final boolean RANDOM_SELECTION = true; | ||
| 68 | + | ||
| 65 | /** | 69 | /** |
| 66 | * Defines methods used to create objects representing flows. | 70 | * Defines methods used to create objects representing flows. |
| 67 | */ | 71 | */ |
| ... | @@ -120,6 +124,44 @@ public class PathCompiler<T> { | ... | @@ -120,6 +124,44 @@ public class PathCompiler<T> { |
| 120 | return vlanIds; | 124 | return vlanIds; |
| 121 | } | 125 | } |
| 122 | 126 | ||
| 127 | + /** | ||
| 128 | + * Implements the first fit selection behavior. | ||
| 129 | + * | ||
| 130 | + * @param available the set of available VLAN ids. | ||
| 131 | + * @return the chosen VLAN id. | ||
| 132 | + */ | ||
| 133 | + private VlanId firsFitSelection(Set<VlanId> available) { | ||
| 134 | + if (!available.isEmpty()) { | ||
| 135 | + return available.iterator().next(); | ||
| 136 | + } | ||
| 137 | + return VlanId.vlanId(VlanId.NO_VID); | ||
| 138 | + } | ||
| 139 | + | ||
| 140 | + /** | ||
| 141 | + * Implements the random selection behavior. | ||
| 142 | + * | ||
| 143 | + * @param available the set of available VLAN ids. | ||
| 144 | + * @return the chosen VLAN id. | ||
| 145 | + */ | ||
| 146 | + private VlanId randomSelection(Set<VlanId> available) { | ||
| 147 | + if (!available.isEmpty()) { | ||
| 148 | + int size = available.size(); | ||
| 149 | + int index = RandomUtils.nextInt(size); | ||
| 150 | + return Iterables.get(available, index); | ||
| 151 | + } | ||
| 152 | + return VlanId.vlanId(VlanId.NO_VID); | ||
| 153 | + } | ||
| 154 | + | ||
| 155 | + /** | ||
| 156 | + * Select a VLAN id from the set of available VLAN ids. | ||
| 157 | + * | ||
| 158 | + * @param available the set of available VLAN ids. | ||
| 159 | + * @return the chosen VLAN id. | ||
| 160 | + */ | ||
| 161 | + private VlanId selectVlanId(Set<VlanId> available) { | ||
| 162 | + return RANDOM_SELECTION ? randomSelection(available) : firsFitSelection(available); | ||
| 163 | + } | ||
| 164 | + | ||
| 123 | private Map<LinkKey, VlanId> findVlanIds(PathCompilerCreateFlow creator, Set<LinkKey> links) { | 165 | private Map<LinkKey, VlanId> findVlanIds(PathCompilerCreateFlow creator, Set<LinkKey> links) { |
| 124 | Map<LinkKey, VlanId> vlanIds = new HashMap<>(); | 166 | Map<LinkKey, VlanId> vlanIds = new HashMap<>(); |
| 125 | for (LinkKey link : links) { | 167 | for (LinkKey link : links) { |
| ... | @@ -129,7 +171,11 @@ public class PathCompiler<T> { | ... | @@ -129,7 +171,11 @@ public class PathCompiler<T> { |
| 129 | if (common.isEmpty()) { | 171 | if (common.isEmpty()) { |
| 130 | continue; | 172 | continue; |
| 131 | } | 173 | } |
| 132 | - vlanIds.put(link, common.iterator().next()); | 174 | + VlanId selected = selectVlanId(common); |
| 175 | + if (selected.toShort() == VlanId.NO_VID) { | ||
| 176 | + continue; | ||
| 177 | + } | ||
| 178 | + vlanIds.put(link, selected); | ||
| 133 | } | 179 | } |
| 134 | return vlanIds; | 180 | return vlanIds; |
| 135 | } | 181 | } |
| ... | @@ -185,7 +231,6 @@ public class PathCompiler<T> { | ... | @@ -185,7 +231,6 @@ public class PathCompiler<T> { |
| 185 | if (egressVlanId == null) { | 231 | if (egressVlanId == null) { |
| 186 | throw new IntentCompilationException("No available VLAN ID for " + link); | 232 | throw new IntentCompilationException("No available VLAN ID for " + link); |
| 187 | } | 233 | } |
| 188 | - prevVlanId = egressVlanId; | ||
| 189 | 234 | ||
| 190 | TrafficSelector transitSelector = DefaultTrafficSelector.builder() | 235 | TrafficSelector transitSelector = DefaultTrafficSelector.builder() |
| 191 | .matchInPort(prev.port()) | 236 | .matchInPort(prev.port()) |
| ... | @@ -200,6 +245,11 @@ public class PathCompiler<T> { | ... | @@ -200,6 +245,11 @@ public class PathCompiler<T> { |
| 200 | creator.createFlow(transitSelector, | 245 | creator.createFlow(transitSelector, |
| 201 | transitTreat.build(), prev, link.src(), | 246 | transitTreat.build(), prev, link.src(), |
| 202 | intent.priority(), true, flows, devices); | 247 | intent.priority(), true, flows, devices); |
| 248 | + /* For the next hop we have to remember | ||
| 249 | + * the previous egress VLAN id and the egress | ||
| 250 | + * node | ||
| 251 | + */ | ||
| 252 | + prevVlanId = egressVlanId; | ||
| 203 | prev = link.dst(); | 253 | prev = link.dst(); |
| 204 | } else { | 254 | } else { |
| 205 | // Egress traffic | 255 | // Egress traffic | ... | ... |
| ... | @@ -106,10 +106,25 @@ class MockResourceService implements ResourceService { | ... | @@ -106,10 +106,25 @@ class MockResourceService implements ResourceService { |
| 106 | .collect(Collectors.toList()); | 106 | .collect(Collectors.toList()); |
| 107 | } | 107 | } |
| 108 | 108 | ||
| 109 | + | ||
| 110 | + /** | ||
| 111 | + * It adds a number of VLAN ids in order to test the random behavior. | ||
| 112 | + * | ||
| 113 | + * @param parent the parent resource | ||
| 114 | + * @return a set of VLAN ids | ||
| 115 | + */ | ||
| 116 | + private Collection<Resource> addVlanIds(DiscreteResourceId parent) { | ||
| 117 | + Collection<Resource> resources = new HashSet<>(); | ||
| 118 | + for (int i = VlanId.NO_VID + 1; i < VlanId.MAX_VLAN; i++) { | ||
| 119 | + resources.add(Resources.discrete(parent).resource().child(VlanId.vlanId((short) i))); | ||
| 120 | + } | ||
| 121 | + return resources; | ||
| 122 | + } | ||
| 123 | + | ||
| 109 | @Override | 124 | @Override |
| 110 | public Set<Resource> getAvailableResources(DiscreteResourceId parent) { | 125 | public Set<Resource> getAvailableResources(DiscreteResourceId parent) { |
| 111 | Collection<Resource> resources = new HashSet<>(); | 126 | Collection<Resource> resources = new HashSet<>(); |
| 112 | - resources.add(Resources.discrete(parent).resource().child(VlanId.vlanId((short) 10))); | 127 | + resources.addAll(addVlanIds(parent)); |
| 113 | resources.add(Resources.discrete(parent).resource().child(MplsLabel.mplsLabel(10))); | 128 | resources.add(Resources.discrete(parent).resource().child(MplsLabel.mplsLabel(10))); |
| 114 | resources.add(Resources.discrete(parent).resource().child(TributarySlot.of(1))); | 129 | resources.add(Resources.discrete(parent).resource().child(TributarySlot.of(1))); |
| 115 | resources.add(Resources.discrete(parent).resource().child(TributarySlot.of(2))); | 130 | resources.add(Resources.discrete(parent).resource().child(TributarySlot.of(2))); | ... | ... |
| ... | @@ -61,7 +61,10 @@ import static org.easymock.EasyMock.replay; | ... | @@ -61,7 +61,10 @@ import static org.easymock.EasyMock.replay; |
| 61 | import static org.hamcrest.MatcherAssert.assertThat; | 61 | import static org.hamcrest.MatcherAssert.assertThat; |
| 62 | import static org.hamcrest.Matchers.hasSize; | 62 | import static org.hamcrest.Matchers.hasSize; |
| 63 | import static org.hamcrest.Matchers.is; | 63 | import static org.hamcrest.Matchers.is; |
| 64 | +import static org.hamcrest.Matchers.lessThan; | ||
| 64 | import static org.hamcrest.number.OrderingComparison.greaterThan; | 65 | import static org.hamcrest.number.OrderingComparison.greaterThan; |
| 66 | +import static org.junit.Assert.assertNotEquals; | ||
| 67 | +import static org.junit.Assert.assertTrue; | ||
| 65 | import static org.onosproject.net.DefaultEdgeLink.createEdgeLink; | 68 | import static org.onosproject.net.DefaultEdgeLink.createEdgeLink; |
| 66 | import static org.onosproject.net.Link.Type.DIRECT; | 69 | import static org.onosproject.net.Link.Type.DIRECT; |
| 67 | import static org.onosproject.net.NetTestTools.APP_ID; | 70 | import static org.onosproject.net.NetTestTools.APP_ID; |
| ... | @@ -257,7 +260,7 @@ public class PathIntentCompilerTest { | ... | @@ -257,7 +260,7 @@ public class PathIntentCompilerTest { |
| 257 | .get(); | 260 | .get(); |
| 258 | verifyIdAndPriority(rule2, d2p0.deviceId()); | 261 | verifyIdAndPriority(rule2, d2p0.deviceId()); |
| 259 | verifyVlanEncapSelector(rule2.selector(), d2p0, vlanToEncap); | 262 | verifyVlanEncapSelector(rule2.selector(), d2p0, vlanToEncap); |
| 260 | - verifyVlanEncapTreatment(rule2.treatment(), d2p1, false, false); | 263 | + vlanToEncap = verifyVlanEncapTreatment(rule2.treatment(), d2p1, false, false); |
| 261 | 264 | ||
| 262 | FlowRule rule3 = rules.stream() | 265 | FlowRule rule3 = rules.stream() |
| 263 | .filter(x -> x.deviceId().equals(d3p0.deviceId())) | 266 | .filter(x -> x.deviceId().equals(d3p0.deviceId())) |
| ... | @@ -300,7 +303,7 @@ public class PathIntentCompilerTest { | ... | @@ -300,7 +303,7 @@ public class PathIntentCompilerTest { |
| 300 | .get(); | 303 | .get(); |
| 301 | verifyIdAndPriority(rule2, d2p0.deviceId()); | 304 | verifyIdAndPriority(rule2, d2p0.deviceId()); |
| 302 | verifyVlanEncapSelector(rule2.selector(), d2p0, vlanToEncap); | 305 | verifyVlanEncapSelector(rule2.selector(), d2p0, vlanToEncap); |
| 303 | - verifyVlanEncapTreatment(rule2.treatment(), d2p1, false, false); | 306 | + vlanToEncap = verifyVlanEncapTreatment(rule2.treatment(), d2p1, false, false); |
| 304 | 307 | ||
| 305 | FlowRule rule3 = rules.stream() | 308 | FlowRule rule3 = rules.stream() |
| 306 | .filter(x -> x.deviceId().equals(d3p0.deviceId())) | 309 | .filter(x -> x.deviceId().equals(d3p0.deviceId())) |
| ... | @@ -323,6 +326,66 @@ public class PathIntentCompilerTest { | ... | @@ -323,6 +326,66 @@ public class PathIntentCompilerTest { |
| 323 | sut.deactivate(); | 326 | sut.deactivate(); |
| 324 | } | 327 | } |
| 325 | 328 | ||
| 329 | + /** | ||
| 330 | + * Tests the random selection of VlanIds in the PathCompiler. | ||
| 331 | + * It can fail randomly (it is unlikely) | ||
| 332 | + */ | ||
| 333 | + @Test | ||
| 334 | + public void testRandomVlanSelection() { | ||
| 335 | + | ||
| 336 | + if (PathCompiler.RANDOM_SELECTION) { | ||
| 337 | + | ||
| 338 | + sut.activate(); | ||
| 339 | + | ||
| 340 | + List<Intent> compiled = sut.compile(constraintVlanIntent, Collections.emptyList()); | ||
| 341 | + assertThat(compiled, hasSize(1)); | ||
| 342 | + | ||
| 343 | + Collection<FlowRule> rules = ((FlowRuleIntent) compiled.get(0)).flowRules(); | ||
| 344 | + assertThat(rules, hasSize(3)); | ||
| 345 | + | ||
| 346 | + FlowRule rule1 = rules.stream() | ||
| 347 | + .filter(x -> x.deviceId().equals(d1p0.deviceId())) | ||
| 348 | + .findFirst() | ||
| 349 | + .get(); | ||
| 350 | + verifyIdAndPriority(rule1, d1p0.deviceId()); | ||
| 351 | + assertThat(rule1.selector(), is(DefaultTrafficSelector.builder(selector) | ||
| 352 | + .matchInPort(d1p0.port()).build())); | ||
| 353 | + | ||
| 354 | + VlanId vlanToEncap = verifyVlanEncapTreatment(rule1.treatment(), d1p1, true, false); | ||
| 355 | + | ||
| 356 | + assertTrue(VlanId.NO_VID < vlanToEncap.toShort() && vlanToEncap.toShort() < VlanId.MAX_VLAN); | ||
| 357 | + | ||
| 358 | + /** | ||
| 359 | + * This second part is meant to test if the random selection is working properly. | ||
| 360 | + * We are compiling the same intent in order to verify if the VLAN ID is different | ||
| 361 | + * from the previous one. | ||
| 362 | + */ | ||
| 363 | + | ||
| 364 | + List<Intent> compiled2 = sut.compile(constraintVlanIntent, Collections.emptyList()); | ||
| 365 | + assertThat(compiled2, hasSize(1)); | ||
| 366 | + | ||
| 367 | + Collection<FlowRule> rules2 = ((FlowRuleIntent) compiled2.get(0)).flowRules(); | ||
| 368 | + assertThat(rules2, hasSize(3)); | ||
| 369 | + | ||
| 370 | + FlowRule rule2 = rules2.stream() | ||
| 371 | + .filter(x -> x.deviceId().equals(d1p0.deviceId())) | ||
| 372 | + .findFirst() | ||
| 373 | + .get(); | ||
| 374 | + verifyIdAndPriority(rule2, d1p0.deviceId()); | ||
| 375 | + assertThat(rule2.selector(), is(DefaultTrafficSelector.builder(selector) | ||
| 376 | + .matchInPort(d1p0.port()).build())); | ||
| 377 | + | ||
| 378 | + VlanId vlanToEncap2 = verifyVlanEncapTreatment(rule2.treatment(), d1p1, true, false); | ||
| 379 | + | ||
| 380 | + assertTrue(VlanId.NO_VID < vlanToEncap2.toShort() && vlanToEncap2.toShort() < VlanId.MAX_VLAN); | ||
| 381 | + assertNotEquals(vlanToEncap, vlanToEncap2); | ||
| 382 | + | ||
| 383 | + sut.deactivate(); | ||
| 384 | + | ||
| 385 | + } | ||
| 386 | + | ||
| 387 | + } | ||
| 388 | + | ||
| 326 | private VlanId verifyVlanEncapTreatment(TrafficTreatment trafficTreatment, | 389 | private VlanId verifyVlanEncapTreatment(TrafficTreatment trafficTreatment, |
| 327 | ConnectPoint egress, boolean isIngress, boolean isEgress) { | 390 | ConnectPoint egress, boolean isIngress, boolean isEgress) { |
| 328 | Set<Instructions.OutputInstruction> ruleOutput = trafficTreatment.allInstructions().stream() | 391 | Set<Instructions.OutputInstruction> ruleOutput = trafficTreatment.allInstructions().stream() |
| ... | @@ -339,12 +402,21 @@ public class PathIntentCompilerTest { | ... | @@ -339,12 +402,21 @@ public class PathIntentCompilerTest { |
| 339 | .collect(Collectors.toSet()); | 402 | .collect(Collectors.toSet()); |
| 340 | assertThat(vlanRules, hasSize(1)); | 403 | assertThat(vlanRules, hasSize(1)); |
| 341 | L2ModificationInstruction.ModVlanIdInstruction vlanRule = vlanRules.iterator().next(); | 404 | L2ModificationInstruction.ModVlanIdInstruction vlanRule = vlanRules.iterator().next(); |
| 342 | - assertThat(vlanRule.vlanId().toShort(), greaterThan((short) 0)); | 405 | + assertThat(vlanRule.vlanId().toShort(), greaterThan((short) VlanId.NO_VID)); |
| 406 | + assertThat(vlanRule.vlanId().toShort(), lessThan((short) VlanId.MAX_VLAN)); | ||
| 343 | vlanToEncap = vlanRule.vlanId(); | 407 | vlanToEncap = vlanRule.vlanId(); |
| 344 | } else if (!isIngress && !isEgress) { | 408 | } else if (!isIngress && !isEgress) { |
| 345 | - assertThat(trafficTreatment.allInstructions().stream() | 409 | + |
| 346 | - .filter(treat -> treat instanceof L2ModificationInstruction.ModVlanIdInstruction) | 410 | + Set<L2ModificationInstruction.ModVlanIdInstruction> vlanRules = trafficTreatment.allInstructions().stream() |
| 347 | - .collect(Collectors.toSet()), hasSize(0)); | 411 | + .filter(treat -> treat instanceof L2ModificationInstruction.ModVlanIdInstruction) |
| 412 | + .map(x -> (L2ModificationInstruction.ModVlanIdInstruction) x) | ||
| 413 | + .collect(Collectors.toSet()); | ||
| 414 | + assertThat(vlanRules, hasSize(1)); | ||
| 415 | + L2ModificationInstruction.ModVlanIdInstruction vlanRule = vlanRules.iterator().next(); | ||
| 416 | + assertThat(vlanRule.vlanId().toShort(), greaterThan((short) VlanId.NO_VID)); | ||
| 417 | + assertThat(vlanRule.vlanId().toShort(), lessThan((short) VlanId.MAX_VLAN)); | ||
| 418 | + vlanToEncap = vlanRule.vlanId(); | ||
| 419 | + | ||
| 348 | } else { | 420 | } else { |
| 349 | assertThat(trafficTreatment.allInstructions().stream() | 421 | assertThat(trafficTreatment.allInstructions().stream() |
| 350 | .filter(treat -> treat instanceof L2ModificationInstruction.ModVlanIdInstruction) | 422 | .filter(treat -> treat instanceof L2ModificationInstruction.ModVlanIdInstruction) |
| ... | @@ -385,7 +457,7 @@ public class PathIntentCompilerTest { | ... | @@ -385,7 +457,7 @@ public class PathIntentCompilerTest { |
| 385 | .get(); | 457 | .get(); |
| 386 | verifyIdAndPriority(rule1, d1p0.deviceId()); | 458 | verifyIdAndPriority(rule1, d1p0.deviceId()); |
| 387 | assertThat(rule1.selector(), is(DefaultTrafficSelector.builder(selector) | 459 | assertThat(rule1.selector(), is(DefaultTrafficSelector.builder(selector) |
| 388 | - .matchInPort(d1p0.port()).build())); | 460 | + .matchInPort(d1p0.port()).build())); |
| 389 | MplsLabel mplsLabelToEncap = verifyMplsEncapTreatment(rule1.treatment(), d1p1, true, false); | 461 | MplsLabel mplsLabelToEncap = verifyMplsEncapTreatment(rule1.treatment(), d1p1, true, false); |
| 390 | 462 | ||
| 391 | FlowRule rule2 = rules.stream() | 463 | FlowRule rule2 = rules.stream() | ... | ... |
-
Please register or login to post a comment