/* * Copyright 2016-present Open Networking Laboratory * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.onosproject.net.intent.impl.compiler; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import org.hamcrest.Matchers; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.onlab.packet.IpPrefix; import org.onlab.packet.VlanId; import org.onlab.util.Bandwidth; import org.onosproject.TestApplicationId; import org.onosproject.core.ApplicationId; import org.onosproject.net.ConnectPoint; import org.onosproject.net.DeviceId; import org.onosproject.net.FilteredConnectPoint; import org.onosproject.net.PortNumber; import org.onosproject.net.flow.DefaultTrafficSelector; import org.onosproject.net.flow.TrafficSelector; import org.onosproject.net.flow.TrafficTreatment; import org.onosproject.net.intent.AbstractIntentTest; import org.onosproject.net.intent.Constraint; import org.onosproject.net.intent.Intent; import org.onosproject.net.intent.IntentException; import org.onosproject.net.intent.IntentTestsMocks; import org.onosproject.net.intent.Key; import org.onosproject.net.intent.LinkCollectionIntent; import org.onosproject.net.intent.SinglePointToMultiPointIntent; import org.onosproject.net.intent.constraint.BandwidthConstraint; import org.onosproject.net.intent.constraint.PartialFailureConstraint; import org.onosproject.net.resource.ContinuousResource; import org.onosproject.net.resource.MockResourceService; import org.onosproject.net.resource.ResourceAllocation; import org.onosproject.net.resource.ResourceService; import org.onosproject.net.resource.Resources; import org.onosproject.net.topology.PathService; import java.util.Collections; import java.util.List; import java.util.Set; import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertEquals; import static org.onosproject.net.intent.LinksHaveEntryWithSourceDestinationPairMatcher.linksHasPath; /** * Unit tests for SinglePointToMultiPointIntentCompiler. */ public class SinglePointToMultiPointIntentCompilerTest extends AbstractIntentTest { private static final ApplicationId APPID = new TestApplicationId("foo"); private static final String S1 = "s1"; private static final String S2 = "s2"; private static final String S3 = "s3"; private static final String S4 = "s4"; private static final String S5 = "s5"; private static final String S6 = "s6"; private static final String S7 = "s7"; private static final String S8 = "s8"; private static final DeviceId DID_1 = DeviceId.deviceId("of:" + S1); private static final DeviceId DID_2 = DeviceId.deviceId("of:" + S2); private static final DeviceId DID_3 = DeviceId.deviceId("of:" + S3); private static final DeviceId DID_4 = DeviceId.deviceId("of:" + S4); private static final DeviceId DID_5 = DeviceId.deviceId("of:" + S5); private static final DeviceId DID_8 = DeviceId.deviceId("of:" + S8); private static final PortNumber PORT_1 = PortNumber.portNumber(1); private static final PortNumber PORT_2 = PortNumber.portNumber(2); private static final PortNumber PORT_3 = PortNumber.portNumber(3); private TrafficSelector selector = new IntentTestsMocks.MockSelector(); private TrafficTreatment treatment = new IntentTestsMocks.MockTreatment(); /** * Creates a SinglePointToMultiPoint intent for an ingress point * and a group of egress points. * * @param ingress the filtered ingress point * @param egress the set of filtered egress points * @return a single point to multi point intent */ private SinglePointToMultiPointIntent makeIntent(FilteredConnectPoint ingress, Set<FilteredConnectPoint> egress) { return makeIntent(ingress, egress, selector); } /** * Generates SinglePointToMultiPointIntent with filtered connection point * and the specified selector. * * @param ingress the filtered ingress point * @param egress the filtered egress point * @param trafficSelector the intent traffic selector * @return a single point to multi point intent */ private SinglePointToMultiPointIntent makeIntent(FilteredConnectPoint ingress, Set<FilteredConnectPoint> egress, TrafficSelector trafficSelector) { return makeIntent(ingress, egress, trafficSelector, Lists.newArrayList()); } /** * Generates SinglePointToMultiPointIntent with filtered connection point * and the given constraints set. * * @param ingress the filtered ingress point * @param egress the filtered egress point * @param constraints the list of intent constraints * @return a single point to multi point intent */ private SinglePointToMultiPointIntent makeIntent(FilteredConnectPoint ingress, Set<FilteredConnectPoint> egress, List<Constraint> constraints) { return makeIntent(ingress, egress, selector, constraints); } /** * Generates SinglePointToMultiPointIntent with filtered connection point, * and the specified traffic selector and intent constraints. * * @param ingress the filtered ingress point * @param egress the set of filtered egress points * @param trafficSelector the intent traffic selector * @param constraints the list of intent constraints * @return a single point to multi point intent */ private SinglePointToMultiPointIntent makeIntent(FilteredConnectPoint ingress, Set<FilteredConnectPoint> egress, TrafficSelector trafficSelector, List<Constraint> constraints) { return SinglePointToMultiPointIntent.builder() .appId(APPID) .treatment(treatment) .selector(trafficSelector) .filteredIngressPoint(ingress) .filteredEgressPoints(egress) .constraints(constraints) .build(); } /** * Creates a compiler for SinglePointToMultiPoint intents. * * @param hops hops to use while computing paths for this intent * @param pathService the path service * @param resourceService the resource service * @return a single point to multi point intent compiler */ private SinglePointToMultiPointIntentCompiler makeCompiler(String[] hops, PathService pathService, ResourceService resourceService) { SinglePointToMultiPointIntentCompiler compiler = new SinglePointToMultiPointIntentCompiler(); compiler.deviceService = new IntentTestsMocks.MockDeviceService(); if (pathService == null) { compiler.pathService = new IntentTestsMocks.Mp2MpMockPathService(hops); } else { compiler.pathService = pathService; } if (resourceService == null) { compiler.resourceService = new MockResourceService(); } else { compiler.resourceService = resourceService; } return compiler; } /** * Creates a compiler for SinglePointToMultiPoint intents. * * @param hops the hops to use while computing paths for this intent * @return a single point to multi point intent compiler */ private SinglePointToMultiPointIntentCompiler makeCompiler(String[] hops) { return makeCompiler(hops, null, null); } /** * Tests a single ingress point with 8 hops to its egress point. */ @Test public void testSingleLongPathCompilation() { FilteredConnectPoint ingress = new FilteredConnectPoint(new ConnectPoint(DID_1, PORT_1)); Set<FilteredConnectPoint> egress = Sets.newHashSet(new FilteredConnectPoint(new ConnectPoint(DID_8, PORT_2))); SinglePointToMultiPointIntent intent = makeIntent(ingress, egress); assertThat(intent, is(notNullValue())); String[] hops = {S2, S3, S4, S5, S6, S7}; SinglePointToMultiPointIntentCompiler compiler = makeCompiler(hops); assertThat(compiler, is(notNullValue())); List<Intent> result = compiler.compile(intent, null); assertThat(result, is(Matchers.notNullValue())); assertThat(result, hasSize(1)); Intent resultIntent = result.get(0); assertThat(resultIntent instanceof LinkCollectionIntent, is(true)); if (resultIntent instanceof LinkCollectionIntent) { LinkCollectionIntent linkIntent = (LinkCollectionIntent) resultIntent; assertThat(linkIntent.links(), hasSize(7)); assertThat(linkIntent.links(), linksHasPath(S1, S2)); assertThat(linkIntent.links(), linksHasPath(S2, S3)); assertThat(linkIntent.links(), linksHasPath(S3, S4)); assertThat(linkIntent.links(), linksHasPath(S4, S5)); assertThat(linkIntent.links(), linksHasPath(S5, S6)); assertThat(linkIntent.links(), linksHasPath(S6, S7)); assertThat(linkIntent.links(), linksHasPath(S7, S8)); } assertThat("key is inherited", resultIntent.key(), is(intent.key())); } /** * Tests a simple topology where two egress points share some path segments * and some path segments are not shared. */ @Test public void testTwoEgressCompilation() { FilteredConnectPoint ingress = new FilteredConnectPoint(new ConnectPoint(DID_1, PORT_1)); FilteredConnectPoint egressOne = new FilteredConnectPoint(new ConnectPoint(DID_4, PORT_2)); FilteredConnectPoint egressTwo = new FilteredConnectPoint(new ConnectPoint(DID_5, PORT_2)); Set<FilteredConnectPoint> egress = Sets.newHashSet(egressOne, egressTwo); SinglePointToMultiPointIntent intent = makeIntent(ingress, egress); assertThat(intent, is(notNullValue())); final String[] hops = {S2, S3}; SinglePointToMultiPointIntentCompiler compiler = makeCompiler(hops); assertThat(compiler, is(notNullValue())); List<Intent> result = compiler.compile(intent, null); assertThat(result, is(notNullValue())); assertThat(result, hasSize(1)); Intent resultIntent = result.get(0); assertThat(resultIntent instanceof LinkCollectionIntent, is(true)); if (resultIntent instanceof LinkCollectionIntent) { LinkCollectionIntent linkIntent = (LinkCollectionIntent) resultIntent; assertThat(linkIntent.links(), hasSize(4)); assertThat(linkIntent.links(), linksHasPath(S1, S2)); assertThat(linkIntent.links(), linksHasPath(S2, S3)); assertThat(linkIntent.links(), linksHasPath(S3, S4)); assertThat(linkIntent.links(), linksHasPath(S3, S5)); } assertThat("key is inherited", resultIntent.key(), is(intent.key())); } /** * Tests multiple egress points that share a common path to the ingress * point. */ @Test public void testMultiEgressCompilation() { FilteredConnectPoint ingress = new FilteredConnectPoint(new ConnectPoint(DID_1, PORT_1)); FilteredConnectPoint egressOne = new FilteredConnectPoint(new ConnectPoint(DID_3, PORT_2)); FilteredConnectPoint egressTwo = new FilteredConnectPoint(new ConnectPoint(DID_4, PORT_2)); FilteredConnectPoint egressThree = new FilteredConnectPoint(new ConnectPoint(DID_5, PORT_2)); Set<FilteredConnectPoint> egress = Sets.newHashSet(egressOne, egressTwo, egressThree); SinglePointToMultiPointIntent intent = makeIntent(ingress, egress); assertThat(intent, is(notNullValue())); final String[] hops = {S2}; SinglePointToMultiPointIntentCompiler compiler = makeCompiler(hops); assertThat(compiler, is(notNullValue())); List<Intent> result = compiler.compile(intent, null); assertThat(result, is(notNullValue())); assertThat(result, hasSize(1)); Intent resultIntent = result.get(0); assertThat(resultIntent instanceof LinkCollectionIntent, is(true)); if (resultIntent instanceof LinkCollectionIntent) { LinkCollectionIntent linkIntent = (LinkCollectionIntent) resultIntent; assertThat(linkIntent.links(), hasSize(4)); assertThat(linkIntent.links(), linksHasPath(S1, S2)); assertThat(linkIntent.links(), linksHasPath(S2, S3)); assertThat(linkIntent.links(), linksHasPath(S2, S4)); assertThat(linkIntent.links(), linksHasPath(S2, S5)); } assertThat("key is inherited", resultIntent.key(), is(intent.key())); } /** * Tests ingress and egress on the same device. */ @Test public void testSameDeviceCompilation() { FilteredConnectPoint ingress = new FilteredConnectPoint(new ConnectPoint(DID_1, PORT_1)); Set<FilteredConnectPoint> egress = Sets.newHashSet(new FilteredConnectPoint(new ConnectPoint(DID_1, PORT_2)), new FilteredConnectPoint(new ConnectPoint(DID_1, PORT_3))); SinglePointToMultiPointIntent intent = makeIntent(ingress, egress); assertThat(intent, is(notNullValue())); final String[] hops = {}; SinglePointToMultiPointIntentCompiler compiler = makeCompiler(hops); assertThat(compiler, is(notNullValue())); List<Intent> result = compiler.compile(intent, null); assertThat(result, is(notNullValue())); assertThat(result, hasSize(1)); Intent resultIntent = result.get(0); assertThat(resultIntent, instanceOf(LinkCollectionIntent.class)); if (resultIntent instanceof LinkCollectionIntent) { LinkCollectionIntent linkIntent = (LinkCollectionIntent) resultIntent; assertThat(linkIntent.links(), hasSize(0)); } assertThat("key is inherited", resultIntent.key(), is(intent.key())); } /** * Tests filtered ingress and egress connect points. */ @Test public void testFilteredConnectPointIntent() { FilteredConnectPoint ingress = new FilteredConnectPoint(new ConnectPoint(DID_1, PORT_1)); Set<FilteredConnectPoint> egress = ImmutableSet.of( new FilteredConnectPoint(new ConnectPoint(DID_3, PORT_1), DefaultTrafficSelector.builder().matchVlanId(VlanId.vlanId("100")).build()), new FilteredConnectPoint(new ConnectPoint(DID_4, PORT_1), DefaultTrafficSelector.builder().matchVlanId(VlanId.vlanId("200")).build()) ); SinglePointToMultiPointIntent intent = makeIntent(ingress, egress, selector); String[] hops = {S2}; SinglePointToMultiPointIntentCompiler compiler = makeCompiler(hops); assertThat(compiler, is(notNullValue())); List<Intent> result = compiler.compile(intent, null); assertThat(result, is(notNullValue())); assertThat(result, hasSize(1)); Intent resultIntent = result.get(0); assertThat(resultIntent, instanceOf(LinkCollectionIntent.class)); if (resultIntent instanceof LinkCollectionIntent) { LinkCollectionIntent linkIntent = (LinkCollectionIntent) resultIntent; assertThat(linkIntent.links(), hasSize(3)); assertThat(linkIntent.links(), linksHasPath(S1, S2)); assertThat(linkIntent.links(), linksHasPath(S2, S3)); assertThat(linkIntent.links(), linksHasPath(S2, S4)); Set<FilteredConnectPoint> ingressPoints = linkIntent.filteredIngressPoints(); assertThat("Link collection ingress points do not match base intent", ingressPoints.size() == 1 && ingressPoints.contains(intent.filteredIngressPoint())); assertThat("Link collection egress points do not match base intent", linkIntent.filteredEgressPoints().equals(intent.filteredEgressPoints())); } assertThat("key is inherited", resultIntent.key(), is(intent.key())); } /** * Tests filtered ingress and egress points with an intent selector set. */ @Test public void testNonTrivialSelectorsIntent() { FilteredConnectPoint ingress = new FilteredConnectPoint(new ConnectPoint(DID_1, PORT_1)); Set<FilteredConnectPoint> egress = ImmutableSet.of( new FilteredConnectPoint(new ConnectPoint(DID_3, PORT_1), DefaultTrafficSelector.builder().matchVlanId(VlanId.vlanId("100")).build()), new FilteredConnectPoint(new ConnectPoint(DID_4, PORT_1), DefaultTrafficSelector.builder().matchVlanId(VlanId.vlanId("200")).build()) ); TrafficSelector ipPrefixSelector = DefaultTrafficSelector.builder() .matchIPDst(IpPrefix.valueOf("192.168.100.0/24")) .build(); SinglePointToMultiPointIntent intent = makeIntent(ingress, egress, ipPrefixSelector); String[] hops = {S2}; SinglePointToMultiPointIntentCompiler compiler = makeCompiler(hops); assertThat(compiler, is(notNullValue())); List<Intent> result = compiler.compile(intent, null); assertThat(result, is(notNullValue())); assertThat(result, hasSize(1)); Intent resultIntent = result.get(0); assertThat(resultIntent, instanceOf(LinkCollectionIntent.class)); if (resultIntent instanceof LinkCollectionIntent) { LinkCollectionIntent linkIntent = (LinkCollectionIntent) resultIntent; assertThat(linkIntent.links(), hasSize(3)); assertThat(linkIntent.links(), linksHasPath(S1, S2)); assertThat(linkIntent.links(), linksHasPath(S2, S3)); assertThat(linkIntent.links(), linksHasPath(S2, S4)); Set<FilteredConnectPoint> ingressPoints = linkIntent.filteredIngressPoints(); assertThat("Link collection ingress points do not match base intent", ingressPoints.size() == 1 && ingressPoints.contains(intent.filteredIngressPoint())); assertThat("Link collection egress points do not match base intent", linkIntent.filteredEgressPoints().equals(intent.filteredEgressPoints())); assertThat(linkIntent.selector(), is(ipPrefixSelector)); } assertThat("key is inherited", resultIntent.key(), is(intent.key())); } /** * Tests if bandwidth resources get allocated correctly. */ @Test public void testBandwidthConstrainedIntentAllocation() { final double bpsTotal = 1000.0; final double bpsToReserve = 100.0; ContinuousResource resourceSw1P1 = Resources.continuous(DID_1, PORT_1, Bandwidth.class) .resource(bpsToReserve); ContinuousResource resourceSw1P2 = Resources.continuous(DID_1, PORT_2, Bandwidth.class) .resource(bpsToReserve); ContinuousResource resourceSw2P1 = Resources.continuous(DID_2, PORT_1, Bandwidth.class) .resource(bpsToReserve); ContinuousResource resourceSw2P2 = Resources.continuous(DID_2, PORT_2, Bandwidth.class) .resource(bpsToReserve); ContinuousResource resourceSw3P1 = Resources.continuous(DID_3, PORT_1, Bandwidth.class) .resource(bpsToReserve); ContinuousResource resourceSw3P2 = Resources.continuous(DID_3, PORT_2, Bandwidth.class) .resource(bpsToReserve); ContinuousResource resourceSw3P3 = Resources.continuous(DID_3, PORT_3, Bandwidth.class) .resource(bpsToReserve); ContinuousResource resourceSw4P1 = Resources.continuous(DID_4, PORT_1, Bandwidth.class) .resource(bpsToReserve); ContinuousResource resourceSw4P2 = Resources.continuous(DID_4, PORT_2, Bandwidth.class) .resource(bpsToReserve); String[] hops = {DID_3.toString()}; final ResourceService resourceService = MockResourceService.makeCustomBandwidthResourceService(bpsTotal); final List<Constraint> constraints = Collections.singletonList(new BandwidthConstraint(Bandwidth.bps(bpsToReserve))); FilteredConnectPoint ingress = new FilteredConnectPoint(new ConnectPoint(DID_4, PORT_1)); Set<FilteredConnectPoint> egress = ImmutableSet.of( new FilteredConnectPoint(new ConnectPoint(DID_1, PORT_2)), new FilteredConnectPoint(new ConnectPoint(DID_2, PORT_2))); TrafficSelector ipPrefixSelector = DefaultTrafficSelector.builder() .matchIPDst(IpPrefix.valueOf("192.168.100.0/24")) .build(); SinglePointToMultiPointIntent intent = makeIntent(ingress, egress, ipPrefixSelector, constraints); SinglePointToMultiPointIntentCompiler compiler = makeCompiler(null, new IntentTestsMocks.FixedMP2MPMockPathService(hops), resourceService); compiler.compile(intent, null); Key intentKey = intent.key(); ResourceAllocation rA1 = new ResourceAllocation(resourceSw1P1, intentKey); ResourceAllocation rA2 = new ResourceAllocation(resourceSw1P2, intentKey); ResourceAllocation rA3 = new ResourceAllocation(resourceSw2P1, intentKey); ResourceAllocation rA4 = new ResourceAllocation(resourceSw2P2, intentKey); ResourceAllocation rA5 = new ResourceAllocation(resourceSw3P1, intentKey); ResourceAllocation rA6 = new ResourceAllocation(resourceSw3P2, intentKey); ResourceAllocation rA7 = new ResourceAllocation(resourceSw3P3, intentKey); ResourceAllocation rA8 = new ResourceAllocation(resourceSw4P1, intentKey); ResourceAllocation rA9 = new ResourceAllocation(resourceSw4P2, intentKey); Set<ResourceAllocation> expectedResourceAllocations = ImmutableSet.of(rA1, rA2, rA3, rA4, rA5, rA6, rA7, rA8, rA9); Set<ResourceAllocation> resourceAllocations = ImmutableSet.copyOf(resourceService.getResourceAllocations(intentKey)); assertThat(resourceAllocations, hasSize(9)); assertEquals(expectedResourceAllocations, resourceAllocations); } /** * Tests if all expected links are present when a partial failure * constraint is used and one ingress is not present. */ @Test public void testPartialFailureConstraintSuccess() { FilteredConnectPoint ingress = new FilteredConnectPoint(new ConnectPoint(DID_1, PORT_1)); Set<FilteredConnectPoint> egress = ImmutableSet.of( new FilteredConnectPoint(new ConnectPoint(DID_4, PORT_2)), new FilteredConnectPoint(new ConnectPoint(DID_5, PORT_2))); final List<Constraint> constraints = Collections.singletonList(new PartialFailureConstraint()); SinglePointToMultiPointIntent intent = makeIntent(ingress, egress, constraints); String[] hops = {S3}; SinglePointToMultiPointIntentCompiler compiler = makeCompiler(null, new IntentTestsMocks.FixedMP2MPMockPathService(hops), null); assertThat(compiler, is(notNullValue())); List<Intent> result = compiler.compile(intent, null); assertThat(result, is(notNullValue())); assertThat(result, hasSize(1)); Intent resultIntent = result.get(0); assertThat(resultIntent, instanceOf(LinkCollectionIntent.class)); if (resultIntent instanceof LinkCollectionIntent) { LinkCollectionIntent linkIntent = (LinkCollectionIntent) resultIntent; assertThat(linkIntent.links(), hasSize(2)); assertThat(linkIntent.links(), linksHasPath(S1, S3)); assertThat(linkIntent.links(), linksHasPath(S3, S4)); } assertThat("key is inherited", resultIntent.key(), is(intent.key())); } /** * Exception expected to be raised when an intent does not find all paths * and a partiale failure constraint is not specified. */ @Rule public ExpectedException intentException = ExpectedException.none(); /** * Tests if compiling an intent without partial failure constraints set and * with a missing egress connect point generates an exception and no other * results. */ @Test public void testPartialFailureConstraintFailure() { FilteredConnectPoint ingress = new FilteredConnectPoint(new ConnectPoint(DID_1, PORT_1)); Set<FilteredConnectPoint> egress = ImmutableSet.of( new FilteredConnectPoint(new ConnectPoint(DID_4, PORT_2)), new FilteredConnectPoint(new ConnectPoint(DID_5, PORT_2))); SinglePointToMultiPointIntent intent = makeIntent(ingress, egress); String[] hops = {S3}; SinglePointToMultiPointIntentCompiler compiler = makeCompiler(null, new IntentTestsMocks.FixedMP2MPMockPathService(hops), null); assertThat(compiler, is(notNullValue())); intentException.expect(IntentException.class); List<Intent> result = compiler.compile(intent, null); assertThat(result, null); } }