blob: a93ac8df4300135e11f4506bf2f439bc2b6457c5 [file] [log] [blame]
/*
* Copyright 2021-present Open Networking Foundation
*
* 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.opencord.olt.impl;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
import org.onlab.packet.ChassisId;
import org.onlab.packet.EthType;
import org.onlab.packet.IPv4;
import org.onlab.packet.IPv6;
import org.onlab.packet.MacAddress;
import org.onlab.packet.TpPort;
import org.onlab.packet.VlanId;
import org.onosproject.cfg.ComponentConfigAdapter;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreServiceAdapter;
import org.onosproject.core.DefaultApplicationId;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.DefaultAnnotations;
import org.onosproject.net.DefaultDevice;
import org.onosproject.net.DefaultHost;
import org.onosproject.net.DefaultPort;
import org.onosproject.net.Device;
import org.onosproject.net.DeviceId;
import org.onosproject.net.Host;
import org.onosproject.net.HostId;
import org.onosproject.net.HostLocation;
import org.onosproject.net.Port;
import org.onosproject.net.PortNumber;
import org.onosproject.net.device.DeviceService;
import org.onosproject.net.flow.DefaultFlowRule;
import org.onosproject.net.flow.DefaultTrafficSelector;
import org.onosproject.net.flow.DefaultTrafficTreatment;
import org.onosproject.net.flow.FlowRule;
import org.onosproject.net.flow.FlowRuleEvent;
import org.onosproject.net.flow.FlowRuleService;
import org.onosproject.net.flow.TrafficSelector;
import org.onosproject.net.flow.TrafficTreatment;
import org.onosproject.net.flow.criteria.Criteria;
import org.onosproject.net.flowobjective.DefaultFilteringObjective;
import org.onosproject.net.flowobjective.DefaultForwardingObjective;
import org.onosproject.net.flowobjective.FilteringObjective;
import org.onosproject.net.flowobjective.FlowObjectiveService;
import org.onosproject.net.flowobjective.ForwardingObjective;
import org.onosproject.net.host.HostService;
import org.onosproject.net.meter.MeterId;
import org.onosproject.net.provider.ProviderId;
import org.onosproject.store.service.TestStorageService;
import org.opencord.olt.AccessDevicePort;
import org.opencord.olt.DiscoveredSubscriber;
import org.opencord.olt.OltPortStatus;
import org.opencord.olt.ServiceKey;
import org.opencord.olt.FlowOperation;
import org.opencord.olt.impl.fttb.FttbUtils;
import org.opencord.sadis.BaseInformationService;
import org.opencord.sadis.SadisService;
import org.opencord.sadis.SubscriberAndDeviceInformation;
import org.opencord.sadis.UniTagInformation;
import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.argThat;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.onosproject.net.AnnotationKeys.PORT_NAME;
import static org.opencord.olt.OltFlowsStatus.ERROR;
import static org.opencord.olt.OltFlowsStatus.NONE;
import static org.opencord.olt.OltFlowsStatus.ADDED;
import static org.opencord.olt.OltFlowsStatus.PENDING_ADD;
import static org.opencord.olt.OltFlowsStatus.PENDING_REMOVE;
import static org.opencord.olt.OltFlowsStatus.REMOVED;
import static org.opencord.olt.impl.OsgiPropertyConstants.DEFAULT_BP_ID_DEFAULT;
import static org.opencord.olt.impl.OsgiPropertyConstants.DEFAULT_MCAST_SERVICE_NAME;
import static org.opencord.olt.impl.OsgiPropertyConstants.DEFAULT_MCAST_SERVICE_NAME_DEFAULT;
import static org.opencord.olt.impl.OsgiPropertyConstants.DOWNSTREAM_OLT;
import static org.opencord.olt.impl.OsgiPropertyConstants.DOWNSTREAM_ONU;
import static org.opencord.olt.impl.OsgiPropertyConstants.UPSTREAM_OLT;
import static org.opencord.olt.impl.OsgiPropertyConstants.UPSTREAM_ONU;
import static org.opencord.olt.impl.fttb.FttbUtils.FTTB_FLOW_DIRECTION;
import static org.opencord.olt.impl.fttb.FttbUtils.FTTB_FLOW_DOWNSTREAM;
import static org.opencord.olt.impl.fttb.FttbUtils.FTTB_FLOW_UPSTREAM;
import static org.opencord.olt.impl.fttb.FttbUtils.FTTB_SERVICE_NAME;
public class OltFlowServiceTest extends OltTestHelpers {
public static final String PORT_1 = "port-1";
public static final String PORT_2 = "port-2";
public static final String PORT_3 = "port-3";
private OltFlowService component;
private OltFlowService oltFlowService;
private DeviceService deviceService;
OltFlowService.InternalFlowListener internalFlowListener;
private final ApplicationId testAppId = new DefaultApplicationId(1, "org.opencord.olt.test");
private final short eapolDefaultVlan = 4091;
private final DeviceId deviceId = DeviceId.deviceId("test-device");
private final Device testDevice = new DefaultDevice(ProviderId.NONE, deviceId, Device.Type.OLT,
"testManufacturer", "1.0", "1.0", "SN", new ChassisId(1));
Port nniPort = new OltPort(testDevice, true, PortNumber.portNumber(1048576),
DefaultAnnotations.builder().set(PORT_NAME, "nni-1").build());
Port nniPortDisabled = new OltPort(testDevice, false, PortNumber.portNumber(1048576),
DefaultAnnotations.builder().set(PORT_NAME, "nni-1").build());
Port uniUpdateEnabled = new OltPort(testDevice, true, PortNumber.portNumber(16),
DefaultAnnotations.builder().set(PORT_NAME, "uni-1").build());
private final UniTagInformation dpuMgmtUti = new UniTagInformation.Builder()
.setPonCTag(VlanId.vlanId("6"))
.setPonSTag(VlanId.vlanId("60"))
.setUsPonCTagPriority(1)
.setUsPonSTagPriority(2)
.setTechnologyProfileId(64)
.setUpstreamBandwidthProfile("usBp")
.setUpstreamOltBandwidthProfile("usOltBp")
.setServiceName(FttbUtils.FTTB_SERVICE_DPU_MGMT_TRAFFIC)
.setIsDhcpRequired(true)
.setEnableMacLearning(true)
.build();
private final UniTagInformation ancpUti = new UniTagInformation.Builder()
.setPonCTag(VlanId.vlanId("4"))
.setPonSTag(VlanId.vlanId("40"))
.setUsPonCTagPriority(3)
.setUsPonSTagPriority(4)
.setTechnologyProfileId(64)
.setUpstreamBandwidthProfile("usBp")
.setUpstreamOltBandwidthProfile("usOltBp")
.setServiceName(FttbUtils.FTTB_SERVICE_DPU_ANCP_TRAFFIC)
.setIsDhcpRequired(false)
.build();
private final UniTagInformation fttbSubscriberUti = new UniTagInformation.Builder()
.setPonCTag(VlanId.vlanId("8"))
.setPonSTag(VlanId.vlanId("80"))
.setTechnologyProfileId(64)
.setUpstreamBandwidthProfile("usBp")
.setUpstreamOltBandwidthProfile("usOltBp")
.setServiceName(FttbUtils.FTTB_SERVICE_SUBSCRIBER_TRAFFIC)
.setIsDhcpRequired(false)
.build();
@Before
public void setUp() {
component = new OltFlowService();
component.cfgService = new ComponentConfigAdapter();
component.sadisService = Mockito.mock(SadisService.class);
component.coreService = Mockito.spy(new CoreServiceAdapter());
component.oltMeterService = Mockito.mock(OltMeterService.class);
component.flowObjectiveService = Mockito.mock(FlowObjectiveService.class);
component.hostService = Mockito.mock(HostService.class);
component.flowRuleService = Mockito.mock(FlowRuleService.class);
component.storageService = new TestStorageService();
component.oltDeviceService = Mockito.mock(OltDeviceService.class);
component.appId = testAppId;
component.deviceService = Mockito.mock(DeviceService.class);
doReturn(Mockito.mock(BaseInformationService.class))
.when(component.sadisService).getSubscriberInfoService();
doReturn(testAppId).when(component.coreService).registerApplication("org.opencord.olt");
doReturn(testDevice).when(component.deviceService).getDevice(testDevice.id());
when(component.sadisService.getSubscriberInfoService().get(testDevice.serialNumber())).
thenReturn(Mockito.mock(SubscriberAndDeviceInformation.class));
when(component.oltDeviceService.getNniPort(testDevice)).thenReturn(Optional.of(nniPort));
component.activate(null);
component.bindSadisService(component.sadisService);
internalFlowListener = spy(component.internalFlowListener);
oltFlowService = spy(component);
deviceService = Mockito.mock(DeviceService.class);
}
@After
public void tearDown() {
component.deactivate(null);
}
@Test
public void testUpdateConnectPointStatus() {
DeviceId deviceId = DeviceId.deviceId("test-device");
ProviderId pid = new ProviderId("of", "foo");
Device device =
new DefaultDevice(pid, deviceId, Device.Type.OLT, "", "", "", "", null);
Port port1 = new DefaultPort(device, PortNumber.portNumber(1), true,
DefaultAnnotations.builder().set(PORT_NAME, PORT_1).build());
Port port2 = new DefaultPort(device, PortNumber.portNumber(2), true,
DefaultAnnotations.builder().set(PORT_NAME, PORT_2).build());
Port port3 = new DefaultPort(device, PortNumber.portNumber(3), true,
DefaultAnnotations.builder().set(PORT_NAME, PORT_3).build());
ServiceKey sk1 = new ServiceKey(new AccessDevicePort(port1), new UniTagInformation());
ServiceKey sk2 = new ServiceKey(new AccessDevicePort(port2), new UniTagInformation());
ServiceKey sk3 = new ServiceKey(new AccessDevicePort(port3), new UniTagInformation());
// cpStatus map for the test
component.cpStatus = component.storageService.
<ServiceKey, OltPortStatus>consistentMapBuilder().build().asJavaMap();
OltPortStatus cp1Status = new OltPortStatus(PENDING_ADD, NONE, NONE, NONE, NONE);
component.cpStatus.put(sk1, cp1Status);
//check that we only update the provided value
component.updateConnectPointStatus(sk1, ADDED, null, null, null, null);
OltPortStatus updated = component.cpStatus.get(sk1);
Assert.assertEquals(ADDED, updated.defaultEapolStatus);
Assert.assertEquals(NONE, updated.subscriberFlowsStatus);
Assert.assertEquals(NONE, updated.dhcpStatus);
// check that it creates an entry if it does not exist
component.updateConnectPointStatus(sk2, PENDING_ADD, NONE, NONE, NONE, NONE);
Assert.assertNotNull(component.cpStatus.get(sk2));
// check that if we create a new entry with null values they're converted to NONE
component.updateConnectPointStatus(sk3, null, null, null, null, null);
updated = component.cpStatus.get(sk3);
Assert.assertEquals(NONE, updated.defaultEapolStatus);
Assert.assertEquals(NONE, updated.subscriberFlowsStatus);
Assert.assertEquals(NONE, updated.dhcpStatus);
}
/**
* If the flow status is PENDING_REMOVE or ERROR and there is no
* previous state in the map that don't update it.
* In case of a device disconnection we immediately wipe out the status,
* but then flows might update the cpStatus map. That result
*/
@Test
public void doNotUpdateConnectPointStatus() {
DeviceId deviceId = DeviceId.deviceId("test-device");
ProviderId pid = new ProviderId("of", "foo");
Device device =
new DefaultDevice(pid, deviceId, Device.Type.OLT, "", "", "", "", null);
Port port1 = new DefaultPort(device, PortNumber.portNumber(1), true,
DefaultAnnotations.builder().set(PORT_NAME, PORT_1).build());
ServiceKey sk1 = new ServiceKey(new AccessDevicePort(port1), new UniTagInformation());
// cpStatus map for the test
component.cpStatus = component.storageService.
<ServiceKey, OltPortStatus>consistentMapBuilder().build().asJavaMap();
// check that an entry is not created if the only status is pending remove
component.updateConnectPointStatus(sk1, null, null, null, PENDING_REMOVE, null);
OltPortStatus entry = component.cpStatus.get(sk1);
Assert.assertNull(entry);
// check that an entry is not created if the only status is ERROR
component.updateConnectPointStatus(sk1, null, null, null, ERROR, null);
entry = component.cpStatus.get(sk1);
Assert.assertNull(entry);
}
@Test
public void testHasDefaultEapol() {
DeviceId deviceId = DeviceId.deviceId("test-device");
ProviderId pid = new ProviderId("of", "foo");
Device device =
new DefaultDevice(pid, deviceId, Device.Type.OLT, "", "", "", "", null);
Port port = new DefaultPort(device, PortNumber.portNumber(16), true,
DefaultAnnotations.builder().set(PORT_NAME, "name-1").build());
ServiceKey skWithStatus = new ServiceKey(new AccessDevicePort(port),
component.defaultEapolUniTag);
Port port17 = new DefaultPort(device, PortNumber.portNumber(17), true,
DefaultAnnotations.builder().set(PORT_NAME, "name-1").build());
OltPortStatus portStatusAdded = new OltPortStatus(
ADDED,
NONE,
null,
null,
null
);
OltPortStatus portStatusRemoved = new OltPortStatus(
REMOVED,
NONE,
null,
null,
null
);
component.cpStatus.put(skWithStatus, portStatusAdded);
Assert.assertTrue(component.hasDefaultEapol(port));
component.cpStatus.put(skWithStatus, portStatusRemoved);
Assert.assertFalse(component.hasDefaultEapol(port17));
}
@Test
public void testHasSubscriberFlows() {
// TODO test with multiple services
DeviceId deviceId = DeviceId.deviceId("test-device");
ProviderId pid = new ProviderId("of", "foo");
Device device =
new DefaultDevice(pid, deviceId, Device.Type.OLT, "", "", "", "", null);
Port port = new DefaultPort(device, PortNumber.portNumber(16), true,
DefaultAnnotations.builder().set(PORT_NAME, "name-1").build());
UniTagInformation uti = new UniTagInformation.Builder().setServiceName("test").build();
ServiceKey skWithStatus = new ServiceKey(new AccessDevicePort(port),
uti);
OltPortStatus withDefaultEapol = new OltPortStatus(
ADDED,
NONE,
NONE,
NONE,
NONE
);
OltPortStatus withDhcp = new OltPortStatus(
REMOVED,
NONE,
NONE,
ADDED,
NONE
);
OltPortStatus withSubFlow = new OltPortStatus(
REMOVED,
ADDED,
ADDED,
ADDED,
NONE
);
component.cpStatus.put(skWithStatus, withDefaultEapol);
Assert.assertFalse(component.hasSubscriberFlows(port, uti));
component.cpStatus.put(skWithStatus, withDhcp);
Assert.assertTrue(component.hasDhcpFlows(port, uti));
component.cpStatus.put(skWithStatus, withSubFlow);
Assert.assertTrue(component.hasSubscriberFlows(port, uti));
}
@Test
public void testHandleBasicPortFlowsNoEapol() throws Exception {
component.enableEapol = false;
// create empty service for testing
List<UniTagInformation> uniTagInformationList = new LinkedList<>();
UniTagInformation empty = new UniTagInformation.Builder().build();
uniTagInformationList.add(empty);
SubscriberAndDeviceInformation si = new SubscriberAndDeviceInformation();
si.setUniTagList(uniTagInformationList);
final DiscoveredSubscriber addedSub =
new DiscoveredSubscriber(testDevice,
uniUpdateEnabled, DiscoveredSubscriber.Status.ADDED,
false, si);
component.handleBasicPortFlows(addedSub, DEFAULT_BP_ID_DEFAULT, DEFAULT_BP_ID_DEFAULT);
// if eapol is not enabled there's nothing we need to do,
// so make sure we don't even call sadis
verify(component.subsService, never()).get(any());
}
@Test
public void testHandleBasicPortFlowsWithEapolNoMeter() throws Exception {
// create empty service for testing
List<UniTagInformation> uniTagInformationList = new LinkedList<>();
UniTagInformation empty = new UniTagInformation.Builder().build();
uniTagInformationList.add(empty);
SubscriberAndDeviceInformation si = new SubscriberAndDeviceInformation();
si.setUniTagList(uniTagInformationList);
final DiscoveredSubscriber addedSub =
new DiscoveredSubscriber(testDevice,
uniUpdateEnabled, DiscoveredSubscriber.Status.ADDED,
false, si);
// whether the meter is pending or not is up to the createMeter method to handle
// we just don't proceed with the subscriber till it's ready
doReturn(false).when(component.oltMeterService)
.createMeter(addedSub.device.id(), DEFAULT_BP_ID_DEFAULT);
boolean res = component.handleBasicPortFlows(addedSub, DEFAULT_BP_ID_DEFAULT, DEFAULT_BP_ID_DEFAULT);
Assert.assertFalse(res);
// we do not create flows
verify(component.flowObjectiveService, never())
.filter(eq(addedSub.device.id()), any());
}
@Test
public void testHandleBasicPortFlowsWithEapolAddedMeter() throws Exception {
// create empty service for testing
List<UniTagInformation> uniTagInformationList = new LinkedList<>();
UniTagInformation empty = new UniTagInformation.Builder().build();
uniTagInformationList.add(empty);
SubscriberAndDeviceInformation si = new SubscriberAndDeviceInformation();
si.setUniTagList(uniTagInformationList);
final DiscoveredSubscriber addedSub =
new DiscoveredSubscriber(testDevice,
uniUpdateEnabled, DiscoveredSubscriber.Status.ADDED,
false, si);
// this is the happy case, we have the meter so we check that the default EAPOL flow
// is installed
doReturn(true).when(component.oltMeterService)
.createMeter(deviceId, DEFAULT_BP_ID_DEFAULT);
doReturn(true).when(component.oltMeterService)
.hasMeterByBandwidthProfile(deviceId, DEFAULT_BP_ID_DEFAULT);
doReturn(MeterId.meterId(1)).when(component.oltMeterService)
.getMeterIdForBandwidthProfile(deviceId, DEFAULT_BP_ID_DEFAULT);
FilteringObjective expectedFilter = DefaultFilteringObjective.builder()
.permit()
.withKey(Criteria.matchInPort(uniUpdateEnabled.number()))
.addCondition(Criteria.matchEthType(EthType.EtherType.EAPOL.ethType()))
.fromApp(testAppId)
.withPriority(10000)
.withMeta(
DefaultTrafficTreatment.builder()
.meter(MeterId.meterId(1))
.writeMetadata(OltFlowServiceUtils.createTechProfValueForWriteMetadata(
VlanId.NONE,
component.defaultTechProfileId, MeterId.meterId(1)), 0)
.setOutput(PortNumber.CONTROLLER)
.pushVlan()
.setVlanId(VlanId.vlanId(eapolDefaultVlan)).build()
)
.add();
component.handleBasicPortFlows(addedSub, DEFAULT_BP_ID_DEFAULT, DEFAULT_BP_ID_DEFAULT);
// we check for an existing meter (present)
// FIXME understand why the above test invokes this call and this one doesn't
// verify(oltFlowService.oltMeterService, times(1))
// .hasMeterByBandwidthProfile(eq(addedSub.device.id()), eq(DEFAULT_BP_ID_DEFAULT));
// the meter exist, no need to check for PENDING or to create it
verify(component.oltMeterService, never())
.hasPendingMeterByBandwidthProfile(eq(deviceId), eq(DEFAULT_BP_ID_DEFAULT));
verify(component.oltMeterService, never())
.createMeterForBp(eq(deviceId), eq(DEFAULT_BP_ID_DEFAULT));
verify(component.flowObjectiveService, times(1))
.filter(eq(deviceId), argThat(new FilteringObjectiveMatcher(expectedFilter)));
}
@Test
public void testHandleBasicPortFlowsRemovedSub() throws Exception {
// create empty service for testing
List<UniTagInformation> uniTagInformationList = new LinkedList<>();
UniTagInformation empty = new UniTagInformation.Builder().build();
uniTagInformationList.add(empty);
SubscriberAndDeviceInformation si = new SubscriberAndDeviceInformation();
si.setUniTagList(uniTagInformationList);
final DiscoveredSubscriber removedSub =
new DiscoveredSubscriber(testDevice,
uniUpdateEnabled, DiscoveredSubscriber.Status.REMOVED,
false, si);
// we are testing that when a port goes down we remove the default EAPOL flow
doReturn(MeterId.meterId(1)).when(component.oltMeterService)
.getMeterIdForBandwidthProfile(deviceId, DEFAULT_BP_ID_DEFAULT);
FilteringObjective expectedFilter = DefaultFilteringObjective.builder()
.deny()
.withKey(Criteria.matchInPort(uniUpdateEnabled.number()))
.addCondition(Criteria.matchEthType(EthType.EtherType.EAPOL.ethType()))
.fromApp(testAppId)
.withPriority(10000)
.withMeta(
DefaultTrafficTreatment.builder()
.meter(MeterId.meterId(1))
.writeMetadata(OltFlowServiceUtils.createTechProfValueForWriteMetadata(
VlanId.NONE,
component.defaultTechProfileId, MeterId.meterId(1)), 0)
.setOutput(PortNumber.CONTROLLER)
.pushVlan()
.setVlanId(VlanId.vlanId(eapolDefaultVlan)).build()
)
.add();
component.handleBasicPortFlows(removedSub, DEFAULT_BP_ID_DEFAULT, DEFAULT_BP_ID_DEFAULT);
verify(component.flowObjectiveService, times(1))
.filter(eq(deviceId), argThat(new FilteringObjectiveMatcher(expectedFilter)));
}
@Test
public void testHandleNniFlowsOnlyLldp() {
component.enableDhcpOnNni = false;
component.handleNniFlows(testDevice, nniPort, FlowOperation.ADD);
FilteringObjective expectedFilter = DefaultFilteringObjective.builder()
.permit()
.withKey(Criteria.matchInPort(nniPort.number()))
.addCondition(Criteria.matchEthType(EthType.EtherType.LLDP.ethType()))
.fromApp(testAppId)
.withPriority(10000)
.withMeta(DefaultTrafficTreatment.builder().setOutput(PortNumber.CONTROLLER).build())
.add();
verify(component.flowObjectiveService, times(1))
.filter(eq(deviceId), argThat(new FilteringObjectiveMatcher(expectedFilter)));
verify(component.flowObjectiveService, times(1))
.filter(eq(deviceId), any());
}
@Test
public void testHandleNniFlowsDhcpV4() {
component.enableDhcpOnNni = true;
component.enableDhcpV4 = true;
component.handleNniFlows(testDevice, nniPort, FlowOperation.ADD);
FilteringObjective expectedFilter = DefaultFilteringObjective.builder()
.permit()
.withKey(Criteria.matchInPort(nniPort.number()))
.addCondition(Criteria.matchEthType(EthType.EtherType.IPV4.ethType()))
.addCondition(Criteria.matchIPProtocol(IPv4.PROTOCOL_UDP))
.addCondition(Criteria.matchUdpSrc(TpPort.tpPort(67)))
.addCondition(Criteria.matchUdpDst(TpPort.tpPort(68)))
.fromApp(testAppId)
.withPriority(10000)
.withMeta(DefaultTrafficTreatment.builder().setOutput(PortNumber.CONTROLLER).build())
.add();
// invoked with the correct DHCP filtering objective
verify(component.flowObjectiveService, times(1))
.filter(eq(deviceId), argThat(new FilteringObjectiveMatcher(expectedFilter)));
// invoked only twice, LLDP and DHCP
verify(component.flowObjectiveService, times(2))
.filter(eq(deviceId), any());
}
@Test
public void testRemoveNniFlowsDhcpV4() {
component.enableDhcpOnNni = true;
component.enableDhcpV4 = true;
component.handleNniFlows(testDevice, nniPortDisabled, FlowOperation.REMOVE);
FilteringObjective expectedFilter = DefaultFilteringObjective.builder()
.deny()
.withKey(Criteria.matchInPort(nniPort.number()))
.addCondition(Criteria.matchEthType(EthType.EtherType.IPV4.ethType()))
.addCondition(Criteria.matchIPProtocol(IPv4.PROTOCOL_UDP))
.addCondition(Criteria.matchUdpSrc(TpPort.tpPort(67)))
.addCondition(Criteria.matchUdpDst(TpPort.tpPort(68)))
.fromApp(testAppId)
.withPriority(10000)
.withMeta(DefaultTrafficTreatment.builder().setOutput(PortNumber.CONTROLLER).build())
.add();
// invoked with the correct DHCP filtering objective
verify(component.flowObjectiveService, times(1))
.filter(eq(deviceId), argThat(new FilteringObjectiveMatcher(expectedFilter)));
// invoked only twice, LLDP and DHCP
verify(component.flowObjectiveService, times(2))
.filter(eq(deviceId), any());
}
@Test
public void testHandleNniFlowsDhcpV6() {
component.enableDhcpOnNni = true;
component.enableDhcpV4 = false;
component.enableDhcpV6 = true;
component.handleNniFlows(testDevice, nniPort, FlowOperation.ADD);
FilteringObjective expectedFilter = DefaultFilteringObjective.builder()
.permit()
.withKey(Criteria.matchInPort(nniPort.number()))
.addCondition(Criteria.matchEthType(EthType.EtherType.IPV6.ethType()))
.addCondition(Criteria.matchIPProtocol(IPv6.PROTOCOL_UDP))
.addCondition(Criteria.matchUdpSrc(TpPort.tpPort(546)))
.addCondition(Criteria.matchUdpDst(TpPort.tpPort(547)))
.fromApp(testAppId)
.withPriority(10000)
.withMeta(DefaultTrafficTreatment.builder().setOutput(PortNumber.CONTROLLER).build())
.add();
// invoked with the correct DHCP filtering objective
verify(component.flowObjectiveService, times(1))
.filter(eq(deviceId), argThat(new FilteringObjectiveMatcher(expectedFilter)));
// invoked only twice, LLDP and DHCP
verify(component.flowObjectiveService, times(2))
.filter(eq(deviceId), any());
}
@Test
public void testHandleNniFlowsIgmp() {
component.enableDhcpOnNni = false;
component.enableIgmpOnNni = true;
component.handleNniFlows(testDevice, nniPort, FlowOperation.ADD);
FilteringObjective expectedFilter = DefaultFilteringObjective.builder()
.permit()
.withKey(Criteria.matchInPort(nniPort.number()))
.addCondition(Criteria.matchEthType(EthType.EtherType.IPV4.ethType()))
.addCondition(Criteria.matchIPProtocol(IPv4.PROTOCOL_IGMP))
.fromApp(testAppId)
.withPriority(10000)
.add();
// invoked with the correct DHCP filtering objective
verify(component.flowObjectiveService, times(1))
.filter(eq(deviceId), argThat(new FilteringObjectiveMatcher(expectedFilter)));
// invoked only twice, LLDP and DHCP
verify(component.flowObjectiveService, times(2))
.filter(eq(deviceId), any());
}
@Test
public void testHandleNniFlowsPppoe() {
component.enableDhcpOnNni = false;
component.enablePppoeOnNni = true;
component.enablePppoe = true;
component.handleNniFlows(testDevice, nniPort, FlowOperation.ADD);
FilteringObjective expectedFilter = DefaultFilteringObjective.builder()
.permit()
.withKey(Criteria.matchInPort(nniPort.number()))
.addCondition(Criteria.matchEthType(EthType.EtherType.PPPoED.ethType()))
.fromApp(testAppId)
.withPriority(10000)
.withMeta(DefaultTrafficTreatment.builder().setOutput(PortNumber.CONTROLLER).build())
.add();
// invoked with the correct Pppoe filtering objective
verify(component.flowObjectiveService, times(1))
.filter(eq(deviceId), argThat(new FilteringObjectiveMatcher(expectedFilter)));
// invoked only twice, LLDP and DHCP
verify(component.flowObjectiveService, times(2))
.filter(eq(deviceId), any());
}
@Test
public void testRemoveNniFlowsPppoe() {
component.enableDhcpOnNni = false;
component.enablePppoeOnNni = true;
component.enablePppoe = true;
component.handleNniFlows(testDevice, nniPortDisabled, FlowOperation.REMOVE);
FilteringObjective expectedFilter = DefaultFilteringObjective.builder()
.deny()
.withKey(Criteria.matchInPort(nniPort.number()))
.addCondition(Criteria.matchEthType(EthType.EtherType.PPPoED.ethType()))
.fromApp(testAppId)
.withPriority(10000)
.withMeta(DefaultTrafficTreatment.builder().setOutput(PortNumber.CONTROLLER).build())
.add();
// invoked with the correct Pppoe filtering objective
verify(component.flowObjectiveService, times(1))
.filter(eq(deviceId), argThat(new FilteringObjectiveMatcher(expectedFilter)));
// invoked only twice, LLDP and DHCP
verify(component.flowObjectiveService, times(2))
.filter(eq(deviceId), any());
}
@Test
public void testMacAddressNotRequired() {
// create a single service that doesn't require mac address
List<UniTagInformation> uniTagInformationList = new LinkedList<>();
UniTagInformation hsia = new UniTagInformation.Builder()
.setEnableMacLearning(false)
.build();
uniTagInformationList.add(hsia);
SubscriberAndDeviceInformation si = new SubscriberAndDeviceInformation();
si.setUniTagList(uniTagInformationList);
boolean isMacAvailable = component.isMacAddressAvailable(testDevice.id(), uniUpdateEnabled, si);
// we return true as we don't care wether it's available or not
Assert.assertTrue(isMacAvailable);
}
@Test
public void testIsMacAddressAvailableViaMacLearning() {
// create a single service that requires macLearning to be enabled
List<UniTagInformation> uniTagInformationList = new LinkedList<>();
VlanId hsiaCtag = VlanId.vlanId((short) 11);
UniTagInformation hsia = new UniTagInformation.Builder()
.setPonCTag(hsiaCtag)
.setEnableMacLearning(true).build();
uniTagInformationList.add(hsia);
SubscriberAndDeviceInformation si = new SubscriberAndDeviceInformation();
si.setUniTagList(uniTagInformationList);
// with no hosts discovered, return false
boolean isMacAvailable = component.isMacAddressAvailable(testDevice.id(), uniUpdateEnabled, si);
Assert.assertFalse(isMacAvailable);
// with a discovered host, return true
Host fakeHost = new DefaultHost(ProviderId.NONE, HostId.hostId(MacAddress.NONE), MacAddress.ZERO,
hsiaCtag, HostLocation.NONE, new HashSet<>(), DefaultAnnotations.builder().build());
Set<Host> hosts = new HashSet<>(Arrays.asList(fakeHost));
doReturn(hosts).when(component.hostService).getConnectedHosts((ConnectPoint) any());
isMacAvailable = component.isMacAddressAvailable(testDevice.id(), uniUpdateEnabled, si);
Assert.assertTrue(isMacAvailable);
}
@Test
public void testIsMacAddressAvailableViaConfiguration() {
// create a single service that has a macAddress configured
List<UniTagInformation> uniTagInformationList = new LinkedList<>();
UniTagInformation hsia = new UniTagInformation.Builder()
.setConfiguredMacAddress("2e:0a:00:01:00:00")
.build();
uniTagInformationList.add(hsia);
SubscriberAndDeviceInformation si = new SubscriberAndDeviceInformation();
si.setUniTagList(uniTagInformationList);
boolean isMacAvailable = component.isMacAddressAvailable(testDevice.id(), uniUpdateEnabled, si);
Assert.assertTrue(isMacAvailable);
}
@Test
public void testHandleSubscriberDhcpFlowsAdd() {
String usBp = "usBp";
String usOltBp = "usOltBp";
component.enableDhcpV4 = true;
// create two services, one requires DHCP the other doesn't
List<UniTagInformation> uniTagInformationList = new LinkedList<>();
VlanId hsiaCtag = VlanId.vlanId((short) 11);
UniTagInformation hsia = new UniTagInformation.Builder()
.setPonCTag(hsiaCtag)
.setTechnologyProfileId(64)
.setUniTagMatch(VlanId.vlanId(VlanId.NO_VID))
.setUpstreamBandwidthProfile(usBp)
.setUpstreamOltBandwidthProfile(usOltBp)
.setIsDhcpRequired(true).build();
UniTagInformation mc = new UniTagInformation.Builder()
.setIsDhcpRequired(false).build();
uniTagInformationList.add(hsia);
uniTagInformationList.add(mc);
SubscriberAndDeviceInformation si = new SubscriberAndDeviceInformation();
si.setUniTagList(uniTagInformationList);
final DiscoveredSubscriber addedSub =
new DiscoveredSubscriber(testDevice,
uniUpdateEnabled, DiscoveredSubscriber.Status.ADDED,
false, si);
// return meter IDs
doReturn(MeterId.meterId(2)).when(component.oltMeterService)
.getMeterIdForBandwidthProfile(addedSub.device.id(), usBp);
doReturn(MeterId.meterId(3)).when(component.oltMeterService)
.getMeterIdForBandwidthProfile(addedSub.device.id(), usOltBp);
// TODO improve the matches on the filter
FilteringObjective expectedFilter = DefaultFilteringObjective.builder()
.permit()
.withKey(Criteria.matchInPort(addedSub.port.number()))
.addCondition(Criteria.matchEthType(EthType.EtherType.IPV4.ethType()))
.addCondition(Criteria.matchIPProtocol(IPv4.PROTOCOL_UDP))
.addCondition(Criteria.matchUdpSrc(TpPort.tpPort(68)))
.addCondition(Criteria.matchUdpDst(TpPort.tpPort(67)))
.fromApp(testAppId)
.withPriority(10000)
.add();
component.handleSubscriberDhcpFlows(addedSub.device.id(), addedSub.port,
FlowOperation.ADD, si);
verify(component.flowObjectiveService, times(1))
.filter(eq(addedSub.device.id()), argThat(new FilteringObjectiveMatcher(expectedFilter)));
}
@Test
public void testHandleSubscriberPppoeFlowsAdd() {
String usBp = "usBp";
String usOltBp = "usOltBp";
component.enablePppoe = true;
// create two services, one requires Pppoe the other doesn't
List<UniTagInformation> uniTagInformationList = new LinkedList<>();
VlanId hsiaCtag = VlanId.vlanId((short) 11);
UniTagInformation hsia = new UniTagInformation.Builder()
.setPonCTag(hsiaCtag)
.setTechnologyProfileId(64)
.setUniTagMatch(VlanId.vlanId(VlanId.NO_VID))
.setUpstreamBandwidthProfile(usBp)
.setUpstreamOltBandwidthProfile(usOltBp)
.setIsPppoeRequired(true).build();
UniTagInformation mc = new UniTagInformation.Builder()
.setIsPppoeRequired(false).build();
uniTagInformationList.add(hsia);
uniTagInformationList.add(mc);
SubscriberAndDeviceInformation si = new SubscriberAndDeviceInformation();
si.setUniTagList(uniTagInformationList);
final DiscoveredSubscriber addedSub =
new DiscoveredSubscriber(testDevice,
uniUpdateEnabled, DiscoveredSubscriber.Status.ADDED,
false, si);
// return meter IDs
doReturn(MeterId.meterId(2)).when(component.oltMeterService)
.getMeterIdForBandwidthProfile(addedSub.device.id(), usBp);
doReturn(MeterId.meterId(3)).when(component.oltMeterService)
.getMeterIdForBandwidthProfile(addedSub.device.id(), usOltBp);
FilteringObjective expectedFilter = DefaultFilteringObjective.builder()
.permit()
.withKey(Criteria.matchInPort(addedSub.port.number()))
.addCondition(Criteria.matchEthType(EthType.EtherType.PPPoED.ethType()))
.fromApp(testAppId)
.withPriority(10000)
.add();
component.handleSubscriberPppoeFlows(addedSub.device.id(), addedSub.port,
FlowOperation.ADD, si);
verify(component.flowObjectiveService, times(1))
.filter(eq(addedSub.device.id()), argThat(new FilteringObjectiveMatcher(expectedFilter)));
}
@Test
public void testInternalFlowListenerNotMaster() {
doReturn(false).when(component.oltDeviceService).isLocalLeader(any());
FlowRule flowRule = DefaultFlowRule.builder()
.forDevice(DeviceId.deviceId("foo"))
.fromApp(testAppId)
.makePermanent()
.withPriority(1000)
.build();
FlowRuleEvent event = new FlowRuleEvent(FlowRuleEvent.Type.RULE_ADDED,
flowRule);
internalFlowListener.event(event);
// if we're not master of the device, we should not update
verify(internalFlowListener, never()).updateCpStatus(any(), any(), any());
}
@Test
public void testInternalFlowListenerDifferentApp() {
ApplicationId someAppId = new DefaultApplicationId(1, "org.opencord.olt.not-test");
FlowRule flowRule = DefaultFlowRule.builder()
.forDevice(DeviceId.deviceId("foo"))
.fromApp(someAppId)
.makePermanent()
.withPriority(1000)
.build();
FlowRuleEvent event = new FlowRuleEvent(FlowRuleEvent.Type.RULE_ADDED,
flowRule);
internalFlowListener.event(event);
// if we're not master of the device, we should not update
verify(internalFlowListener, never()).updateCpStatus(any(), any(), any());
}
@Test
public void testRemoveSubscriberFlows() {
// test that if we have EAPOL we wait till the tagged flow is removed
// before installing the default one
// setup
component.enableEapol = true;
// mock data
DeviceId deviceId = DeviceId.deviceId("test-device");
ProviderId pid = new ProviderId("of", "foo");
Device device =
new DefaultDevice(pid, deviceId, Device.Type.OLT, "", "", "", "", null);
Port port = new DefaultPort(device, PortNumber.portNumber(1), true,
DefaultAnnotations.builder().set(PORT_NAME, PORT_1).build());
List<UniTagInformation> uniTagInformationList = new LinkedList<>();
UniTagInformation hsia = new UniTagInformation.Builder()
.setUpstreamBandwidthProfile("usbp")
.setDownstreamBandwidthProfile("dsbp")
.setPonCTag(VlanId.vlanId((short) 900)).build();
uniTagInformationList.add(hsia);
SubscriberAndDeviceInformation si = new SubscriberAndDeviceInformation();
si.setUniTagList(uniTagInformationList);
DiscoveredSubscriber sub = new DiscoveredSubscriber(
device, port, DiscoveredSubscriber.Status.REMOVED, true, si);
// first test that when we remove the EAPOL flow we return false so that the
// subscriber is not removed from the queue
doReturn(null).when(deviceService).getPort(any());
doReturn(true).when(oltFlowService).areSubscriberFlowsPendingRemoval(any(), any(), eq(true));
boolean res = oltFlowService.removeSubscriberFlows(sub, DEFAULT_BP_ID_DEFAULT, DEFAULT_MCAST_SERVICE_NAME);
verify(oltFlowService, times(1))
.handleSubscriberDhcpFlows(deviceId, port, FlowOperation.REMOVE, si);
verify(oltFlowService, times(1))
.handleSubscriberEapolFlows(sub, FlowOperation.REMOVE, si);
verify(oltFlowService, times(1))
.handleSubscriberDataFlows(device, port, FlowOperation.REMOVE,
si, DEFAULT_MCAST_SERVICE_NAME);
verify(oltFlowService, times(1))
.handleSubscriberIgmpFlows(sub, FlowOperation.REMOVE);
verify(oltFlowService, never())
.handleEapolFlow(any(), any(), any(),
eq(FlowOperation.ADD), eq(VlanId.vlanId(OltFlowService.EAPOL_DEFAULT_VLAN)));
Assert.assertFalse(res);
// then test that if the tagged EAPOL is not there we install the default EAPOL
// and return true so we remove the subscriber from the queue
doReturn(false).when(oltFlowService).areSubscriberFlowsPendingRemoval(any(), any(), eq(true));
doReturn(port).when(oltFlowService.deviceService).getPort(deviceId, port.number());
res = oltFlowService.removeSubscriberFlows(sub, DEFAULT_BP_ID_DEFAULT, DEFAULT_MCAST_SERVICE_NAME);
verify(oltFlowService, times(1))
.handleEapolFlow(any(), any(), any(),
eq(FlowOperation.ADD), eq(VlanId.vlanId(OltFlowService.EAPOL_DEFAULT_VLAN)));
Assert.assertTrue(res);
}
@Test
public void testRemovedFlowEvent() throws InterruptedException {
// test that we update the status in case of REMOVED flow even with non
// existing port in the onos device manager
DeviceId deviceId = DeviceId.deviceId("test-device");
ProviderId pid = new ProviderId("of", "foo");
Device device =
new DefaultDevice(pid, deviceId, Device.Type.OLT, "", "", "", "", null);
Port port1 = new DefaultPort(device, PortNumber.portNumber(1), true,
DefaultAnnotations.builder().set(PORT_NAME, PORT_1).build());
// create empty service for testing
List<UniTagInformation> uniTagInformationList = new LinkedList<>();
UniTagInformation vlanUniTag = new UniTagInformation.Builder().setPonCTag(VlanId.vlanId((short) 60))
.build();
uniTagInformationList.add(vlanUniTag);
SubscriberAndDeviceInformation si = new SubscriberAndDeviceInformation();
si.setUniTagList(uniTagInformationList);
ServiceKey sk1 = new ServiceKey(new AccessDevicePort(port1), vlanUniTag);
TrafficSelector selector = DefaultTrafficSelector.builder()
.matchInPort(port1.number())
.matchVlanId(VlanId.vlanId((short) 60))
.build();
TrafficTreatment treatment = DefaultTrafficTreatment.builder()
.immediate()
.setOutput(port1.number())
.build();
FlowRule flowRule = DefaultFlowRule.builder()
.makePermanent()
.withPriority(1000)
.forTable(0)
.forDevice(deviceId)
.fromApp(testAppId)
.withSelector(selector)
.withTreatment(treatment)
.build();
// cpStatus map for the test
component.cpStatus = component.storageService.
<ServiceKey, OltPortStatus>consistentMapBuilder().build().asJavaMap();
OltPortStatus cp1Status = new OltPortStatus(NONE, NONE, PENDING_REMOVE, NONE, NONE);
component.cpStatus.put(sk1, cp1Status);
FlowRuleEvent event = new FlowRuleEvent(FlowRuleEvent.Type.RULE_REMOVED, flowRule);
doReturn(true).when(component.oltDeviceService).isLocalLeader(any());
doReturn(device).when(component.deviceService).getDevice(deviceId);
doReturn(si).when(component.subsService).get(PORT_1);
oltFlowService.internalFlowListener.event(event);
//Some time to finish the operation
TimeUnit.MILLISECONDS.sleep(200);
OltPortStatus status = component.cpStatus.get(sk1);
Assert.assertEquals(REMOVED, status.subscriberFlowsStatus);
}
@Test
public void testHandleNniFlowsDhcpV4WithNniDhcpTrapVid() {
component.enableDhcpOnNni = true;
component.enableDhcpV4 = true;
SubscriberAndDeviceInformation testOltFttbSadis = new SubscriberAndDeviceInformation();
testOltFttbSadis.setNniDhcpTrapVid(VlanId.vlanId("60"));
when(component.sadisService.getSubscriberInfoService().get(testDevice.serialNumber())).
thenReturn(testOltFttbSadis);
component.handleNniFlows(testDevice, nniPort, FlowOperation.ADD);
FilteringObjective expectedFilter = DefaultFilteringObjective.builder()
.permit()
.withKey(Criteria.matchInPort(nniPort.number()))
.addCondition(Criteria.matchEthType(EthType.EtherType.IPV4.ethType()))
.addCondition(Criteria.matchIPProtocol(IPv4.PROTOCOL_UDP))
.addCondition(Criteria.matchUdpSrc(TpPort.tpPort(67)))
.addCondition(Criteria.matchUdpDst(TpPort.tpPort(68)))
.addCondition(Criteria.matchVlanId(testOltFttbSadis.nniDhcpTrapVid()))
.fromApp(testAppId)
.withPriority(10000)
.withMeta(DefaultTrafficTreatment.builder().setOutput(PortNumber.CONTROLLER).build())
.add();
// invoked with the correct DHCP filtering objective
verify(component.flowObjectiveService, times(1))
.filter(eq(deviceId), argThat(new FilteringObjectiveMatcher(expectedFilter)));
// invoked only twice, LLDP and DHCP
verify(component.flowObjectiveService, times(2))
.filter(eq(deviceId), any());
}
@Test
public void testHandleFttbSubscriberDhcpFlowsAdd() {
component.enableDhcpV4 = true;
// add two services, one requires DHCP the other doesn't
List<UniTagInformation> uniTagInformationList = new LinkedList<>();
UniTagInformation mc = new UniTagInformation.Builder()
.setIsDhcpRequired(false).build();
uniTagInformationList.add(dpuMgmtUti);
uniTagInformationList.add(mc);
SubscriberAndDeviceInformation si = new SubscriberAndDeviceInformation();
si.setUniTagList(uniTagInformationList);
final DiscoveredSubscriber addedSub =
new DiscoveredSubscriber(testDevice,
uniUpdateEnabled, DiscoveredSubscriber.Status.ADDED,
false, si);
// return meter IDs
MeterId usBpMeterId = MeterId.meterId(2);
MeterId usOltBpMeterId = MeterId.meterId(3);
doReturn(usBpMeterId).when(component.oltMeterService)
.getMeterIdForBandwidthProfile(addedSub.device.id(), dpuMgmtUti.getUpstreamBandwidthProfile());
doReturn(usOltBpMeterId).when(component.oltMeterService)
.getMeterIdForBandwidthProfile(addedSub.device.id(), dpuMgmtUti.getUpstreamOltBandwidthProfile());
TrafficTreatment expectedTreatment = DefaultTrafficTreatment.builder()
.setVlanId(dpuMgmtUti.getPonSTag())
.setVlanPcp((byte) dpuMgmtUti.getUsPonSTagPriority())
.setOutput(PortNumber.CONTROLLER)
.meter(usBpMeterId)
//TODO why tests use this one and not the other method ?
.writeMetadata(OltFlowServiceUtils.createTechProfValueForWriteMetadata(VlanId.NONE,
dpuMgmtUti.getTechnologyProfileId(), usOltBpMeterId), 0L).build();
FilteringObjective expectedFilter = DefaultFilteringObjective.builder()
.permit()
.withKey(Criteria.matchInPort(addedSub.port.number()))
.addCondition(Criteria.matchEthType(EthType.EtherType.IPV4.ethType()))
.addCondition(Criteria.matchIPProtocol(IPv4.PROTOCOL_UDP))
.addCondition(Criteria.matchUdpSrc(TpPort.tpPort(68)))
.addCondition(Criteria.matchUdpDst(TpPort.tpPort(67)))
.addCondition(Criteria.matchVlanId(dpuMgmtUti.getPonCTag()))
.addCondition(Criteria.matchVlanPcp((byte) dpuMgmtUti.getUsPonCTagPriority()))
.fromApp(testAppId)
.withPriority(10000)
.withMeta(expectedTreatment)
.add();
component.handleSubscriberDhcpFlows(addedSub.device.id(), addedSub.port,
FlowOperation.ADD, si);
verify(component.flowObjectiveService, times(1))
.filter(eq(addedSub.device.id()), argThat(new FilteringObjectiveMatcher(expectedFilter)));
}
@Test
public void testRemoveFttbSubscriberDhcpFlows() {
component.enableDhcpV4 = true;
// Mocking the get call, to mark the SubscriberKey as already added.
component.cpStatus = Mockito.mock(Map.class);
doReturn(new OltPortStatus(null, null, null, ADDED, null))
.when(component.cpStatus).get(Mockito.any());
List<UniTagInformation> uniTagInformationList = new LinkedList<>();
uniTagInformationList.add(dpuMgmtUti);
SubscriberAndDeviceInformation si = new SubscriberAndDeviceInformation();
si.setUniTagList(uniTagInformationList);
final DiscoveredSubscriber removedSub =
new DiscoveredSubscriber(testDevice,
uniUpdateEnabled, DiscoveredSubscriber.Status.REMOVED,
false, si);
// return meter IDs
MeterId usBpMeterId = MeterId.meterId(2);
MeterId usOltBpMeterId = MeterId.meterId(3);
doReturn(usBpMeterId).when(component.oltMeterService)
.getMeterIdForBandwidthProfile(removedSub.device.id(), dpuMgmtUti.getUpstreamBandwidthProfile());
doReturn(usOltBpMeterId).when(component.oltMeterService)
.getMeterIdForBandwidthProfile(removedSub.device.id(), dpuMgmtUti.getUpstreamOltBandwidthProfile());
TrafficTreatment expectedTreatment = DefaultTrafficTreatment.builder()
.setVlanId(dpuMgmtUti.getPonSTag())
.setVlanPcp((byte) dpuMgmtUti.getUsPonSTagPriority())
.setOutput(PortNumber.CONTROLLER)
.meter(usBpMeterId)
.writeMetadata(OltFlowServiceUtils.createTechProfValueForWriteMetadata(VlanId.NONE,
dpuMgmtUti.getTechnologyProfileId(), usOltBpMeterId), 0L).build();
FilteringObjective expectedFilter = DefaultFilteringObjective.builder()
.deny()
.withKey(Criteria.matchInPort(removedSub.port.number()))
.addCondition(Criteria.matchEthType(EthType.EtherType.IPV4.ethType()))
.addCondition(Criteria.matchIPProtocol(IPv4.PROTOCOL_UDP))
.addCondition(Criteria.matchUdpSrc(TpPort.tpPort(68)))
.addCondition(Criteria.matchUdpDst(TpPort.tpPort(67)))
.addCondition(Criteria.matchVlanId(dpuMgmtUti.getPonCTag()))
.addCondition(Criteria.matchVlanPcp((byte) dpuMgmtUti.getUsPonCTagPriority()))
.fromApp(testAppId)
.withPriority(10000)
.withMeta(expectedTreatment)
.add();
component.handleSubscriberDhcpFlows(removedSub.device.id(), removedSub.port,
FlowOperation.REMOVE, si);
verify(component.flowObjectiveService, times(1))
.filter(eq(removedSub.device.id()), argThat(new FilteringObjectiveMatcher(expectedFilter)));
}
@Test
public void testHandleFttbMacSwitchingFlowsAdd() {
component.enableDhcpV4 = true;
List<UniTagInformation> uniTagInformationList = new LinkedList<>();
uniTagInformationList.add(dpuMgmtUti);
uniTagInformationList.add(ancpUti);
SubscriberAndDeviceInformation si = new SubscriberAndDeviceInformation();
si.setUniTagList(uniTagInformationList);
final DiscoveredSubscriber addedSub =
new DiscoveredSubscriber(testDevice,
uniUpdateEnabled, DiscoveredSubscriber.Status.ADDED,
false, si);
// return meter IDs
MeterId usBpMeterId = MeterId.meterId(1);
MeterId usOltBpMeterId = MeterId.meterId(1);
MeterId dsBpMeterId = MeterId.meterId(2);
MeterId dsOltBpMeterId = MeterId.meterId(2);
MacAddress mac = MacAddress.valueOf("0A:00:27:00:00:09");
Host host = Mockito.mock(Host.class);
doReturn(mac).when(host).mac();
doReturn(dpuMgmtUti.getPonSTag()).when(host).vlan();
doReturn(new HashSet<>(Arrays.asList(host))).when(component.hostService)
.getConnectedHosts(new ConnectPoint(addedSub.device.id(), addedSub.port.number()));
doReturn(usBpMeterId).when(component.oltMeterService)
.getMeterIdForBandwidthProfile(addedSub.device.id(), dpuMgmtUti.getUpstreamBandwidthProfile());
doReturn(usOltBpMeterId).when(component.oltMeterService)
.getMeterIdForBandwidthProfile(addedSub.device.id(), dpuMgmtUti.getUpstreamOltBandwidthProfile());
doReturn(dsBpMeterId).when(component.oltMeterService)
.getMeterIdForBandwidthProfile(addedSub.device.id(), dpuMgmtUti.getDownstreamBandwidthProfile());
doReturn(dsOltBpMeterId).when(component.oltMeterService)
.getMeterIdForBandwidthProfile(addedSub.device.id(), dpuMgmtUti.getDownstreamOltBandwidthProfile());
String[] directions = {FTTB_FLOW_UPSTREAM, FTTB_FLOW_DOWNSTREAM};
for (UniTagInformation uti : uniTagInformationList) {
for (String direction : directions) {
TrafficTreatment.Builder expectedTreatment = DefaultTrafficTreatment.builder();
TrafficSelector.Builder expectedSelectorBuilder = DefaultTrafficSelector.builder();
DefaultAnnotations.Builder annotationBuilder = DefaultAnnotations.builder();
annotationBuilder.set(FTTB_FLOW_DIRECTION, direction);
annotationBuilder.set(FTTB_SERVICE_NAME, uti.getServiceName());
switch (direction) {
case FTTB_FLOW_UPSTREAM:
expectedSelectorBuilder
.matchInPort(addedSub.port.number())
.matchVlanId(uti.getPonCTag())
.matchVlanPcp((byte) uti.getUsPonCTagPriority())
.matchEthSrc(mac);
expectedTreatment.setVlanId(uti.getPonSTag())
.setVlanPcp((byte) uti.getUsPonSTagPriority())
.setOutput(nniPort.number())
.meter(usBpMeterId)
.writeMetadata(OltFlowServiceUtils.createMetadata(VlanId.NONE,
uti.getTechnologyProfileId(), nniPort.number()), 0L).build();
annotationBuilder.set(UPSTREAM_ONU, usBpMeterId.toString());
annotationBuilder.set(UPSTREAM_OLT, usOltBpMeterId.toString());
break;
case FTTB_FLOW_DOWNSTREAM:
expectedSelectorBuilder
.matchInPort(nniPort.number())
.matchVlanId(uti.getPonSTag())
.matchEthDst(mac);
expectedTreatment.setVlanId(uti.getPonCTag())
.setOutput(addedSub.port.number())
.meter(dsBpMeterId)
.writeMetadata(OltFlowServiceUtils.createMetadata(VlanId.NONE,
uti.getTechnologyProfileId(), addedSub.port.number()), 0L).build();
annotationBuilder.set(DOWNSTREAM_ONU, dsBpMeterId.toString());
annotationBuilder.set(DOWNSTREAM_OLT, dsOltBpMeterId.toString());
break;
default:
return;
}
ForwardingObjective expected = DefaultForwardingObjective.builder()
.withFlag(ForwardingObjective.Flag.VERSATILE)
.withPriority(1000)
.makePermanent()
.withSelector(expectedSelectorBuilder.build())
.withAnnotations(annotationBuilder.build())
.fromApp(testAppId)
.withTreatment(expectedTreatment.build())
.add();
component.handleSubscriberDataFlows(addedSub.device, addedSub.port,
FlowOperation.ADD, si, DEFAULT_MCAST_SERVICE_NAME_DEFAULT);
verify(component.flowObjectiveService, times(1))
.forward(eq(addedSub.device.id()), eq(expected));
}
}
}
@Test
public void testRemoveFttbMacSwitchingFlows() {
component.enableDhcpV4 = true;
component.cpStatus = Mockito.mock(Map.class);
List<UniTagInformation> uniTagInformationList = new LinkedList<>();
uniTagInformationList.add(dpuMgmtUti);
uniTagInformationList.add(ancpUti);
SubscriberAndDeviceInformation si = new SubscriberAndDeviceInformation();
si.setUniTagList(uniTagInformationList);
final DiscoveredSubscriber removedSub =
new DiscoveredSubscriber(testDevice,
uniUpdateEnabled, DiscoveredSubscriber.Status.REMOVED,
false, si);
ServiceKey sk1 = new ServiceKey(new AccessDevicePort(removedSub.port), dpuMgmtUti);
ServiceKey sk2 = new ServiceKey(new AccessDevicePort(removedSub.port), ancpUti);
component.cpStatus = component.storageService.
<ServiceKey, OltPortStatus>consistentMapBuilder().build().asJavaMap();
OltPortStatus cp1Status = new OltPortStatus(NONE, NONE, PENDING_ADD, NONE, NONE);
OltPortStatus cp2Status = new OltPortStatus(NONE, NONE, PENDING_ADD, NONE, NONE);
component.cpStatus.put(sk1, cp1Status);
component.cpStatus.put(sk2, cp2Status);
// return meter IDs
MeterId usBpMeterId = MeterId.meterId(1);
MeterId usOltBpMeterId = MeterId.meterId(1);
MeterId dsBpMeterId = MeterId.meterId(2);
MeterId dsOltBpMeterId = MeterId.meterId(2);
MacAddress mac = MacAddress.valueOf("0A:00:27:00:00:09");
Host host = Mockito.mock(Host.class);
doReturn(mac).when(host).mac();
doReturn(dpuMgmtUti.getPonSTag()).when(host).vlan();
doReturn(new HashSet<>(Arrays.asList(host))).when(component.hostService)
.getConnectedHosts(new ConnectPoint(removedSub.device.id(), removedSub.port.number()));
doReturn(usBpMeterId).when(component.oltMeterService)
.getMeterIdForBandwidthProfile(removedSub.device.id(), dpuMgmtUti.getUpstreamBandwidthProfile());
doReturn(usOltBpMeterId).when(component.oltMeterService)
.getMeterIdForBandwidthProfile(removedSub.device.id(), dpuMgmtUti.getUpstreamOltBandwidthProfile());
doReturn(dsBpMeterId).when(component.oltMeterService)
.getMeterIdForBandwidthProfile(removedSub.device.id(), dpuMgmtUti.getDownstreamBandwidthProfile());
doReturn(dsOltBpMeterId).when(component.oltMeterService)
.getMeterIdForBandwidthProfile(removedSub.device.id(), dpuMgmtUti.getDownstreamOltBandwidthProfile());
String[] directions = {FTTB_FLOW_UPSTREAM, FTTB_FLOW_DOWNSTREAM};
for (UniTagInformation uti : uniTagInformationList) {
for (String direction : directions) {
TrafficTreatment.Builder expectedTreatment = DefaultTrafficTreatment.builder();
TrafficSelector.Builder expectedSelectorBuilder = DefaultTrafficSelector.builder();
DefaultAnnotations.Builder annotationBuilder = DefaultAnnotations.builder();
annotationBuilder.set(FTTB_FLOW_DIRECTION, direction);
annotationBuilder.set(FTTB_SERVICE_NAME, uti.getServiceName());
switch (direction) {
case FTTB_FLOW_UPSTREAM:
expectedSelectorBuilder
.matchInPort(removedSub.port.number())
.matchVlanId(uti.getPonCTag())
.matchVlanPcp((byte) uti.getUsPonCTagPriority())
.matchEthSrc(mac);
expectedTreatment.setVlanId(uti.getPonSTag())
.setVlanPcp((byte) uti.getUsPonSTagPriority())
.setOutput(nniPort.number())
.meter(usBpMeterId)
.writeMetadata(OltFlowServiceUtils.createMetadata(VlanId.NONE,
uti.getTechnologyProfileId(), nniPort.number()), 0L).build();
annotationBuilder.set(UPSTREAM_ONU, usBpMeterId.toString());
annotationBuilder.set(UPSTREAM_OLT, usOltBpMeterId.toString());
break;
case FTTB_FLOW_DOWNSTREAM:
expectedSelectorBuilder
.matchInPort(nniPort.number())
.matchVlanId(uti.getPonSTag())
.matchEthDst(mac);
expectedTreatment.setVlanId(uti.getPonCTag())
.setOutput(removedSub.port.number())
.meter(dsBpMeterId)
.writeMetadata(OltFlowServiceUtils.createMetadata(VlanId.NONE,
uti.getTechnologyProfileId(), removedSub.port.number()), 0L).build();
annotationBuilder.set(DOWNSTREAM_ONU, dsBpMeterId.toString());
annotationBuilder.set(DOWNSTREAM_OLT, dsOltBpMeterId.toString());
break;
default:
return;
}
ForwardingObjective expected = DefaultForwardingObjective.builder()
.withFlag(ForwardingObjective.Flag.VERSATILE)
.withPriority(1000)
.makePermanent()
.withSelector(expectedSelectorBuilder.build())
.withAnnotations(annotationBuilder.build())
.fromApp(testAppId)
.withTreatment(expectedTreatment.build())
.remove();
component.handleSubscriberDataFlows(removedSub.device, removedSub.port,
FlowOperation.REMOVE, si, DEFAULT_MCAST_SERVICE_NAME_DEFAULT);
verify(component.flowObjectiveService, times(1))
.forward(eq(removedSub.device.id()), eq(expected));
}
}
}
@Test
public void testHandleFttbSubscriberFlowsAdd() {
component.enableDhcpV4 = true;
List<UniTagInformation> uniTagInformationList = new LinkedList<>();
uniTagInformationList.add(fttbSubscriberUti);
SubscriberAndDeviceInformation si = new SubscriberAndDeviceInformation();
si.setUniTagList(uniTagInformationList);
final DiscoveredSubscriber addedSub =
new DiscoveredSubscriber(testDevice,
uniUpdateEnabled, DiscoveredSubscriber.Status.ADDED,
false, si);
// return meter IDs
MeterId usBpMeterId = MeterId.meterId(1);
MeterId usOltBpMeterId = MeterId.meterId(1);
MeterId dsBpMeterId = MeterId.meterId(2);
MeterId dsOltBpMeterId = MeterId.meterId(2);
doReturn(usBpMeterId).when(component.oltMeterService)
.getMeterIdForBandwidthProfile(addedSub.device.id(), dpuMgmtUti.getUpstreamBandwidthProfile());
doReturn(usOltBpMeterId).when(component.oltMeterService)
.getMeterIdForBandwidthProfile(addedSub.device.id(), dpuMgmtUti.getUpstreamOltBandwidthProfile());
doReturn(dsBpMeterId).when(component.oltMeterService)
.getMeterIdForBandwidthProfile(addedSub.device.id(), dpuMgmtUti.getDownstreamBandwidthProfile());
doReturn(dsOltBpMeterId).when(component.oltMeterService)
.getMeterIdForBandwidthProfile(addedSub.device.id(), dpuMgmtUti.getDownstreamOltBandwidthProfile());
String[] directions = {FTTB_FLOW_UPSTREAM, FTTB_FLOW_DOWNSTREAM};
for (UniTagInformation uti : uniTagInformationList) {
for (String direction : directions) {
TrafficTreatment.Builder expectedTreatment = DefaultTrafficTreatment.builder();
TrafficSelector.Builder expectedSelectorBuilder = DefaultTrafficSelector.builder();
DefaultAnnotations.Builder annotationBuilder = DefaultAnnotations.builder();
annotationBuilder.set(FTTB_FLOW_DIRECTION, direction);
annotationBuilder.set(FTTB_SERVICE_NAME, uti.getServiceName());
switch (direction) {
case FTTB_FLOW_UPSTREAM:
expectedSelectorBuilder
.matchInPort(addedSub.port.number())
.matchVlanId(uti.getPonCTag());
expectedTreatment.setVlanId(uti.getPonSTag())
.setOutput(nniPort.number())
.meter(usBpMeterId)
.writeMetadata(OltFlowServiceUtils.createMetadata(VlanId.ANY,
uti.getTechnologyProfileId(), nniPort.number()), 0L).build();
annotationBuilder.set(UPSTREAM_ONU, usBpMeterId.toString());
annotationBuilder.set(UPSTREAM_OLT, usOltBpMeterId.toString());
break;
case FTTB_FLOW_DOWNSTREAM:
expectedSelectorBuilder
.matchInPort(nniPort.number())
.matchMetadata(uti.getPonSTag().toShort())
.matchVlanId(uti.getPonSTag());
expectedTreatment.setVlanId(uti.getPonCTag())
.setOutput(addedSub.port.number())
.meter(dsBpMeterId)
.writeMetadata(OltFlowServiceUtils.createMetadata(VlanId.ANY,
uti.getTechnologyProfileId(), addedSub.port.number()), 0L).build();
annotationBuilder.set(DOWNSTREAM_ONU, dsBpMeterId.toString());
annotationBuilder.set(DOWNSTREAM_OLT, dsOltBpMeterId.toString());
break;
default:
return;
}
ForwardingObjective expected = DefaultForwardingObjective.builder()
.withFlag(ForwardingObjective.Flag.VERSATILE)
.withPriority(1000)
.makePermanent()
.withSelector(expectedSelectorBuilder.build())
.withAnnotations(annotationBuilder.build())
.fromApp(testAppId)
.withTreatment(expectedTreatment.build())
.add();
component.handleSubscriberDataFlows(addedSub.device, addedSub.port,
FlowOperation.ADD, si, DEFAULT_MCAST_SERVICE_NAME_DEFAULT);
verify(component.flowObjectiveService, times(1))
.forward(eq(addedSub.device.id()), eq(expected));
}
}
}
@Test
public void testRemoveFttbSubscriberFlows() {
component.enableDhcpV4 = true;
OltPortStatus oltPortStatus1 = new OltPortStatus(null, null, ADDED,
null, null);
// Mocking the get call, to mark the SubscriberKey as already added.
component.cpStatus = Mockito.mock(Map.class);
when(component.cpStatus.get(Mockito.any())).thenReturn(oltPortStatus1);
List<UniTagInformation> uniTagInformationList = new LinkedList<>();
uniTagInformationList.add(fttbSubscriberUti);
SubscriberAndDeviceInformation si = new SubscriberAndDeviceInformation();
si.setUniTagList(uniTagInformationList);
final DiscoveredSubscriber removedSub =
new DiscoveredSubscriber(testDevice,
uniUpdateEnabled, DiscoveredSubscriber.Status.REMOVED,
false, si);
// return meter IDs
MeterId usBpMeterId = MeterId.meterId(1);
MeterId usOltBpMeterId = MeterId.meterId(1);
MeterId dsBpMeterId = MeterId.meterId(2);
MeterId dsOltBpMeterId = MeterId.meterId(2);
doReturn(usBpMeterId).when(component.oltMeterService)
.getMeterIdForBandwidthProfile(removedSub.device.id(), dpuMgmtUti.getUpstreamBandwidthProfile());
doReturn(usOltBpMeterId).when(component.oltMeterService)
.getMeterIdForBandwidthProfile(removedSub.device.id(), dpuMgmtUti.getUpstreamOltBandwidthProfile());
doReturn(dsBpMeterId).when(component.oltMeterService)
.getMeterIdForBandwidthProfile(removedSub.device.id(), dpuMgmtUti.getDownstreamBandwidthProfile());
doReturn(dsOltBpMeterId).when(component.oltMeterService)
.getMeterIdForBandwidthProfile(removedSub.device.id(), dpuMgmtUti.getDownstreamOltBandwidthProfile());
String[] directions = {FTTB_FLOW_UPSTREAM, FTTB_FLOW_DOWNSTREAM};
for (UniTagInformation uti : uniTagInformationList) {
for (String direction : directions) {
TrafficTreatment.Builder expectedTreatment = DefaultTrafficTreatment.builder();
TrafficSelector.Builder expectedSelectorBuilder = DefaultTrafficSelector.builder();
DefaultAnnotations.Builder annotationBuilder = DefaultAnnotations.builder();
annotationBuilder.set(FTTB_FLOW_DIRECTION, direction);
annotationBuilder.set(FTTB_SERVICE_NAME, uti.getServiceName());
switch (direction) {
case FTTB_FLOW_UPSTREAM:
expectedSelectorBuilder
.matchInPort(removedSub.port.number())
.matchVlanId(uti.getPonCTag());
expectedTreatment.setVlanId(uti.getPonSTag())
.setOutput(nniPort.number())
.meter(usBpMeterId)
.writeMetadata(OltFlowServiceUtils.createMetadata(VlanId.ANY,
uti.getTechnologyProfileId(), nniPort.number()), 0L).build();
annotationBuilder.set(UPSTREAM_ONU, usBpMeterId.toString());
annotationBuilder.set(UPSTREAM_OLT, usOltBpMeterId.toString());
break;
case FTTB_FLOW_DOWNSTREAM:
expectedSelectorBuilder
.matchInPort(nniPort.number())
.matchMetadata(uti.getPonSTag().toShort())
.matchVlanId(uti.getPonSTag());
expectedTreatment.setVlanId(uti.getPonCTag())
.setOutput(removedSub.port.number())
.meter(dsBpMeterId)
.writeMetadata(OltFlowServiceUtils.createMetadata(VlanId.ANY,
uti.getTechnologyProfileId(), removedSub.port.number()), 0L).build();
annotationBuilder.set(DOWNSTREAM_ONU, dsBpMeterId.toString());
annotationBuilder.set(DOWNSTREAM_OLT, dsOltBpMeterId.toString());
break;
default:
return;
}
ForwardingObjective expected = DefaultForwardingObjective.builder()
.withFlag(ForwardingObjective.Flag.VERSATILE)
.withPriority(1000)
.makePermanent()
.withSelector(expectedSelectorBuilder.build())
.withAnnotations(annotationBuilder.build())
.fromApp(testAppId)
.withTreatment(expectedTreatment.build())
.remove();
component.handleSubscriberDataFlows(removedSub.device, removedSub.port,
FlowOperation.REMOVE, si, DEFAULT_MCAST_SERVICE_NAME_DEFAULT);
verify(component.flowObjectiveService, times(1))
.forward(eq(removedSub.device.id()), eq(expected));
}
}
}
}