Add unit test for openstack switching host provider

Change-Id: If2ff5cf7d361d3857aca14b67e8d66ced0aacaa6
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingHostProvider.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingHostProvider.java
index b19d78d..01e1099 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingHostProvider.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingHostProvider.java
@@ -46,7 +46,6 @@
 import org.onosproject.net.host.HostService;
 import org.onosproject.net.provider.AbstractProvider;
 import org.onosproject.net.provider.ProviderId;
-import org.onosproject.openstacknetworking.api.InstancePortAdminService;
 import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
 import org.onosproject.openstacknode.api.OpenstackNode;
 import org.onosproject.openstacknode.api.OpenstackNodeEvent;
@@ -105,18 +104,15 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected OpenstackNodeService osNodeService;
 
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected InstancePortAdminService instancePortAdminService;
+    private HostProviderService hostProviderService;
 
-    private final ExecutorService deviceEventExecutor =
+    private final ExecutorService executor =
             Executors.newSingleThreadExecutor(groupedThreads(this.getClass().getSimpleName(), "device-event"));
     private final InternalDeviceListener internalDeviceListener =
             new InternalDeviceListener();
     private final InternalOpenstackNodeListener internalNodeListener =
             new InternalOpenstackNodeListener();
 
-    private HostProviderService hostProvider;
-
     /**
      * Creates OpenStack switching host provider.
      */
@@ -129,7 +125,7 @@
         coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
         deviceService.addListener(internalDeviceListener);
         osNodeService.addListener(internalNodeListener);
-        hostProvider = hostProviderRegistry.register(this);
+        hostProviderService = hostProviderRegistry.register(this);
 
         log.info("Started");
     }
@@ -140,7 +136,7 @@
         osNodeService.removeListener(internalNodeListener);
         deviceService.removeListener(internalDeviceListener);
 
-        deviceEventExecutor.shutdown();
+        executor.shutdown();
 
         log.info("Stopped");
     }
@@ -151,6 +147,32 @@
     }
 
     /**
+     * A helper method which logs the port addition event and performs port
+     * addition action.
+     *
+     * @param event device event
+     */
+    protected void portAddedHelper(DeviceEvent event) {
+        log.debug("Instance port {} is detected from {}",
+                event.port().annotations().value(PORT_NAME),
+                event.subject().id());
+        processPortAdded(event.port());
+    }
+
+    /**
+     * A helper method which logs the port removal event and performs port
+     * removal action.
+     *
+     * @param event device event
+     */
+    protected void portRemovedHelper(DeviceEvent event) {
+        log.debug("Instance port {} is removed from {}",
+                event.port().annotations().value(PORT_NAME),
+                event.subject().id());
+        processPortRemoved(event.port());
+    }
+
+    /**
      * Processes port addition event.
      * Once a port addition event is detected, it tries to create a host instance
      * with openstack augmented host information such as networkId, portId,
@@ -158,7 +180,7 @@
      *
      * @param port port object used in ONOS
      */
-    private void processPortAdded(Port port, Device device) {
+    protected void processPortAdded(Port port) {
         // TODO check the node state is COMPLETE
         org.openstack4j.model.network.Port osPort = osNetworkService.port(port);
         if (osPort == null) {
@@ -228,17 +250,17 @@
             // newly added location is not in the existing location list,
             // therefore, we simply add this into the location list
             if (locations.size() == 0) {
-                hostProvider.addLocationToHost(hostId,
+                hostProviderService.addLocationToHost(hostId,
                         new HostLocation(connectPoint, createTime));
             }
 
             // newly added location is in the existing location list,
             // the hostDetected method invocation in turn triggers host Update event
             if (locations.size() == 1) {
-                hostProvider.hostDetected(hostId, hostDesc, false);
+                hostProviderService.hostDetected(hostId, hostDesc, false);
             }
         } else {
-            hostProvider.hostDetected(hostId, hostDesc, false);
+            hostProviderService.hostDetected(hostId, hostDesc, false);
         }
     }
 
@@ -248,10 +270,9 @@
      * instance through host provider by giving connect point information,
      * and vanishes it.
      *
-     * @param event device event
+     * @param port ONOS port
      */
-    private void processPortRemoved(DeviceEvent event) {
-        Port port = event.port();
+    protected void processPortRemoved(Port port) {
         ConnectPoint connectPoint = new ConnectPoint(port.element().id(), port.number());
 
         Set<Host> hosts = hostService.getConnectedHosts(connectPoint);
@@ -263,13 +284,13 @@
 
             // if the host contains only one filtered location, we remove the host
             if (h.locations().size() == 1) {
-                hostProvider.hostVanished(h.id());
+                hostProviderService.hostVanished(h.id());
             }
 
             // if the host contains multiple locations, we simply remove the
             // host location
             if (h.locations().size() > 1 && hostLocation.isPresent()) {
-                hostProvider.removeLocationFromHost(h.id(), hostLocation.get());
+                hostProviderService.removeLocationFromHost(h.id(), hostLocation.get());
             }
         });
     }
@@ -298,7 +319,7 @@
         }
 
         private boolean isDirectPort(String portName) {
-            return portNamePrefixMap().values().stream().filter(p -> portName.startsWith(p)).findAny().isPresent();
+            return portNamePrefixMap().values().stream().anyMatch(portName::startsWith);
         }
 
         @Override
@@ -307,16 +328,16 @@
             switch (event.type()) {
                 case PORT_UPDATED:
                     if (!event.port().isEnabled()) {
-                        portRemovedHelper(deviceEventExecutor, event);
+                        executor.execute(() -> portRemovedHelper(event));
                     } else if (event.port().isEnabled()) {
-                        portAddedHelper(deviceEventExecutor, event);
+                        executor.execute(() -> portAddedHelper(event));
                     }
                     break;
                 case PORT_ADDED:
-                    portAddedHelper(deviceEventExecutor, event);
+                    executor.execute(() -> portAddedHelper(event));
                     break;
                 case PORT_REMOVED:
-                    portRemovedHelper(deviceEventExecutor, event);
+                    executor.execute(() -> portRemovedHelper(event));
                     break;
                 default:
                     break;
@@ -324,38 +345,6 @@
         }
     }
 
-    /**
-     * A helper method which logs the port addition event and performs port
-     * addition action.
-     *
-     * @param executor device executor service
-     * @param event device event
-     */
-    private void portAddedHelper(ExecutorService executor, DeviceEvent event) {
-        executor.execute(() -> {
-            log.debug("Instance port {} is detected from {}",
-                    event.port().annotations().value(PORT_NAME),
-                    event.subject().id());
-            processPortAdded(event.port(), event.subject());
-        });
-    }
-
-    /**
-     * A helper method which logs the port removal event and performs port
-     * removal action.
-     *
-     * @param executor device executor service
-     * @param event device event
-     */
-    private void portRemovedHelper(ExecutorService executor, DeviceEvent event) {
-        executor.execute(() -> {
-            log.debug("Instance port {} is removed from {}",
-                    event.port().annotations().value(PORT_NAME),
-                    event.subject().id());
-            processPortRemoved(event);
-        });
-    }
-
     private class InternalOpenstackNodeListener implements OpenstackNodeListener {
 
         @Override
@@ -378,10 +367,8 @@
 
             switch (event.type()) {
                 case OPENSTACK_NODE_COMPLETE:
-                    deviceEventExecutor.execute(() -> {
-                        log.info("COMPLETE node {} is detected", osNode.hostname());
-                        processCompleteNode(event.subject());
-                    });
+                    log.info("COMPLETE node {} is detected", osNode.hostname());
+                    executor.execute(() -> processCompleteNode(event.subject()));
                     break;
                 case OPENSTACK_NODE_INCOMPLETE:
                     log.warn("{} is changed to INCOMPLETE state", osNode);
@@ -405,23 +392,20 @@
                         log.debug("Instance port {} is detected from {}",
                                 port.annotations().value(PORT_NAME),
                                 osNode.hostname());
-                        processPortAdded(port,
-                                deviceService.getDevice(osNode.intgBridge()));
+                        processPortAdded(port);
                     });
 
-            portNamePrefixMap().values().forEach(portNamePrefix -> {
-                deviceService.getPorts(osNode.intgBridge()).stream()
-                        .filter(port -> port.annotations().value(PORT_NAME)
-                                .startsWith(portNamePrefix) &&
-                                port.isEnabled())
-                        .forEach(port -> {
-                            log.debug("Instance port {} is detected from {}",
-                                    port.annotations().value(portNamePrefix),
-                                    osNode.hostname());
-                            processPortAdded(port,
-                                    deviceService.getDevice(osNode.intgBridge()));
-                        });
-            });
+            portNamePrefixMap().values().forEach(portNamePrefix ->
+                    deviceService.getPorts(osNode.intgBridge()).stream()
+                    .filter(port -> port.annotations().value(PORT_NAME)
+                            .startsWith(portNamePrefix) &&
+                            port.isEnabled())
+                    .forEach(port -> {
+                        log.debug("Instance port {} is detected from {}",
+                                port.annotations().value(portNamePrefix),
+                                osNode.hostname());
+                        processPortAdded(port);
+                    }));
 
             Tools.stream(hostService.getHosts())
                     .filter(host -> deviceService.getPort(
@@ -429,7 +413,7 @@
                             host.location().port()) == null)
                     .forEach(host -> {
                         log.info("Remove stale host {}", host.id());
-                        hostProvider.hostVanished(host.id());
+                        hostProviderService.hostVanished(host.id());
                     });
         }
     }
diff --git a/apps/openstacknetworking/app/src/test/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingHostProviderTest.java b/apps/openstacknetworking/app/src/test/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingHostProviderTest.java
new file mode 100644
index 0000000..e968d26
--- /dev/null
+++ b/apps/openstacknetworking/app/src/test/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingHostProviderTest.java
@@ -0,0 +1,368 @@
+/*
+ * Copyright 2018-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.onosproject.openstacknetworking.impl;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import com.google.common.util.concurrent.MoreExecutors;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.junit.TestUtils;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreServiceAdapter;
+import org.onosproject.core.DefaultApplicationId;
+import org.onosproject.mastership.MastershipServiceAdapter;
+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.PortNumber;
+import org.onosproject.net.device.DeviceServiceAdapter;
+import org.onosproject.net.host.DefaultHostDescription;
+import org.onosproject.net.host.HostDescription;
+import org.onosproject.net.host.HostProvider;
+import org.onosproject.net.host.HostProviderRegistry;
+import org.onosproject.net.host.HostProviderService;
+import org.onosproject.net.host.HostServiceAdapter;
+import org.onosproject.net.provider.ProviderId;
+import org.openstack4j.model.network.Network;
+import org.openstack4j.model.network.NetworkType;
+import org.openstack4j.model.network.Port;
+import org.openstack4j.openstack.networking.domain.NeutronNetwork;
+import org.openstack4j.openstack.networking.domain.NeutronPort;
+
+import java.util.Map;
+import java.util.Set;
+
+import static org.junit.Assert.assertEquals;
+import static org.onosproject.openstacknetworking.api.Constants.ANNOTATION_NETWORK_ID;
+import static org.onosproject.openstacknetworking.api.Constants.ANNOTATION_PORT_ID;
+
+/**
+ * Unit tests for Openstack Switching Host Provider.
+ */
+public class OpenstackSwitchingHostProviderTest {
+
+    private static final String PORT_ID = "65c0ee9f-d634-4522-8954-51021b570b0d";
+    private static final String NETWORK_ID = "396f12f8-521e-4b91-8e21-2e003500433a";
+    private static final String IP_ADDRESS = "10.10.10.2";
+    private static final String SUBNET_ID = "d32019d3-bc6e-4319-9c1d-6722fc136a22";
+    private static final String MAC_ADDRESS = "00:11:22:33:44:55";
+    private static final String MAC_ADDRESS2 = "11:22:33:44:55:66";
+
+    private static final ProviderId PID = new ProviderId("of", "foo");
+    private static final DefaultAnnotations ANNOTATIONS =
+                                    DefaultAnnotations.builder()
+                                            .set(ANNOTATION_NETWORK_ID, NETWORK_ID)
+                                            .set(ANNOTATION_PORT_ID, PORT_ID).build();
+
+    // Host Mac, VLAN
+    private static final ProviderId PROVIDER_ID = ProviderId.NONE;
+    private static final MacAddress HOST_MAC = MacAddress.valueOf(MAC_ADDRESS);
+    private static final MacAddress HOST_MAC2 = MacAddress.valueOf(MAC_ADDRESS2);
+
+    private static final VlanId HOST_VLAN_UNTAGGED = VlanId.NONE;
+    private static final HostId HOST_ID_UNTAGGED = HostId.hostId(HOST_MAC, HOST_VLAN_UNTAGGED);
+
+    private static final String SEGMENT_ID = "1";
+
+    // Host IP
+    private static final IpAddress HOST_IP11 = IpAddress.valueOf("10.0.1.1");
+
+    // Device
+    private static final DeviceId DEV_ID1 = DeviceId.deviceId("of:0000000000000001");
+    private static final DeviceId DEV_ID2 = DeviceId.deviceId("of:0000000000000002");
+
+    private static final Device DEV1 =
+            new DefaultDevice(PID, DEV_ID1, Device.Type.SWITCH, "", "", "", "", null);
+    private static final Device DEV2 =
+            new DefaultDevice(PID, DEV_ID2, Device.Type.SWITCH, "", "", "", "", null);
+
+    // Port
+    private static final PortNumber P1 = PortNumber.portNumber(1);
+    private static final PortNumber P2 = PortNumber.portNumber(2);
+
+    // Connect Point
+    private static final ConnectPoint CP11 = new ConnectPoint(DEV_ID1, P1);
+    private static final HostLocation HOST_LOC11 = new HostLocation(CP11, 0);
+    private static final ConnectPoint CP12 = new ConnectPoint(DEV_ID1, P2);
+    private static final HostLocation HOST_LOC12 = new HostLocation(CP12, 0);
+    private static final ConnectPoint CP21 = new ConnectPoint(DEV_ID2, P1);
+
+    private Map<HostId, Host> hostMap = Maps.newHashMap();
+    private Map<ConnectPoint, MacAddress> macMap = Maps.newHashMap();
+
+    private OpenstackSwitchingHostProvider target;
+
+    private HostDescription resultHostDesc;
+    private HostId resultHostId;
+    private HostLocation resultHostLocation;
+
+    /**
+     * Initial setup for this unit test.
+     */
+    @Before
+    public void setUp() {
+        target = new OpenstackSwitchingHostProvider();
+        TestUtils.setField(target, "coreService", new TestCoreService());
+        TestUtils.setField(target, "deviceService", new TestDeviceService());
+        TestUtils.setField(target, "hostService", new TestHostService());
+        TestUtils.setField(target, "mastershipService", new TestMastershipService());
+        TestUtils.setField(target, "osNodeService", new TestOpenstackNodeService());
+        TestUtils.setField(target, "osNetworkService", new TestOpenstackNetworkService());
+        TestUtils.setField(target, "hostProviderRegistry", new TestHostProviderRegistry());
+        TestUtils.setField(target, "executor", MoreExecutors.newDirectExecutorService());
+
+        Host host1 = new DefaultHost(PROVIDER_ID, HOST_ID_UNTAGGED, HOST_MAC, HOST_VLAN_UNTAGGED,
+                Sets.newHashSet(HOST_LOC11), Sets.newHashSet(HOST_IP11), false);
+        hostMap.put(HOST_ID_UNTAGGED, host1);
+
+        macMap.put(CP11, HOST_MAC);
+        macMap.put(CP12, HOST_MAC);
+        macMap.put(CP21, HOST_MAC2);
+
+        target.activate();
+    }
+
+    /**
+     * Tears down this unit test.
+     */
+    @After
+    public void tearDown() {
+        target.deactivate();
+        target = null;
+    }
+
+    /**
+     * Tests the process port added method for new addition case.
+     */
+    @Test
+    public void testProcessPortAddedForNewAddition() {
+        org.onosproject.net.Port port = new DefaultPort(DEV2, P1, true, ANNOTATIONS);
+        target.processPortAdded(port);
+
+        HostId hostId = HostId.hostId(HOST_MAC2);
+        HostDescription hostDesc = new DefaultHostDescription(
+                HOST_MAC2,
+                VlanId.NONE,
+                new HostLocation(CP21, System.currentTimeMillis()),
+                ImmutableSet.of(),
+                ANNOTATIONS
+
+        );
+
+        verifyHostResult(hostId, hostDesc);
+    }
+
+    /**
+     * Tests the process port added method for updating case.
+     */
+    @Test
+    public void testProcessPortAddedForUpdate() {
+        org.onosproject.net.Port port = new DefaultPort(DEV1, P1, true, ANNOTATIONS);
+        target.processPortAdded(port);
+
+        HostId hostId = HostId.hostId(HOST_MAC);
+        HostDescription hostDesc = new DefaultHostDescription(
+                HOST_MAC,
+                VlanId.NONE,
+                new HostLocation(CP11, System.currentTimeMillis()),
+                ImmutableSet.of(),
+                ANNOTATIONS
+        );
+
+        verifyHostResult(hostId, hostDesc);
+    }
+
+    /**
+     * Tests the process port added method for migration case.
+     */
+    @Test
+    public void testProcessPortAddedForMigration() {
+        org.onosproject.net.Port port = new DefaultPort(DEV1, P2, true, ANNOTATIONS);
+        target.processPortAdded(port);
+
+        HostId hostId = HostId.hostId(HOST_MAC);
+
+        verifyHostLocationResult(hostId, HOST_LOC12);
+    }
+
+    /**
+     * Mocks the CoreService.
+     */
+    private class TestCoreService extends CoreServiceAdapter {
+
+        @Override
+        public ApplicationId registerApplication(String name) {
+            return new DefaultApplicationId(100, "hostProviderTestApp");
+        }
+    }
+
+    /**
+     * Mocks the DeviceService.
+     */
+    private class TestDeviceService extends DeviceServiceAdapter {
+    }
+
+    /**
+     * Mocks the HostService.
+     */
+    private class TestHostService extends HostServiceAdapter {
+
+        @Override
+        public Host getHost(HostId hostId) {
+            return hostMap.get(hostId);
+        }
+    }
+
+    /**
+     * Mocks the HostProviderService.
+     */
+    private class TestHostProviderService implements HostProviderService {
+
+        @Override
+        public void hostDetected(HostId hostId, HostDescription hostDescription,
+                                 boolean replaceIps) {
+            resultHostId = hostId;
+            resultHostDesc = hostDescription;
+        }
+
+        @Override
+        public void hostVanished(HostId hostId) {
+
+        }
+
+        @Override
+        public void removeIpFromHost(HostId hostId, IpAddress ipAddress) {
+
+        }
+
+        @Override
+        public void removeLocationFromHost(HostId hostId, HostLocation location) {
+
+        }
+
+        @Override
+        public void addLocationToHost(HostId hostId, HostLocation location) {
+            resultHostId = hostId;
+            resultHostLocation = location;
+        }
+
+        @Override
+        public HostProvider provider() {
+            return null;
+        }
+    }
+
+    /**
+     * Mocks the HostProviderRegistry.
+     */
+    private class TestHostProviderRegistry implements HostProviderRegistry {
+
+        @Override
+        public HostProviderService register(HostProvider provider) {
+            return new TestHostProviderService();
+        }
+
+        @Override
+        public void unregister(HostProvider provider) {
+
+        }
+
+        @Override
+        public Set<ProviderId> getProviders() {
+            return null;
+        }
+    }
+
+    /**
+     * Mocks the MastershipService.
+     */
+    private class TestMastershipService extends MastershipServiceAdapter {
+    }
+
+    /**
+     * Mocks the OpenstackNodeService.
+     */
+    private class TestOpenstackNodeService extends OpenstackNodeServiceAdapter {
+    }
+
+    /**
+     * Mocks the OpenstackNetworkService.
+     */
+    private class TestOpenstackNetworkService extends OpenstackNetworkServiceAdapter {
+
+        @Override
+        public Port port(org.onosproject.net.Port port) {
+            Port osPort = NeutronPort.builder()
+                                        .networkId(NETWORK_ID)
+                                        .fixedIp(IP_ADDRESS, SUBNET_ID)
+                                        .macAddress(macMap.get(
+                                                new ConnectPoint(port.element().id(),
+                                                        port.number())).toString())
+                                        .build();
+            osPort.setId(PORT_ID);
+
+            return osPort;
+        }
+
+        @Override
+        public Network network(String networkId) {
+            Network osNetwork = NeutronNetwork.builder()
+                                        .networkType(NetworkType.VXLAN)
+                                        .segmentId(SEGMENT_ID)
+                                        .build();
+            osNetwork.setId(NETWORK_ID);
+            return osNetwork;
+        }
+    }
+
+    /**
+     * Verifies the HostId and HostDescription.
+     *
+     * @param hostId        host identifier
+     * @param hostDesc      host description
+     */
+    private void verifyHostResult(HostId hostId, HostDescription hostDesc) {
+        assertEquals(hostId, resultHostId);
+        assertEquals(hostDesc.hwAddress(), resultHostDesc.hwAddress());
+        assertEquals(hostDesc.annotations().value(NETWORK_ID),
+                resultHostDesc.annotations().value(NETWORK_ID));
+    }
+
+    /**
+     * Verifies the HostId and HostLocation.
+     *
+     * @param hostId        host identifier
+     * @param hostLocation  host location
+     */
+    private void verifyHostLocationResult(HostId hostId, HostLocation hostLocation) {
+        assertEquals(hostId, resultHostId);
+        assertEquals(hostLocation.deviceId(), resultHostLocation.deviceId());
+        assertEquals(hostLocation.port(), resultHostLocation.port());
+    }
+}