Refactors IGMPProxy application in API and APP bundles
Change-Id: I465d6c0f49079804ae8e0a1f464581c25c6d2300
diff --git a/app/src/test/java/org/opencord/igmpproxy/impl/IgmpManagerBase.java b/app/src/test/java/org/opencord/igmpproxy/impl/IgmpManagerBase.java
new file mode 100644
index 0000000..b301f94
--- /dev/null
+++ b/app/src/test/java/org/opencord/igmpproxy/impl/IgmpManagerBase.java
@@ -0,0 +1,682 @@
+/*
+ * Copyright 2017-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.igmpproxy.impl;
+
+import com.google.common.collect.ImmutableSet;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.IGMP;
+import org.onlab.packet.IGMPMembership;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onosproject.cfg.ComponentConfigService;
+import org.onosproject.cfg.ConfigProperty;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.event.DefaultEventSinkRegistry;
+import org.onosproject.event.Event;
+import org.onosproject.event.EventDeliveryService;
+import org.onosproject.event.EventSink;
+import org.onosproject.mastership.MastershipServiceAdapter;
+import org.onosproject.mcast.api.McastListener;
+import org.onosproject.mcast.api.McastRoute;
+import org.onosproject.mcast.api.McastRouteData;
+import org.onosproject.mcast.api.MulticastRouteService;
+import org.onosproject.net.AnnotationKeys;
+import org.onosproject.net.Annotations;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DefaultAnnotations;
+import org.onosproject.net.DefaultDevice;
+import org.onosproject.net.DefaultPort;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.HostId;
+import org.onosproject.net.Port;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.SparseAnnotations;
+import org.onosproject.net.config.Config;
+import org.onosproject.net.config.ConfigFactory;
+import org.onosproject.net.config.NetworkConfigRegistryAdapter;
+import org.onosproject.net.config.basics.McastConfig;
+import org.onosproject.net.config.basics.SubjectFactories;
+import org.onosproject.net.device.DeviceServiceAdapter;
+import org.onosproject.net.packet.DefaultInboundPacket;
+import org.onosproject.net.packet.DefaultPacketContext;
+import org.onosproject.net.packet.InboundPacket;
+import org.onosproject.net.packet.OutboundPacket;
+import org.onosproject.net.packet.PacketContext;
+import org.onosproject.net.packet.PacketProcessor;
+import org.onosproject.net.packet.PacketServiceAdapter;
+import org.opencord.sadis.BandwidthProfileInformation;
+import org.opencord.sadis.BaseInformationService;
+import org.opencord.sadis.SadisService;
+import org.opencord.sadis.SubscriberAndDeviceInformation;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.component.ComponentContext;
+import org.osgi.service.component.ComponentInstance;
+
+import static com.google.common.base.Preconditions.checkState;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+public class IgmpManagerBase {
+
+ // Device configuration
+ protected static final DeviceId DEVICE_ID_OF_A = DeviceId.deviceId("of:1");
+ protected static final DeviceId DEVICE_ID_OF_B = DeviceId.deviceId("of:2");
+ protected static final DeviceId DEVICE_ID_OF_C = DeviceId.deviceId("of:00000000000000003");
+
+ //Multicast ip address
+ protected static final Ip4Address GROUP_IP = Ip4Address.valueOf("224.0.0.0");
+ //Unknown group Ip address
+ protected static final Ip4Address UNKNOWN_GRP_IP = Ip4Address.valueOf("124.0.0.0");
+ // Source ip address of two different device.
+ protected static final Ip4Address SOURCE_IP_OF_A = Ip4Address.valueOf("10.177.125.4");
+ protected static final Ip4Address SOURCE_IP_OF_B = Ip4Address.valueOf("10.177.125.5");
+
+ // Common connect point of aggregation switch used by all devices.
+ protected static final ConnectPoint COMMON_CONNECT_POINT =
+ ConnectPoint.deviceConnectPoint("of:00000000000000003/3");
+ // Uplink ports for two olts A and B
+ protected static final PortNumber PORT_A = PortNumber.portNumber(1);
+ protected static final PortNumber PORT_B = PortNumber.portNumber(2);
+ protected static final PortNumber PORT_C = PortNumber.portNumber(3);
+ protected static final PortNumber PORT_NNI = PortNumber.portNumber(65536);
+
+ // Connect Point mode for two olts
+ protected static final ConnectPoint CONNECT_POINT_A = new ConnectPoint(DEVICE_ID_OF_A, PORT_A);
+ protected static final ConnectPoint CONNECT_POINT_B = new ConnectPoint(DEVICE_ID_OF_B, PORT_B);
+ protected static final ConnectPoint CONNECT_POINT_C = new ConnectPoint(DEVICE_ID_OF_C, PORT_C);
+
+ protected static final String CLIENT_NAS_PORT_ID = "PON 1/1";
+ protected static final String CLIENT_CIRCUIT_ID = "CIR-PON 1/1";
+ protected String dsBpId = "HSIA-DS";
+
+ private static final int STATISTICS_GEN_PERIOD_IN_SEC = 2;
+
+ private static final String NNI_PREFIX = "nni";
+
+ protected List<Port> lsPorts = new ArrayList<Port>();
+ protected List<Device> lsDevices = new ArrayList<Device>();
+ // Flag for adding two different devices in oltData
+ protected boolean flagForDevice = true;
+ PacketContext context;
+ // Flag for sending two different packets
+ protected boolean flagForPacket = true;
+ // Flag for sending two different packets
+ protected boolean flagForQueryPacket = false;
+ // Flag to check permission
+ boolean flagForPermission = false;
+ // List to store the packets emitted
+ protected List<OutboundPacket> savedPackets;
+ protected PacketProcessor packetProcessor;
+
+ class MockDeviceService extends DeviceServiceAdapter {
+
+ @Override
+ public Device getDevice(DeviceId deviceId) {
+ if (flagForDevice) {
+ DefaultAnnotations.Builder annotationsBuilder = DefaultAnnotations.builder()
+ .set(AnnotationKeys.MANAGEMENT_ADDRESS, SOURCE_IP_OF_A.toString());
+ SparseAnnotations annotations = annotationsBuilder.build();
+ Annotations[] da = {annotations };
+ Device deviceA = new DefaultDevice(null, DEVICE_ID_OF_A, Device.Type.OTHER, "", "", "", "", null, da);
+ flagForDevice = false;
+ return deviceA;
+ } else {
+ DefaultAnnotations.Builder annotationsBuilder = DefaultAnnotations.builder()
+ .set(AnnotationKeys.MANAGEMENT_ADDRESS, SOURCE_IP_OF_B.toString());
+ SparseAnnotations annotations = annotationsBuilder.build();
+ Annotations[] da = {annotations };
+ Device deviceB = new DefaultDevice(null, DEVICE_ID_OF_B, Device.Type.OTHER, "", "", "", "", null, da);
+ return deviceB;
+ }
+ }
+ @Override
+ public List<Port> getPorts(DeviceId deviceId) {
+ return lsPorts;
+ }
+
+ @Override
+ public Iterable<Device> getAvailableDevices() {
+ DefaultAnnotations.Builder annotationsBuilder = DefaultAnnotations.builder()
+ .set(AnnotationKeys.MANAGEMENT_ADDRESS, SOURCE_IP_OF_A.toString());
+ SparseAnnotations annotations = annotationsBuilder.build();
+ Annotations[] da = {annotations };
+ Device deviceA = new DefaultDevice(null, DEVICE_ID_OF_C, Device.Type.OTHER, "", "", "", "", null, da);
+ lsDevices.add(deviceA);
+ return lsDevices;
+ }
+
+ @Override
+ public Port getPort(DeviceId deviceId, PortNumber portNumber) {
+ if (portNumber.equals(PORT_NNI)) {
+ DefaultAnnotations.Builder annotationsBuilder = DefaultAnnotations.builder()
+ .set(AnnotationKeys.PORT_NAME, NNI_PREFIX);
+ Port nni = new DefaultPort(null, portNumber, true, annotationsBuilder.build());
+ return nni;
+ }
+ return super.getPort(deviceId, portNumber);
+ }
+ }
+
+ static final Class<IgmpproxyConfig> IGMPPROXY_CONFIG_CLASS = IgmpproxyConfig.class;
+ static final Class<IgmpproxySsmTranslateConfig> IGMPPROXY_SSM_CONFIG_CLASS = IgmpproxySsmTranslateConfig.class;
+ static final Class<McastConfig> MCAST_CONFIG_CLASS = McastConfig.class;
+ ConfigFactory<ApplicationId, IgmpproxyConfig> igmpproxyConfigFactory =
+ new ConfigFactory<ApplicationId, IgmpproxyConfig>(
+ SubjectFactories.APP_SUBJECT_FACTORY, IGMPPROXY_CONFIG_CLASS, "igmpproxy") {
+ @Override
+ public IgmpproxyConfig createConfig() {
+ return new IgmpproxyConfig();
+ }
+ };
+
+ ConfigFactory<ApplicationId, IgmpproxySsmTranslateConfig> igmpproxySsmConfigFactory =
+ new ConfigFactory<ApplicationId, IgmpproxySsmTranslateConfig>(
+ SubjectFactories.APP_SUBJECT_FACTORY, IGMPPROXY_SSM_CONFIG_CLASS, "ssmTranslate", true) {
+
+ @Override
+ public IgmpproxySsmTranslateConfig createConfig() {
+ return new IgmpproxySsmTranslateConfig();
+ }
+ };
+
+
+ class MockIgmpProxyConfig extends IgmpproxyConfig {
+ boolean igmpOnPodBasis = true;
+
+ MockIgmpProxyConfig(boolean igmpFlagValue) {
+ igmpOnPodBasis = igmpFlagValue;
+ }
+ @Override
+ public boolean igmpOnPodBasis() {
+ return igmpOnPodBasis;
+ }
+
+ @Override
+ public ConnectPoint getSourceDeviceAndPort() {
+ if (flagForPermission) {
+ return null;
+ }
+ return COMMON_CONNECT_POINT;
+ }
+
+ @Override
+ public ConnectPoint connectPoint() {
+ return COMMON_CONNECT_POINT;
+ }
+ }
+
+
+ class TestNetworkConfigRegistry extends NetworkConfigRegistryAdapter {
+ Boolean igmpOnPodFlag = false;
+
+ TestNetworkConfigRegistry(Boolean igmpFlag) {
+ igmpOnPodFlag = igmpFlag;
+ }
+ @SuppressWarnings("unchecked")
+ @Override
+ public <S> Set<S> getSubjects(Class<S> subjectClass) {
+ if (subjectClass.getName().equalsIgnoreCase("org.onosproject.net.DeviceId")) {
+ return (Set<S>) ImmutableSet.of(DEVICE_ID_OF_A, DEVICE_ID_OF_B);
+ }
+ return null;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public <S, C extends Config<S>> C getConfig(S subject, Class<C> configClass) {
+ if (configClass.getName().equalsIgnoreCase("org.opencord.igmpproxy.impl.IgmpproxyConfig")) {
+ IgmpproxyConfig igmpproxyConfig = new MockIgmpProxyConfig(igmpOnPodFlag);
+ return (C) igmpproxyConfig;
+ } else {
+ super.getConfig(subject, configClass);
+ }
+ return null;
+ }
+ }
+
+
+ /**
+ * Keeps a reference to the PacketProcessor and saves the OutboundPackets. Adds
+ * the emitted packet in savedPackets list
+ */
+ class MockPacketService extends PacketServiceAdapter {
+
+ public MockPacketService() {
+ savedPackets = new LinkedList<>();
+ }
+
+ @Override
+ public void addProcessor(PacketProcessor processor, int priority) {
+ packetProcessor = processor;
+ }
+
+ @Override
+ public void emit(OutboundPacket packet) {
+ synchronized (savedPackets) {
+ savedPackets.add(packet);
+ savedPackets.notify();
+ }
+ }
+ }
+
+
+ class MockMastershipService extends MastershipServiceAdapter {
+ @Override
+ public boolean isLocalMaster(DeviceId deviceId) {
+ return true;
+ }
+ }
+
+ final class TestMulticastRouteService implements MulticastRouteService {
+ @Override
+ public void addListener(McastListener listener) {
+ }
+
+ @Override
+ public void removeListener(McastListener listener) {
+ }
+
+ @Override
+ public void add(McastRoute route) {
+ }
+
+ @Override
+ public void remove(McastRoute route) {
+ }
+
+ @Override
+ public Set<McastRoute> getRoutes() {
+ return null;
+ }
+
+ @Override
+ public Set<McastRoute> getRoute(IpAddress groupIp, IpAddress sourceIp) {
+ return null;
+ }
+
+ @Override
+ public void addSource(McastRoute route, HostId source) {
+ }
+
+ @Override
+ public void addSources(McastRoute route, HostId hostId, Set<ConnectPoint> connectPoints) {
+ }
+
+ @Override
+ public void addSources(McastRoute route, Set<ConnectPoint> sources) {
+
+ }
+
+ @Override
+ public void removeSources(McastRoute route) {
+
+ }
+
+ @Override
+ public void removeSource(McastRoute route, HostId source) {
+
+ }
+
+ @Override
+ public void addSink(McastRoute route, HostId hostId) {
+
+ }
+
+ @Override
+ public void addSinks(McastRoute route, HostId hostId, Set<ConnectPoint> connectPoints) {
+
+ }
+
+ @Override
+ public void addSinks(McastRoute route, Set<ConnectPoint> sinks) {
+
+ }
+
+ @Override
+ public void removeSinks(McastRoute route) {
+
+ }
+
+ @Override
+ public void removeSink(McastRoute route, HostId hostId) {
+
+ }
+
+ @Override
+ public void removeSinks(McastRoute route, Set<ConnectPoint> sink) {
+
+ }
+
+ @Override
+ public McastRouteData routeData(McastRoute route) {
+ return null;
+ }
+
+ @Override
+ public Set<ConnectPoint> sources(McastRoute route) {
+ return null;
+ }
+
+ @Override
+ public Set<ConnectPoint> sources(McastRoute route, HostId hostId) {
+ return null;
+ }
+
+ @Override
+ public Set<ConnectPoint> sinks(McastRoute route) {
+ return null;
+ }
+
+ @Override
+ public Set<ConnectPoint> sinks(McastRoute route, HostId hostId) {
+ return null;
+ }
+
+ @Override
+ public Set<ConnectPoint> nonHostSinks(McastRoute route) {
+ return null;
+ }
+
+ }
+
+
+ /**
+ * Mocks the DefaultPacketContext.
+ */
+ final class TestPacketContext extends DefaultPacketContext {
+ TestPacketContext(long time, InboundPacket inPkt, OutboundPacket outPkt, boolean block) {
+ super(time, inPkt, outPkt, block);
+ }
+
+ @Override
+ public void send() {
+ // We don't send anything out.
+ }
+ }
+
+ /**
+ * Sends Ethernet packet to the process method of the Packet Processor.
+ *
+ * @param reply Ethernet packet
+ * @throws InterruptedException
+ */
+ void sendPacket(Ethernet reply) {
+
+ if (reply != null) {
+ final ByteBuffer byteBuffer = ByteBuffer.wrap(reply.serialize());
+
+ if (flagForQueryPacket) {
+ InboundPacket inBoundPacket = new DefaultInboundPacket(CONNECT_POINT_C, reply, byteBuffer);
+ context = new TestPacketContext(127L, inBoundPacket, null, false);
+ packetProcessor.process(context);
+ } else {
+ if (flagForPacket) {
+ InboundPacket inPacket = new DefaultInboundPacket(CONNECT_POINT_A, reply, byteBuffer);
+ context = new TestPacketContext(127L, inPacket, null, false);
+ flagForPacket = false;
+
+ packetProcessor.process(context);
+ } else {
+ InboundPacket inBoundPacket = new DefaultInboundPacket(CONNECT_POINT_B, reply, byteBuffer);
+ context = new TestPacketContext(127L, inBoundPacket, null, false);
+ flagForPacket = true;
+
+ packetProcessor.process(context);
+ }
+ }
+ }
+ }
+
+ protected class MockSadisService implements SadisService {
+
+ @Override
+ public BaseInformationService<SubscriberAndDeviceInformation> getSubscriberInfoService() {
+ return new MockSubService();
+ }
+
+ @Override
+ public BaseInformationService<BandwidthProfileInformation> getBandwidthProfileService() {
+ return new MockBpService();
+ }
+ }
+
+ private class MockBpService implements BaseInformationService<BandwidthProfileInformation> {
+
+ @Override
+ public void invalidateAll() {
+
+ }
+
+ @Override
+ public void invalidateId(String id) {
+
+ }
+
+ @Override
+ public BandwidthProfileInformation get(String id) {
+ if (id.equals(dsBpId)) {
+ BandwidthProfileInformation bpInfo = new BandwidthProfileInformation();
+ bpInfo.setAssuredInformationRate(0);
+ bpInfo.setCommittedInformationRate(10000);
+ bpInfo.setCommittedBurstSize(1000L);
+ bpInfo.setExceededBurstSize(2000L);
+ bpInfo.setExceededInformationRate(20000);
+ return bpInfo;
+ }
+ return null;
+ }
+
+ @Override
+ public BandwidthProfileInformation getfromCache(String id) {
+ return null;
+ }
+ }
+
+ private class MockSubService implements BaseInformationService<SubscriberAndDeviceInformation> {
+ MockSubscriberAndDeviceInformation sub =
+ new MockSubscriberAndDeviceInformation(CLIENT_NAS_PORT_ID,
+ CLIENT_NAS_PORT_ID, CLIENT_CIRCUIT_ID, null, null);
+
+ @Override
+ public SubscriberAndDeviceInformation get(String id) {
+ return sub;
+ }
+
+ @Override
+ public void invalidateAll() {
+ }
+
+ @Override
+ public void invalidateId(String id) {
+ }
+
+ @Override
+ public SubscriberAndDeviceInformation getfromCache(String id) {
+ return null;
+ }
+ }
+
+ private class MockSubscriberAndDeviceInformation extends SubscriberAndDeviceInformation {
+
+ MockSubscriberAndDeviceInformation(String id, String nasPortId,
+ String circuitId, MacAddress hardId,
+ Ip4Address ipAddress) {
+ this.setHardwareIdentifier(hardId);
+ this.setId(id);
+ this.setIPAddress(ipAddress);
+ this.setNasPortId(nasPortId);
+ this.setCircuitId(circuitId);
+ this.setUplinkPort((int) PORT_NNI.toLong());
+ }
+ }
+
+ protected class MockCfgService implements ComponentConfigService {
+
+ @Override
+ public Set<String> getComponentNames() {
+ return null;
+ }
+
+ @Override
+ public void registerProperties(Class<?> componentClass) {
+
+ }
+
+ @Override
+ public void unregisterProperties(Class<?> componentClass, boolean clear) {
+
+ }
+
+ @Override
+ public Set<ConfigProperty> getProperties(String componentName) {
+ return null;
+ }
+
+ @Override
+ public void setProperty(String componentName, String name, String value) {
+
+ }
+
+ @Override
+ public void preSetProperty(String componentName, String name, String value) {
+
+ }
+
+ @Override
+ public void preSetProperty(String componentName, String name, String value, boolean override) {
+
+ }
+
+ @Override
+ public void unsetProperty(String componentName, String name) {
+
+ }
+
+ @Override
+ public ConfigProperty getProperty(String componentName, String attribute) {
+ return null;
+ }
+
+ }
+
+ public static class TestEventDispatcher extends DefaultEventSinkRegistry implements EventDeliveryService {
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public synchronized void post(Event event) {
+ EventSink sink = getSink(event.getClass());
+ checkState(sink != null, "No sink for event %s", event);
+ sink.process(event);
+ }
+
+ @Override
+ public void setDispatchTimeLimit(long millis) {
+ }
+
+ @Override
+ public long getDispatchTimeLimit() {
+ return 0;
+ }
+ }
+
+ class MockComponentContext implements ComponentContext {
+
+ @Override
+ public Dictionary<String, Object> getProperties() {
+ Dictionary<String, Object> cfgDict = new Hashtable<String, Object>();
+ cfgDict.put("statisticsGenerationPeriodInSeconds", STATISTICS_GEN_PERIOD_IN_SEC);
+ return cfgDict;
+ }
+
+ @Override
+ public Object locateService(String name) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Object locateService(String name, ServiceReference reference) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Object[] locateServices(String name) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public BundleContext getBundleContext() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Bundle getUsingBundle() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public ComponentInstance getComponentInstance() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public void enableComponent(String name) {
+ // TODO Auto-generated method stub
+ }
+
+ @Override
+ public void disableComponent(String name) {
+ // TODO Auto-generated method stub
+ }
+
+ @Override
+ public ServiceReference getServiceReference() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+ }
+
+ Ethernet buildWrongIgmpPacket(Ip4Address groupIp, Ip4Address sourceIp) {
+ IGMPMembership igmpMembership = new IGMPMembership(groupIp);
+ igmpMembership.setRecordType((byte) 0x33);
+
+ return IgmpSender.getInstance().buildIgmpPacket(IGMP.TYPE_IGMPV3_MEMBERSHIP_REPORT, groupIp,
+ igmpMembership, sourceIp, false);
+ }
+
+ Ethernet buildUnknownIgmpPacket(Ip4Address groupIp, Ip4Address sourceIp) {
+ IGMPMembership igmpMembership = new IGMPMembership(groupIp);
+ igmpMembership.setRecordType((byte) 0x33);
+
+ return IgmpSender.getInstance().buildIgmpPacket((byte) 0x44, groupIp, igmpMembership, sourceIp, false);
+ }
+
+}