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
145 additions
and
8 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 | + |
410 | + Set<L2ModificationInstruction.ModVlanIdInstruction> vlanRules = trafficTreatment.allInstructions().stream() | ||
346 | .filter(treat -> treat instanceof L2ModificationInstruction.ModVlanIdInstruction) | 411 | .filter(treat -> treat instanceof L2ModificationInstruction.ModVlanIdInstruction) |
347 | - .collect(Collectors.toSet()), hasSize(0)); | 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) | ... | ... |
-
Please register or login to post a comment