blob: 39f764e077c9f79b76f6a3288e9101a0def6e3fa [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.flow.DefaultFlowRule;
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.criteria.Criteria;
import org.onosproject.net.flowobjective.DefaultFilteringObjective;
import org.onosproject.net.flowobjective.FilteringObjective;
import org.onosproject.net.flowobjective.FlowObjectiveService;
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.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.Set;
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.onosproject.net.AnnotationKeys.PORT_NAME;
import static org.opencord.olt.impl.OltFlowService.OltFlowsStatus.ERROR;
import static org.opencord.olt.impl.OltFlowService.OltFlowsStatus.NONE;
import static org.opencord.olt.impl.OltFlowService.OltFlowsStatus.ADDED;
import static org.opencord.olt.impl.OltFlowService.OltFlowsStatus.PENDING_ADD;
import static org.opencord.olt.impl.OltFlowService.OltFlowsStatus.PENDING_REMOVE;
import static org.opencord.olt.impl.OltFlowService.OltFlowsStatus.REMOVED;
import static org.opencord.olt.impl.OsgiPropertyConstants.DEFAULT_BP_ID_DEFAULT;
public class OltFlowServiceTest extends OltTestHelpers {
private OltFlowService oltFlowService;
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());
@Before
public void setUp() {
oltFlowService = new OltFlowService();
oltFlowService.cfgService = new ComponentConfigAdapter();
oltFlowService.sadisService = Mockito.mock(SadisService.class);
oltFlowService.coreService = Mockito.spy(new CoreServiceAdapter());
oltFlowService.oltMeterService = Mockito.mock(OltMeterService.class);
oltFlowService.flowObjectiveService = Mockito.mock(FlowObjectiveService.class);
oltFlowService.hostService = Mockito.mock(HostService.class);
oltFlowService.flowRuleService = Mockito.mock(FlowRuleService.class);
oltFlowService.storageService = new TestStorageService();
oltFlowService.oltDeviceService = Mockito.mock(OltDeviceService.class);
oltFlowService.appId = testAppId;
doReturn(Mockito.mock(BaseInformationService.class))
.when(oltFlowService.sadisService).getSubscriberInfoService();
doReturn(testAppId).when(oltFlowService.coreService).registerApplication("org.opencord.olt");
oltFlowService.activate(null);
oltFlowService.bindSadisService(oltFlowService.sadisService);
internalFlowListener = spy(oltFlowService.internalFlowListener);
}
@After
public void tearDown() {
oltFlowService.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
oltFlowService.cpStatus = oltFlowService.storageService.
<ServiceKey, OltPortStatus>consistentMapBuilder().build().asJavaMap();
OltPortStatus cp1Status = new OltPortStatus(PENDING_ADD, NONE, NONE);
oltFlowService.cpStatus.put(sk1, cp1Status);
//check that we only update the provided value
oltFlowService.updateConnectPointStatus(sk1, ADDED, null, null);
OltPortStatus updated = oltFlowService.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
oltFlowService.updateConnectPointStatus(sk2, PENDING_ADD, NONE, NONE);
Assert.assertNotNull(oltFlowService.cpStatus.get(sk2));
// check that if we create a new entry with null values they're converted to NONE
oltFlowService.updateConnectPointStatus(sk3, null, null, null);
updated = oltFlowService.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
oltFlowService.cpStatus = oltFlowService.storageService.
<ServiceKey, OltPortStatus>consistentMapBuilder().build().asJavaMap();
// check that an entry is not created if the only status is pending remove
oltFlowService.updateConnectPointStatus(sk1, null, null, PENDING_REMOVE);
OltPortStatus entry = oltFlowService.cpStatus.get(sk1);
Assert.assertNull(entry);
// check that an entry is not created if the only status is ERROR
oltFlowService.updateConnectPointStatus(sk1, null, null, ERROR);
entry = oltFlowService.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),
oltFlowService.defaultEapolUniTag);
Port port17 = new DefaultPort(device, PortNumber.portNumber(17), true,
DefaultAnnotations.builder().set(PORT_NAME, "name-1").build());
OltPortStatus portStatusAdded = new OltPortStatus(
OltFlowService.OltFlowsStatus.ADDED,
NONE,
null
);
OltPortStatus portStatusRemoved = new OltPortStatus(
REMOVED,
NONE,
null
);
oltFlowService.cpStatus.put(skWithStatus, portStatusAdded);
Assert.assertTrue(oltFlowService.hasDefaultEapol(port));
oltFlowService.cpStatus.put(skWithStatus, portStatusRemoved);
Assert.assertFalse(oltFlowService.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
);
OltPortStatus withDhcp = new OltPortStatus(
REMOVED,
NONE,
ADDED
);
OltPortStatus withSubFlow = new OltPortStatus(
REMOVED,
ADDED,
ADDED
);
oltFlowService.cpStatus.put(skWithStatus, withDefaultEapol);
Assert.assertFalse(oltFlowService.hasSubscriberFlows(port, uti));
oltFlowService.cpStatus.put(skWithStatus, withDhcp);
Assert.assertTrue(oltFlowService.hasDhcpFlows(port, uti));
oltFlowService.cpStatus.put(skWithStatus, withSubFlow);
Assert.assertTrue(oltFlowService.hasSubscriberFlows(port, uti));
}
@Test
public void testHandleBasicPortFlowsNoEapol() throws Exception {
oltFlowService.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);
oltFlowService.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(oltFlowService.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(oltFlowService.oltMeterService)
.createMeter(addedSub.device.id(), DEFAULT_BP_ID_DEFAULT);
boolean res = oltFlowService.handleBasicPortFlows(addedSub, DEFAULT_BP_ID_DEFAULT, DEFAULT_BP_ID_DEFAULT);
Assert.assertFalse(res);
// we do not create flows
verify(oltFlowService.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(oltFlowService.oltMeterService)
.createMeter(deviceId, DEFAULT_BP_ID_DEFAULT);
doReturn(true).when(oltFlowService.oltMeterService)
.hasMeterByBandwidthProfile(deviceId, DEFAULT_BP_ID_DEFAULT);
doReturn(MeterId.meterId(1)).when(oltFlowService.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(oltFlowService.createTechProfValueForWriteMetadata(
VlanId.vlanId(eapolDefaultVlan),
oltFlowService.defaultTechProfileId, MeterId.meterId(1)), 0)
.setOutput(PortNumber.CONTROLLER)
.pushVlan()
.setVlanId(VlanId.vlanId(eapolDefaultVlan)).build()
)
.add();
oltFlowService.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(oltFlowService.oltMeterService, never())
.hasPendingMeterByBandwidthProfile(eq(deviceId), eq(DEFAULT_BP_ID_DEFAULT));
verify(oltFlowService.oltMeterService, never())
.createMeterForBp(eq(deviceId), eq(DEFAULT_BP_ID_DEFAULT));
verify(oltFlowService.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(oltFlowService.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(oltFlowService.createTechProfValueForWriteMetadata(
VlanId.vlanId(eapolDefaultVlan),
oltFlowService.defaultTechProfileId, MeterId.meterId(1)), 0)
.setOutput(PortNumber.CONTROLLER)
.pushVlan()
.setVlanId(VlanId.vlanId(eapolDefaultVlan)).build()
)
.add();
oltFlowService.handleBasicPortFlows(removedSub, DEFAULT_BP_ID_DEFAULT, DEFAULT_BP_ID_DEFAULT);
verify(oltFlowService.flowObjectiveService, times(1))
.filter(eq(deviceId), argThat(new FilteringObjectiveMatcher(expectedFilter)));
}
@Test
public void testHandleNniFlowsOnlyLldp() {
oltFlowService.enableDhcpOnNni = false;
oltFlowService.handleNniFlows(testDevice, nniPort, OltFlowService.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(oltFlowService.flowObjectiveService, times(1))
.filter(eq(deviceId), argThat(new FilteringObjectiveMatcher(expectedFilter)));
verify(oltFlowService.flowObjectiveService, times(1))
.filter(eq(deviceId), any());
}
@Test
public void testHandleNniFlowsDhcpV4() {
oltFlowService.enableDhcpOnNni = true;
oltFlowService.enableDhcpV4 = true;
oltFlowService.handleNniFlows(testDevice, nniPort, OltFlowService.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(oltFlowService.flowObjectiveService, times(1))
.filter(eq(deviceId), argThat(new FilteringObjectiveMatcher(expectedFilter)));
// invoked only twice, LLDP and DHCP
verify(oltFlowService.flowObjectiveService, times(2))
.filter(eq(deviceId), any());
}
@Test
public void testRemoveNniFlowsDhcpV4() {
oltFlowService.enableDhcpOnNni = true;
oltFlowService.enableDhcpV4 = true;
oltFlowService.handleNniFlows(testDevice, nniPortDisabled, OltFlowService.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(oltFlowService.flowObjectiveService, times(1))
.filter(eq(deviceId), argThat(new FilteringObjectiveMatcher(expectedFilter)));
// invoked only twice, LLDP and DHCP
verify(oltFlowService.flowObjectiveService, times(2))
.filter(eq(deviceId), any());
}
@Test
public void testHandleNniFlowsDhcpV6() {
oltFlowService.enableDhcpOnNni = true;
oltFlowService.enableDhcpV4 = false;
oltFlowService.enableDhcpV6 = true;
oltFlowService.handleNniFlows(testDevice, nniPort, OltFlowService.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(oltFlowService.flowObjectiveService, times(1))
.filter(eq(deviceId), argThat(new FilteringObjectiveMatcher(expectedFilter)));
// invoked only twice, LLDP and DHCP
verify(oltFlowService.flowObjectiveService, times(2))
.filter(eq(deviceId), any());
}
@Test
public void testHandleNniFlowsIgmp() {
oltFlowService.enableDhcpOnNni = false;
oltFlowService.enableIgmpOnNni = true;
oltFlowService.handleNniFlows(testDevice, nniPort, OltFlowService.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(oltFlowService.flowObjectiveService, times(1))
.filter(eq(deviceId), argThat(new FilteringObjectiveMatcher(expectedFilter)));
// invoked only twice, LLDP and DHCP
verify(oltFlowService.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 = oltFlowService.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 = oltFlowService.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(oltFlowService.hostService).getConnectedHosts((ConnectPoint) any());
isMacAvailable = oltFlowService.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 = oltFlowService.isMacAddressAvailable(testDevice.id(), uniUpdateEnabled, si);
Assert.assertTrue(isMacAvailable);
}
@Test
public void testHandleSubscriberDhcpFlowsAdd() {
String usBp = "usBp";
String usOltBp = "usOltBp";
oltFlowService.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(oltFlowService.oltMeterService)
.getMeterIdForBandwidthProfile(addedSub.device.id(), usBp);
doReturn(MeterId.meterId(3)).when(oltFlowService.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();
oltFlowService.handleSubscriberDhcpFlows(addedSub.device.id(), addedSub.port,
OltFlowService.FlowOperation.ADD, si);
verify(oltFlowService.flowObjectiveService, times(1))
.filter(eq(addedSub.device.id()), argThat(new FilteringObjectiveMatcher(expectedFilter)));
}
@Test
public void testInternalFlowListenerNotMaster() {
doReturn(false).when(oltFlowService.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());
}
}