blob: 7ee92921e23961e5fad1ddd9cee253ba238afadd [file] [log] [blame]
Yi Tsengf4e13e32017-03-30 15:38:39 -07001/*
2 * Copyright 2016-present Open Networking Laboratory
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package org.onosproject.vpls;
17
18import com.google.common.collect.ImmutableSet;
19import com.google.common.collect.Lists;
20import com.google.common.collect.Maps;
21import com.google.common.collect.Sets;
22import org.junit.After;
23import org.junit.Before;
24import org.junit.Test;
25import org.onlab.packet.MacAddress;
26import org.onlab.packet.VlanId;
27import org.onosproject.incubator.net.intf.Interface;
28import org.onosproject.incubator.net.intf.InterfaceListener;
29import org.onosproject.incubator.net.intf.InterfaceService;
30import org.onosproject.net.ConnectPoint;
31import org.onosproject.net.EncapsulationType;
32import org.onosproject.net.FilteredConnectPoint;
33import org.onosproject.net.Host;
34import org.onosproject.net.flow.DefaultTrafficSelector;
35import org.onosproject.net.flow.TrafficSelector;
36import org.onosproject.net.flow.criteria.Criterion;
37import org.onosproject.net.flow.criteria.VlanIdCriterion;
38import org.onosproject.net.intent.Intent;
39import org.onosproject.net.intent.IntentService;
40import org.onosproject.net.intent.IntentServiceAdapter;
41import org.onosproject.net.intent.IntentUtils;
42import org.onosproject.net.intent.Key;
43import org.onosproject.net.intent.MultiPointToSinglePointIntent;
44import org.onosproject.net.intent.SinglePointToMultiPointIntent;
45import org.onosproject.vpls.api.VplsData;
46import org.onosproject.vpls.intent.VplsIntentUtility;
47
48import java.util.Collection;
49import java.util.List;
50import java.util.Map;
51import java.util.Set;
52import java.util.stream.Collectors;
53
54import static java.lang.String.format;
55import static org.easymock.EasyMock.anyObject;
56import static org.easymock.EasyMock.createMock;
57import static org.easymock.EasyMock.expect;
58import static org.easymock.EasyMock.expectLastCall;
59import static org.easymock.EasyMock.replay;
60import static org.junit.Assert.assertEquals;
61import static org.junit.Assert.assertTrue;
62import static org.onosproject.net.EncapsulationType.NONE;
63import static org.onosproject.net.EncapsulationType.VLAN;
64
65/**
66 * Tests for {@link VplsIntentUtility}.
67 */
68public class VplsIntentTest extends VplsTest {
69
70 private Set<Host> hostsAvailable;
71 private IntentService intentService;
72 private InterfaceService interfaceService;
73
74 @Before
75 public void setUp() throws Exception {
76 idGenerator = new TestIdGenerator();
77 Intent.unbindIdGenerator(idGenerator);
78 Intent.bindIdGenerator(idGenerator);
79 hostsAvailable = Sets.newHashSet();
80 intentService = new TestIntentService();
81 interfaceService = createMock(InterfaceService.class);
82 interfaceService.addListener(anyObject(InterfaceListener.class));
83 expectLastCall().anyTimes();
84 addIfaceConfig();
85 }
86
87 @After
88 public void tearDown() {
89 Intent.unbindIdGenerator(idGenerator);
90 }
91
92 /**
93 * Creates the interface configuration:
94 * On devices 1 and 2 is configured an interface on port 1 with vlan 100.
95 * On device 3 is configured an interface on port 3 with no vlan.
96 * On devices 3 and 4 is configured an interface on port 1 with vlan 200.
97 * On device 4 is an interface configured on port 2 with vlan 400.
98 * On device 5 are configured two interfaces on port 1 and 2 with no vlan.
99 * On device 5 and 6 is configured an interface on port 1 with vlan 300.
100 */
101 private void addIfaceConfig() {
102 Set<Interface> interfaces = ImmutableSet.copyOf(AVAILABLE_INTERFACES);
103 Set<Interface> vlanOneSet = ImmutableSet.of(V100H1, V100H2);
104 Set<Interface> vlanTwoSet = ImmutableSet.of(V200H1, V200H2);
105 Set<Interface> vlanThreeSet = ImmutableSet.of(VNONEH1, VNONEH2);
106 Set<Interface> vlanFourSet = ImmutableSet.of(V400H1, VNONEH3);
107
108 AVAILABLE_INTERFACES.forEach(intf -> {
109 expect(interfaceService.getInterfacesByPort(intf.connectPoint()))
110 .andReturn(Sets.newHashSet(intf)).anyTimes();
111 });
112 expect(interfaceService.getInterfacesByVlan(VLAN100))
113 .andReturn(vlanOneSet).anyTimes();
114 expect(interfaceService.getInterfacesByVlan(VLAN200))
115 .andReturn(vlanTwoSet).anyTimes();
116 expect(interfaceService.getInterfacesByVlan(VLAN300))
117 .andReturn(vlanThreeSet).anyTimes();
118 expect(interfaceService.getInterfacesByVlan(VLAN400))
119 .andReturn(vlanFourSet).anyTimes();
120 expect(interfaceService.getInterfacesByVlan(VlanId.NONE))
121 .andReturn(vlanFourSet).anyTimes();
122 expect(interfaceService.getInterfaces()).andReturn(interfaces).anyTimes();
123
124 replay(interfaceService);
125 }
126
127 /**
128 * Seven ports are configured with VLANs, while three ports are not. No hosts are
129 * registered by the HostService.
130 *
131 * The first three ports have an interface configured on VPLS 1,
132 * the other three on VPLS 2. Two ports are defined for VPLS 3, while
133 * the two remaining ports are configured on VPLS 4.
134 *
135 * The number of intents expected is 10: three for VPLS 1, three for VPLS 2,
136 * two for VPLS 3, two for VPLS 4. Eight MP2SP intents.
137 * Checks if the number of intents submitted to the intent framework is
138 * equal to the number of intents expected and if all intents are equivalent.
139 */
140 @Test
141 public void activateNoHosts() {
142 List<Intent> expectedIntents = Lists.newArrayList();
143 Set<FilteredConnectPoint> fcPoints;
144 Set<Interface> interfaces;
145
146 interfaces = ImmutableSet.of(V100H1, V200H1, V300H1);
147 VplsData vplsData = createVplsData(VPLS1, VLAN, interfaces);
148 Set<Intent> brcIntents = VplsIntentUtility.buildBrcIntents(vplsData, APPID);
149 brcIntents.forEach(intentService::submit);
150 fcPoints = buildFCPoints(interfaces);
151 expectedIntents.addAll(generateVplsBrc(fcPoints, VPLS1, VLAN));
152
153 checkIntents(expectedIntents);
154
155 interfaces = ImmutableSet.of(V100H2, V200H2, V300H2);
156 vplsData = createVplsData(VPLS2, NONE, interfaces);
157 brcIntents = VplsIntentUtility.buildBrcIntents(vplsData, APPID);
158 brcIntents.forEach(intentService::submit);
159 fcPoints = buildFCPoints(interfaces);
160 expectedIntents.addAll(generateVplsBrc(fcPoints, VPLS2, NONE));
161
162 checkIntents(expectedIntents);
163
164 interfaces = ImmutableSet.of(VNONEH1, VNONEH2);
165 vplsData = createVplsData(VPLS3, NONE, interfaces);
166 brcIntents = VplsIntentUtility.buildBrcIntents(vplsData, APPID);
167 brcIntents.forEach(intentService::submit);
168 fcPoints = buildFCPoints(interfaces);
169 expectedIntents.addAll(generateVplsBrc(fcPoints, VPLS3, NONE));
170
171 checkIntents(expectedIntents);
172
173 interfaces = ImmutableSet.of(V400H1, VNONEH3);
174 vplsData = createVplsData(VPLS4, NONE, interfaces);
175 brcIntents = VplsIntentUtility.buildBrcIntents(vplsData, APPID);
176 brcIntents.forEach(intentService::submit);
177 fcPoints = buildFCPoints(interfaces);
178 expectedIntents.addAll(generateVplsBrc(fcPoints, VPLS4, NONE));
179
180 checkIntents(expectedIntents);
181 }
182
183
184
185 /**
186 * Ten ports are configured with VLANs and ten hosts are registered by the
187 * HostService.
188 *
189 * The first three ports have an interface configured on VPLS 1,
190 * the other three on VPLS 2, two on VPLS3 and two on VPLS4.
191 *
192 * The number of intents expected is twenty: six
193 * for VPLS 1, six for VPLS 2. four for VPLS 3, four for VPLS 4.
194 * That is ten sp2mp intents, ten mp2sp intents. For VPLS 1
195 * IPs are added to demonstrate this doesn't influence the number of intents
196 * created. Checks if the number of intents submitted to the intent
197 * framework is equal to the number of intents expected and if all intents
198 * are equivalent.
199 */
200 @Test
201 public void tenInterfacesConfiguredHostsPresent() {
202 hostsAvailable.addAll(AVAILABLE_HOSTS);
203
204 List<Intent> expectedIntents = Lists.newArrayList();
205 Set<FilteredConnectPoint> fcPoints;
206 Set<Host> hosts;
207 Set<Interface> interfaces;
208 VplsData vplsData;
209 Set<Intent> brcIntents;
210 Set<Intent> uniIntents;
211
212 interfaces = ImmutableSet.of(V100H1, V200H1, V300H1);
213 fcPoints = buildFCPoints(interfaces);
214 hosts = ImmutableSet.of(V100HOST1, V200HOST1, V300HOST1);
215 vplsData = createVplsData(VPLS1, VLAN, interfaces);
216 brcIntents = VplsIntentUtility.buildBrcIntents(vplsData, APPID);
217 uniIntents = VplsIntentUtility.buildUniIntents(vplsData, hosts, APPID);
218 brcIntents.forEach(intentService::submit);
219 uniIntents.forEach(intentService::submit);
220 expectedIntents.addAll(generateVplsBrc(fcPoints, VPLS1, VLAN));
221 expectedIntents.addAll(generateVplsUni(fcPoints, hosts, VPLS1, VLAN));
222
223 interfaces = ImmutableSet.of(V100H2, V200H2, V300H2);
224 fcPoints = buildFCPoints(interfaces);
225 hosts = ImmutableSet.of(V100HOST2, V200HOST2, V300HOST2);
226 vplsData = createVplsData(VPLS2, NONE, interfaces);
227 brcIntents = VplsIntentUtility.buildBrcIntents(vplsData, APPID);
228 uniIntents = VplsIntentUtility.buildUniIntents(vplsData, hosts, APPID);
229 brcIntents.forEach(intentService::submit);
230 uniIntents.forEach(intentService::submit);
231 expectedIntents.addAll(generateVplsBrc(fcPoints, VPLS2, NONE));
232 expectedIntents.addAll(generateVplsUni(fcPoints, hosts, VPLS2, NONE));
233
234 interfaces = ImmutableSet.of(VNONEH1, VNONEH2);
235 fcPoints = buildFCPoints(interfaces);
236 hosts = ImmutableSet.of(VNONEHOST1, VNONEHOST2);
237 vplsData = createVplsData(VPLS3, NONE, interfaces);
238 brcIntents = VplsIntentUtility.buildBrcIntents(vplsData, APPID);
239 uniIntents = VplsIntentUtility.buildUniIntents(vplsData, hosts, APPID);
240 brcIntents.forEach(intentService::submit);
241 uniIntents.forEach(intentService::submit);
242 expectedIntents.addAll(generateVplsBrc(fcPoints, VPLS3, NONE));
243 expectedIntents.addAll(generateVplsUni(fcPoints, hosts, VPLS3, NONE));
244
245 interfaces = ImmutableSet.of(V400H1, VNONEH3);
246 fcPoints = buildFCPoints(interfaces);
247 hosts = ImmutableSet.of(V400HOST1, VNONEHOST3);
248 vplsData = createVplsData(VPLS4, NONE, interfaces);
249 brcIntents = VplsIntentUtility.buildBrcIntents(vplsData, APPID);
250 uniIntents = VplsIntentUtility.buildUniIntents(vplsData, hosts, APPID);
251 brcIntents.forEach(intentService::submit);
252 uniIntents.forEach(intentService::submit);
253 expectedIntents.addAll(generateVplsBrc(fcPoints, VPLS4, NONE));
254 expectedIntents.addAll(generateVplsUni(fcPoints, hosts, VPLS4, NONE));
255
256 checkIntents(expectedIntents);
257 }
258
259 /**
260 * Generates a list of the expected sp2mp intents for a VPLS.
261 *
262 * @param fcPoints the filtered connect point
263 * @param name the name of the VPLS
264 * @param encap the encapsulation type
265 * @return the list of expected sp2mp intents for the given VPLS
266 */
267 private List<SinglePointToMultiPointIntent>
268 generateVplsBrc(Set<FilteredConnectPoint> fcPoints, String name, EncapsulationType encap) {
269 List<SinglePointToMultiPointIntent> intents = Lists.newArrayList();
270
271 fcPoints.forEach(point -> {
272 Set<FilteredConnectPoint> otherPoints =
273 fcPoints.stream()
274 .filter(fcp -> !fcp.equals(point))
275 .collect(Collectors.toSet());
276
277 Key brckey = buildKey(VplsIntentUtility.PREFIX_BROADCAST,
278 point.connectPoint(),
279 name,
280 MacAddress.BROADCAST);
281
282 intents.add(buildBrcIntent(brckey, point, otherPoints, encap));
283 });
284
285 return intents;
286 }
287
288 /**
289 * Generates a list of expected mp2sp intents for a given VPLS.
290 *
291 * @param fcPoints the filtered connect point
292 * @param hosts the hosts
293 * @param name the name of the VPLS
294 * @param encap the encapsulation type
295 * @return the list of expected mp2sp intents for the given VPLS
296 */
297 private List<MultiPointToSinglePointIntent>
298 generateVplsUni(Set<FilteredConnectPoint> fcPoints, Set<Host> hosts,
299 String name, EncapsulationType encap) {
300 List<MultiPointToSinglePointIntent> intents = Lists.newArrayList();
301
302 hosts.forEach(host -> {
303 FilteredConnectPoint hostPoint = getHostPoint(host, fcPoints);
304
305 Set<FilteredConnectPoint> otherPoints =
306 fcPoints.stream()
307 .filter(fcp -> !fcp.equals(hostPoint))
308 .collect(Collectors.toSet());
309
310 Key uniKey = buildKey(VplsIntentUtility.PREFIX_UNICAST,
311 host.location(), name, host.mac());
312
313 intents.add(buildUniIntent(uniKey, otherPoints, hostPoint, host, encap));
314 });
315
316 return intents;
317 }
318
319 /**
320 * Checks if the number of intents submitted to the intent framework is equal
321 * to the number of intents expected and if all intents are equivalent.
322 *
323 * @param intents the list of intents expected
324 */
325 private void checkIntents(List<Intent> intents) {
326 assertEquals("The number of intents submitted differs from the number" +
327 " of intents expected. ",
328 intents.size(), intentService.getIntentCount());
329 for (Intent intentOne : intents) {
330 boolean found = false;
331 for (Intent intentTwo : intentService.getIntents()) {
332 if (intentOne.key().equals(intentTwo.key())) {
333 found = true;
334 assertTrue(format("The intent submitted is different from" +
335 " the intent expected. %s %s",
336 intentOne, intentTwo),
337 IntentUtils.intentsAreEqual(intentOne, intentTwo));
338 break;
339 }
340 }
341 assertTrue("The intent submitted is not equal to any of the expected" +
342 " intents. ", found);
343 }
344 }
345
346 /**
347 * Builds a broadcast intent.
348 *
349 * @param key the key to identify the intent
350 * @param src the ingress connect point
351 * @param dsts the egress connect points
352 * @return the generated single-point to multi-point intent
353 */
354 private SinglePointToMultiPointIntent buildBrcIntent(Key key,
355 FilteredConnectPoint src,
356 Set<FilteredConnectPoint> dsts,
357 EncapsulationType encap) {
358 SinglePointToMultiPointIntent.Builder intentBuilder;
359 TrafficSelector selector = DefaultTrafficSelector.builder()
360 .matchEthDst(MacAddress.BROADCAST)
361 .build();
362 intentBuilder = SinglePointToMultiPointIntent.builder()
363 .appId(APPID)
364 .key(key)
365 .selector(selector)
366 .filteredIngressPoint(src)
367 .filteredEgressPoints(dsts)
368 .constraints(VplsIntentUtility.PARTIAL_FAILURE_CONSTRAINT)
369 .priority(PRIORITY_OFFSET);
370 VplsIntentUtility.setEncap(intentBuilder,
371 VplsIntentUtility.PARTIAL_FAILURE_CONSTRAINT,
372 encap);
373
374 return intentBuilder.build();
375 }
376
377 /**
378 * Builds a unicast intent.
379 *
380 * @param key the key to identify the intent
381 * @param srcs the ingress connect points
382 * @param dst the egress connect point
383 * @param host the destination Host
384 * @return the generated multi-point to single-point intent
385 */
386 private MultiPointToSinglePointIntent buildUniIntent(Key key,
387 Set<FilteredConnectPoint> srcs,
388 FilteredConnectPoint dst,
389 Host host,
390 EncapsulationType encap) {
391 MultiPointToSinglePointIntent.Builder intentBuilder;
392 TrafficSelector selector = DefaultTrafficSelector.builder()
393 .matchEthDst(host.mac())
394 .build();
395 intentBuilder = MultiPointToSinglePointIntent.builder()
396 .appId(APPID)
397 .key(key)
398 .selector(selector)
399 .filteredIngressPoints(srcs)
400 .filteredEgressPoint(dst)
401 .constraints(VplsIntentUtility.PARTIAL_FAILURE_CONSTRAINT)
402 .priority(PRIORITY_OFFSET);
403 VplsIntentUtility.setEncap(intentBuilder,
404 VplsIntentUtility.PARTIAL_FAILURE_CONSTRAINT,
405 encap);
406
407 return intentBuilder.build();
408 }
409
410 /**
411 * Returns the filtered connect point associated to a given host.
412 *
413 * @param host the target host
414 * @param fcps the filtered connected points
415 * @return the filtered connect point associated to the given host; null
416 * otherwise
417 */
418 private FilteredConnectPoint getHostPoint(Host host,
419 Set<FilteredConnectPoint> fcps) {
420 return fcps.stream()
421 .filter(fcp -> fcp.connectPoint().equals(host.location()))
422 .filter(fcp -> {
423 VlanIdCriterion vlanCriterion =
424 (VlanIdCriterion) fcp.trafficSelector().
425 getCriterion(Criterion.Type.VLAN_VID);
426 return vlanCriterion == null ||
427 vlanCriterion.vlanId().equals(host.vlan());
428 })
429 .findFirst()
430 .orElse(null);
431 }
432
433 /**
434 * Computes a set of filtered connect points from a list of given interfaces.
435 *
436 * @param interfaces the interfaces to compute
437 * @return the set of filtered connect points
438 */
439 private Set<FilteredConnectPoint> buildFCPoints(Collection<Interface> interfaces) {
440 // Build all filtered connected points in the VPLS
441 return interfaces
442 .stream()
443 .map(intf -> {
444 TrafficSelector.Builder selectorBuilder =
445 DefaultTrafficSelector.builder();
446 if (!intf.vlan().equals(VlanId.NONE)) {
447 selectorBuilder.matchVlanId(intf.vlan());
448 }
449 return new FilteredConnectPoint(intf.connectPoint(),
450 selectorBuilder.build());
451 })
452 .collect(Collectors.toSet());
453 }
454
455 /**
456 * Builds an intent Key either for a single-point to multi-point or
457 * multi-point to single-point intent, based on a prefix that defines
458 * the intent type, the connection point representing the source or the
459 * destination and the VLAN Id representing the VPLS.
460 *
461 * @param prefix the key prefix
462 * @param cPoint the ingress/egress connect point
463 * @param vplsName the VPLS name
464 * @param hostMac the ingress/egress MAC address
465 * @return the key to identify the intent
466 */
467 private Key buildKey(String prefix,
468 ConnectPoint cPoint,
469 String vplsName,
470 MacAddress hostMac) {
471 String keyString = vplsName +
472 DASH +
473 prefix +
474 DASH +
475 cPoint.deviceId() +
476 DASH +
477 cPoint.port() +
478 DASH +
479 hostMac;
480
481 return Key.of(keyString, APPID);
482 }
483
484 /**
485 * Creates VPLS data by given name, encapsulation type and network
486 * interfaces.
487 *
488 * @param name the VPLS name
489 * @param encap the encapsulation type
490 * @param interfaces the network interfaces
491 * @return the VPLS data
492 */
493 private VplsData createVplsData(String name, EncapsulationType encap,
494 Set<Interface> interfaces) {
495 VplsData vplsData = VplsData.of(name, encap);
496 vplsData.addInterfaces(interfaces);
497 return vplsData;
498 }
499
500 /**
501 * Represents a fake IntentService class that allows to store and retrieve
502 * intents without implementing the IntentService logic.
503 */
504 private class TestIntentService extends IntentServiceAdapter {
505 private Map<Key, Intent> intents;
506
507 public TestIntentService() {
508 intents = Maps.newHashMap();
509 }
510
511 @Override
512 public void submit(Intent intent) {
513 intents.put(intent.key(), intent);
514 }
515
516 @Override
517 public long getIntentCount() {
518 return intents.size();
519 }
520
521 @Override
522 public Iterable<Intent> getIntents() {
523 return intents.values();
524 }
525
526 @Override
527 public Intent getIntent(Key intentKey) {
528 for (Intent intent : intents.values()) {
529 if (intent.key().equals(intentKey)) {
530 return intent;
531 }
532 }
533 return null;
534 }
535 }
536
537}