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