| /* |
| * Copyright 2017-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.routing.fibinstaller; |
| |
| import com.google.common.collect.Sets; |
| import org.easymock.EasyMock; |
| import org.junit.Before; |
| import org.junit.Test; |
| import org.onlab.packet.Ethernet; |
| import org.onlab.packet.Ip4Prefix; |
| import org.onlab.packet.IpAddress; |
| import org.onlab.packet.IpPrefix; |
| import org.onlab.packet.MacAddress; |
| import org.onlab.packet.VlanId; |
| import org.onosproject.TestApplicationId; |
| import org.onosproject.app.ApplicationService; |
| import org.onosproject.cfg.ComponentConfigService; |
| import org.onosproject.core.ApplicationId; |
| import org.onosproject.core.CoreService; |
| import org.onosproject.incubator.net.intf.Interface; |
| import org.onosproject.incubator.net.intf.InterfaceListener; |
| import org.onosproject.incubator.net.intf.InterfaceService; |
| import org.onosproject.incubator.net.intf.InterfaceServiceAdapter; |
| import org.onosproject.incubator.net.routing.ResolvedRoute; |
| import org.onosproject.incubator.net.routing.RouteEvent; |
| import org.onosproject.incubator.net.routing.RouteListener; |
| import org.onosproject.incubator.net.routing.RouteServiceAdapter; |
| import org.onosproject.net.ConnectPoint; |
| import org.onosproject.net.DeviceId; |
| import org.onosproject.net.PortNumber; |
| import org.onosproject.net.config.NetworkConfigListener; |
| import org.onosproject.net.config.NetworkConfigRegistry; |
| import org.onosproject.net.config.NetworkConfigService; |
| import org.onosproject.net.device.DeviceListener; |
| import org.onosproject.net.device.DeviceService; |
| import org.onosproject.net.device.DeviceServiceAdapter; |
| import org.onosproject.net.flow.DefaultTrafficSelector; |
| import org.onosproject.net.flow.DefaultTrafficTreatment; |
| import org.onosproject.net.flow.TrafficSelector; |
| import org.onosproject.net.flow.TrafficTreatment; |
| import org.onosproject.net.flowobjective.DefaultForwardingObjective; |
| import org.onosproject.net.flowobjective.DefaultNextObjective; |
| import org.onosproject.net.flowobjective.FlowObjectiveService; |
| import org.onosproject.net.flowobjective.ForwardingObjective; |
| import org.onosproject.net.flowobjective.NextObjective; |
| import org.onosproject.net.host.InterfaceIpAddress; |
| import org.onosproject.routing.RoutingService; |
| import org.onosproject.routing.config.RouterConfig; |
| import org.osgi.service.component.ComponentContext; |
| |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.List; |
| import java.util.Set; |
| |
| import static org.easymock.EasyMock.anyObject; |
| import static org.easymock.EasyMock.anyString; |
| import static org.easymock.EasyMock.createMock; |
| import static org.easymock.EasyMock.createNiceMock; |
| import static org.easymock.EasyMock.eq; |
| import static org.easymock.EasyMock.expect; |
| import static org.easymock.EasyMock.expectLastCall; |
| import static org.easymock.EasyMock.replay; |
| import static org.easymock.EasyMock.reset; |
| import static org.easymock.EasyMock.verify; |
| |
| /** |
| * Unit tests for SingleSwitchFibInstaller. |
| */ |
| public class FibInstallerTest { |
| |
| private static final DeviceId DEVICE_ID = DeviceId.deviceId("of:0000000000000001"); |
| |
| private static final ConnectPoint SW1_ETH1 = new ConnectPoint( |
| DEVICE_ID, PortNumber.portNumber(1)); |
| |
| private static final ConnectPoint SW1_ETH2 = new ConnectPoint( |
| DEVICE_ID, PortNumber.portNumber(2)); |
| |
| private static final int NEXT_ID = 11; |
| |
| private static final VlanId VLAN1 = VlanId.vlanId((short) 1); |
| private static final MacAddress MAC1 = MacAddress.valueOf("00:00:00:00:00:01"); |
| private static final MacAddress MAC2 = MacAddress.valueOf("00:00:00:00:00:02"); |
| |
| private static final IpPrefix PREFIX1 = Ip4Prefix.valueOf("1.1.1.0/24"); |
| private static final IpAddress NEXT_HOP1 = IpAddress.valueOf("192.168.10.1"); |
| private static final IpAddress NEXT_HOP2 = IpAddress.valueOf("192.168.20.1"); |
| private static final InterfaceIpAddress INTF1 = |
| InterfaceIpAddress.valueOf("192.168.10.2/24"); |
| private static final InterfaceIpAddress INTF2 = |
| InterfaceIpAddress.valueOf("192.168.20.2/24"); |
| |
| private final Set<Interface> interfaces = Sets.newHashSet(); |
| private InterfaceService interfaceService; |
| private NetworkConfigService networkConfigService; |
| private NetworkConfigRegistry networkConfigRegistry; |
| private FlowObjectiveService flowObjectiveService; |
| private ApplicationService applicationService; |
| private DeviceService deviceService; |
| private static final ApplicationId APPID = TestApplicationId.create("foo"); |
| |
| private RouteListener routeListener; |
| private DeviceListener deviceListener; |
| |
| private RouterConfig routerConfig; |
| private FibInstaller sSfibInstaller; |
| private InterfaceListener interfaceListener; |
| |
| @Before |
| public void setUp() throws Exception { |
| sSfibInstaller = new FibInstaller(); |
| |
| sSfibInstaller.componentConfigService = createNiceMock(ComponentConfigService.class); |
| |
| ComponentContext mockContext = createNiceMock(ComponentContext.class); |
| |
| routerConfig = new TestRouterConfig(); |
| interfaceService = createMock(InterfaceService.class); |
| |
| networkConfigService = createMock(NetworkConfigService.class); |
| networkConfigService.addListener(anyObject(NetworkConfigListener.class)); |
| expectLastCall().anyTimes(); |
| networkConfigRegistry = createMock(NetworkConfigRegistry.class); |
| flowObjectiveService = createMock(FlowObjectiveService.class); |
| applicationService = createNiceMock(ApplicationService.class); |
| replay(applicationService); |
| deviceService = new TestDeviceService(); |
| CoreService coreService = createNiceMock(CoreService.class); |
| expect(coreService.registerApplication(anyString())).andReturn(APPID).anyTimes(); |
| replay(coreService); |
| |
| sSfibInstaller.networkConfigService = networkConfigService; |
| sSfibInstaller.networkConfigRegistry = networkConfigRegistry; |
| sSfibInstaller.interfaceService = interfaceService; |
| sSfibInstaller.flowObjectiveService = flowObjectiveService; |
| sSfibInstaller.applicationService = applicationService; |
| sSfibInstaller.coreService = coreService; |
| sSfibInstaller.routeService = new TestRouteService(); |
| sSfibInstaller.deviceService = deviceService; |
| |
| setUpNetworkConfigService(); |
| setUpInterfaceService(); |
| sSfibInstaller.activate(mockContext); |
| } |
| |
| /** |
| * Sets up InterfaceService. |
| */ |
| private void setUpInterfaceService() { |
| interfaceService.addListener(anyObject(InterfaceListener.class)); |
| expectLastCall().andDelegateTo(new TestInterfaceService()); |
| |
| // Interface with no VLAN |
| Interface sw1Eth1 = new Interface("intf1", SW1_ETH1, |
| Collections.singletonList(INTF1), MAC1, VlanId.NONE); |
| expect(interfaceService.getMatchingInterface(NEXT_HOP1)).andReturn(sw1Eth1); |
| interfaces.add(sw1Eth1); |
| |
| // Interface with a VLAN |
| Interface sw2Eth1 = new Interface("intf2", SW1_ETH2, |
| Collections.singletonList(INTF2), MAC2, VLAN1); |
| expect(interfaceService.getMatchingInterface(NEXT_HOP2)).andReturn(sw2Eth1); |
| interfaces.add(sw2Eth1); |
| |
| expect(interfaceService.getInterfaces()).andReturn(interfaces); |
| |
| replay(interfaceService); |
| } |
| |
| /* |
| * Sets up NetworkConfigService. |
| */ |
| private void setUpNetworkConfigService() { |
| expect(networkConfigService.getConfig( |
| anyObject(ApplicationId.class), eq(RoutingService.ROUTER_CONFIG_CLASS))). |
| andReturn(routerConfig); |
| replay(networkConfigService); |
| } |
| |
| /** |
| * Sets up FlowObjectiveService. |
| */ |
| private void setUpFlowObjectiveService() { |
| expect(flowObjectiveService.allocateNextId()).andReturn(NEXT_ID); |
| replay(flowObjectiveService); |
| } |
| |
| /** |
| * Creates a next objective with the given parameters. |
| * |
| * @param srcMac source MAC address |
| * @param dstMac destination MAC address |
| * @param port port number |
| * @param vlan vlan ID |
| * @param add whether to create an add objective or remove objective |
| * @return new next objective |
| */ |
| private NextObjective createNextObjective(MacAddress srcMac, |
| MacAddress dstMac, |
| PortNumber port, |
| VlanId vlan, |
| boolean add) { |
| TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder() |
| .setEthSrc(srcMac) |
| .setEthDst(dstMac); |
| TrafficSelector.Builder metabuilder = null; |
| if (!vlan.equals(VlanId.NONE)) { |
| treatment.pushVlan() |
| .setVlanId(vlan) |
| .setVlanPcp((byte) 0); |
| } else { |
| metabuilder = DefaultTrafficSelector.builder(); |
| metabuilder.matchVlanId(VlanId.vlanId(FibInstaller.ASSIGNED_VLAN)); |
| } |
| |
| treatment.setOutput(port); |
| NextObjective.Builder nextBuilder = DefaultNextObjective.builder() |
| .withId(NEXT_ID) |
| .addTreatment(treatment.build()) |
| .withType(NextObjective.Type.SIMPLE) |
| .fromApp(APPID); |
| if (metabuilder != null) { |
| nextBuilder.withMeta(metabuilder.build()); |
| } |
| |
| return add ? nextBuilder.add() : nextBuilder.remove(); |
| } |
| |
| /** |
| * Creates a new forwarding objective with the given parameters. |
| * |
| * @param prefix IP prefix |
| * @param add whether to create an add objective or a remove objective |
| * @return new forwarding objective |
| */ |
| private ForwardingObjective createForwardingObjective(IpPrefix prefix, |
| boolean add) { |
| TrafficSelector selector = DefaultTrafficSelector.builder() |
| .matchEthType(Ethernet.TYPE_IPV4) |
| .matchIPDst(prefix) |
| .build(); |
| |
| int priority = prefix.prefixLength() * 5 + 100; |
| ForwardingObjective.Builder fwdBuilder = DefaultForwardingObjective.builder() |
| .fromApp(APPID) |
| .makePermanent() |
| .withSelector(selector) |
| .withPriority(priority) |
| .withFlag(ForwardingObjective.Flag.SPECIFIC); |
| |
| if (add) { |
| fwdBuilder.nextStep(NEXT_ID); |
| } else { |
| fwdBuilder.withTreatment(DefaultTrafficTreatment.builder().build()); |
| } |
| |
| return add ? fwdBuilder.add() : fwdBuilder.remove(); |
| } |
| |
| /** |
| * Tests adding a route. |
| * |
| * We verify that the flowObjectiveService records the correct state and that the |
| * correct flow is submitted to the flowObjectiveService. |
| */ |
| @Test |
| public void testRouteAdd() { |
| ResolvedRoute resolvedRoute = new ResolvedRoute(PREFIX1, NEXT_HOP1, MAC1, SW1_ETH1); |
| |
| // Create the next objective |
| NextObjective nextObjective = createNextObjective(MAC1, MAC1, SW1_ETH1.port(), VlanId.NONE, true); |
| flowObjectiveService.next(DEVICE_ID, nextObjective); |
| |
| // Create the flow objective |
| ForwardingObjective fwd = createForwardingObjective(PREFIX1, true); |
| flowObjectiveService.forward(DEVICE_ID, fwd); |
| EasyMock.expectLastCall().once(); |
| setUpFlowObjectiveService(); |
| |
| // Send in the add event |
| RouteEvent routeEvent = new RouteEvent(RouteEvent.Type.ROUTE_ADDED, resolvedRoute); |
| routeListener.event(routeEvent); |
| verify(flowObjectiveService); |
| } |
| |
| /** |
| * Tests adding a route with to a next hop in a VLAN. |
| * |
| * We verify that the flowObjectiveService records the correct state and that the |
| * correct flowObjectiveService is submitted to the flowObjectiveService. |
| */ |
| @Test |
| public void testRouteAddWithVlan() { |
| ResolvedRoute route = new ResolvedRoute(PREFIX1, NEXT_HOP2, MAC2, SW1_ETH2); |
| |
| // Create the next objective |
| NextObjective nextObjective = createNextObjective(MAC2, MAC2, SW1_ETH2.port(), VLAN1, true); |
| flowObjectiveService.next(DEVICE_ID, nextObjective); |
| |
| // Create the flow objective |
| ForwardingObjective fwd = createForwardingObjective(PREFIX1, true); |
| flowObjectiveService.forward(DEVICE_ID, fwd); |
| EasyMock.expectLastCall().once(); |
| setUpFlowObjectiveService(); |
| |
| // Send in the add event |
| routeListener.event(new RouteEvent(RouteEvent.Type.ROUTE_ADDED, route)); |
| |
| verify(flowObjectiveService); |
| } |
| |
| /** |
| * Tests updating a route. |
| * |
| * We verify that the flowObjectiveService records the correct state and that the |
| * correct flow is submitted to the flowObjectiveService. |
| */ |
| @Test |
| public void testRouteUpdate() { |
| // Firstly add a route |
| testRouteAdd(); |
| reset(flowObjectiveService); |
| |
| ResolvedRoute oldRoute = new ResolvedRoute(PREFIX1, NEXT_HOP1, MAC1, SW1_ETH1); |
| ResolvedRoute route = new ResolvedRoute(PREFIX1, NEXT_HOP2, MAC2, SW1_ETH2); |
| |
| // Create the next objective |
| NextObjective nextObjective = createNextObjective(MAC2, MAC2, SW1_ETH2.port(), VLAN1, true); |
| flowObjectiveService.next(DEVICE_ID, nextObjective); |
| |
| // Create the flow objective |
| ForwardingObjective fwd = createForwardingObjective(PREFIX1, true); |
| flowObjectiveService.forward(DEVICE_ID, fwd); |
| EasyMock.expectLastCall().once(); |
| setUpFlowObjectiveService(); |
| |
| // Send in the update event |
| routeListener.event(new RouteEvent(RouteEvent.Type.ROUTE_UPDATED, route, oldRoute)); |
| |
| verify(flowObjectiveService); |
| } |
| |
| /** |
| * Tests deleting a route. |
| * |
| * We verify that the flowObjectiveService records the correct state and that the |
| * correct flow is withdrawn from the flowObjectiveService. |
| */ |
| @Test |
| public void testRouteDelete() { |
| // Firstly add a route |
| testRouteAdd(); |
| |
| // Construct the existing route |
| ResolvedRoute route = new ResolvedRoute(PREFIX1, NEXT_HOP1, MAC1, SW1_ETH1); |
| |
| // Create the flow objective |
| reset(flowObjectiveService); |
| ForwardingObjective fwd = createForwardingObjective(PREFIX1, false); |
| flowObjectiveService.forward(DEVICE_ID, fwd); |
| replay(flowObjectiveService); |
| |
| // Send in the delete event |
| routeListener.event(new RouteEvent(RouteEvent.Type.ROUTE_REMOVED, route)); |
| |
| verify(flowObjectiveService); |
| } |
| |
| private class TestInterfaceService extends InterfaceServiceAdapter { |
| @Override |
| public void addListener(InterfaceListener listener) { |
| interfaceListener = listener; |
| } |
| } |
| |
| private class TestRouteService extends RouteServiceAdapter { |
| @Override |
| public void addListener(RouteListener listener) { |
| FibInstallerTest.this.routeListener = listener; |
| } |
| } |
| |
| private class TestRouterConfig extends RouterConfig { |
| |
| @Override |
| public List<String> getInterfaces() { |
| ArrayList<String> interfaces = new ArrayList<>(); |
| interfaces.add("of:0000000000000001/1"); |
| interfaces.add("of:0000000000000001/2"); |
| return interfaces; |
| } |
| |
| @Override |
| public ConnectPoint getControlPlaneConnectPoint() { |
| return SW1_ETH1; |
| } |
| |
| @Override |
| public boolean getOspfEnabled() { |
| return true; |
| } |
| } |
| |
| private class TestDeviceService extends DeviceServiceAdapter { |
| |
| @Override |
| public boolean isAvailable(DeviceId deviceId) { |
| return true; |
| } |
| |
| @Override |
| public void addListener(DeviceListener listener) { |
| FibInstallerTest.this.deviceListener = listener; |
| } |
| } |
| } |