[CORD-1887] Make DHCP relay works with dual homing

Change-Id: I479a292d6d6d4820798ed660cc51826c3b12ede3
diff --git a/apps/dhcprelay/src/test/java/org/onosproject/dhcprelay/DhcpRelayManagerTest.java b/apps/dhcprelay/src/test/java/org/onosproject/dhcprelay/DhcpRelayManagerTest.java
index a179e63..f45555d 100644
--- a/apps/dhcprelay/src/test/java/org/onosproject/dhcprelay/DhcpRelayManagerTest.java
+++ b/apps/dhcprelay/src/test/java/org/onosproject/dhcprelay/DhcpRelayManagerTest.java
@@ -76,6 +76,7 @@
 import org.onosproject.net.flowobjective.FlowObjectiveService;
 import org.onosproject.net.flowobjective.ForwardingObjective;
 import org.onosproject.net.flowobjective.Objective;
+import org.onosproject.net.host.HostProviderService;
 import org.onosproject.net.intf.Interface;
 import org.onosproject.net.intf.InterfaceServiceAdapter;
 import org.onosproject.net.packet.PacketPriority;
@@ -87,9 +88,7 @@
 import org.onosproject.net.HostLocation;
 import org.onosproject.net.config.NetworkConfigRegistry;
 import org.onosproject.net.host.HostDescription;
-import org.onosproject.net.host.HostEvent;
 import org.onosproject.net.host.HostService;
-import org.onosproject.net.host.HostStoreAdapter;
 import org.onosproject.net.host.InterfaceIpAddress;
 import org.onosproject.net.packet.DefaultInboundPacket;
 import org.onosproject.net.packet.InboundPacket;
@@ -98,7 +97,6 @@
 import org.onosproject.net.packet.PacketContextAdapter;
 import org.onosproject.net.packet.PacketProcessor;
 import org.onosproject.net.packet.PacketServiceAdapter;
-import org.onosproject.net.provider.ProviderId;
 import org.onosproject.store.StoreDelegate;
 import org.osgi.service.component.ComponentContext;
 import org.onlab.packet.DHCP6;
@@ -138,12 +136,28 @@
     private static final VlanId CLIENT_VLAN = VlanId.vlanId("100");
     private static final ConnectPoint CLIENT_CP = ConnectPoint.deviceConnectPoint("of:0000000000000001/1");
     private static final MacAddress CLIENT_IFACE_MAC = MacAddress.valueOf("00:00:00:00:11:01");
+    private static final HostLocation CLIENT_LOCATION = new HostLocation(CLIENT_CP, 0);
+    private static final HostId CLIENT_HOST_ID = HostId.hostId(CLIENT_MAC, CLIENT_VLAN);
+    private static final Ip6Address CLIENT_LL_IP_V6 = Ip6Address.valueOf("fe80::200:00ff:fe00:0001");
+    private static final Host EXISTS_HOST = new DefaultHost(DhcpRelayManager.PROVIDER_ID,
+                                                            CLIENT_HOST_ID, CLIENT_MAC, CLIENT_VLAN,
+                                                            CLIENT_LOCATION, ImmutableSet.of(CLIENT_LL_IP_V6));
     private static final Interface CLIENT_INTERFACE = new Interface("C1",
                                                                     CLIENT_CP,
                                                                     INTERFACE_IPS,
                                                                     CLIENT_IFACE_MAC,
                                                                     CLIENT_VLAN);
 
+    // Dual homing test
+    private static final ConnectPoint CLIENT_DH_CP = ConnectPoint.deviceConnectPoint("of:0000000000000001/3");
+    private static final HostLocation CLIENT_DH_LOCATION = new HostLocation(CLIENT_DH_CP, 0);
+    private static final Interface CLIENT_DH_INTERFACE = new Interface("C1-DH",
+                                                                       CLIENT_DH_CP,
+                                                                       INTERFACE_IPS,
+                                                                       CLIENT_IFACE_MAC,
+                                                                       CLIENT_VLAN);
+
+
     // DHCP client 2 (will send with option 82, so the vlan should equals to vlan from server)
     private static final MacAddress CLIENT2_MAC = MacAddress.valueOf("00:00:00:00:00:01");
     private static final VlanId CLIENT2_VLAN = VlanId.NONE;
@@ -208,7 +222,8 @@
     private static final Set<Interface> INTERFACES = ImmutableSet.of(
             CLIENT_INTERFACE,
             CLIENT2_INTERFACE,
-            SERVER_INTERFACE
+            SERVER_INTERFACE,
+            CLIENT_DH_INTERFACE
     );
     private static final String NON_ONOS_CID = "Non-ONOS circuit ID";
     private static final VlanId IGNORED_VLAN = VlanId.vlanId("100");
@@ -216,9 +231,9 @@
 
     private DhcpRelayManager manager;
     private MockPacketService packetService;
-    private MockHostStore mockHostStore;
     private MockRouteStore mockRouteStore;
     private MockDhcpRelayStore mockDhcpRelayStore;
+    private HostProviderService mockHostProviderService;
 
     @Before
     public void setup() {
@@ -254,20 +269,17 @@
         expect(manager.deviceService.getDevice(DEV_2_ID)).andReturn(device).anyTimes();
         replay(manager.deviceService, device);
 
-        mockHostStore = new MockHostStore();
         mockRouteStore = new MockRouteStore();
         mockDhcpRelayStore = new MockDhcpRelayStore();
         manager.dhcpRelayStore = mockDhcpRelayStore;
 
         manager.interfaceService = new MockInterfaceService();
         manager.flowObjectiveService = EasyMock.niceMock(FlowObjectiveService.class);
-
-
-
+        mockHostProviderService = createNiceMock(HostProviderService.class);
         Dhcp4HandlerImpl v4Handler = new Dhcp4HandlerImpl();
+        v4Handler.providerService = mockHostProviderService;
         v4Handler.dhcpRelayStore = mockDhcpRelayStore;
         v4Handler.hostService = manager.hostService;
-        v4Handler.hostStore = mockHostStore;
         v4Handler.interfaceService = manager.interfaceService;
         v4Handler.packetService = manager.packetService;
         v4Handler.routeStore = mockRouteStore;
@@ -278,10 +290,10 @@
         Dhcp6HandlerImpl v6Handler = new Dhcp6HandlerImpl();
         v6Handler.dhcpRelayStore = mockDhcpRelayStore;
         v6Handler.hostService = manager.hostService;
-        v6Handler.hostStore = mockHostStore;
         v6Handler.interfaceService = manager.interfaceService;
         v6Handler.packetService = manager.packetService;
         v6Handler.routeStore = mockRouteStore;
+        v6Handler.providerService = mockHostProviderService;
         manager.v6Handler = v6Handler;
 
         // properties
@@ -306,33 +318,36 @@
      */
     @Test
     public void relayDhcpWithoutAgentInfo() {
+        replay(mockHostProviderService);
         // send request
         packetService.processPacket(new TestDhcpRequestPacketContext(CLIENT_MAC,
                                                                      CLIENT_VLAN,
                                                                      CLIENT_CP,
                                                                      INTERFACE_IP.ipAddress().getIp4Address(),
                                                                      false));
+        // won't trigger the host provider service
+        verify(mockHostProviderService);
+        reset(mockHostProviderService);
 
-        Set<Host> hosts = ImmutableSet.copyOf(mockHostStore.getHosts());
-        assertEquals(0, hosts.size());
         assertEquals(0, mockRouteStore.routes.size());
 
+        HostId expectHostId = HostId.hostId(CLIENT_MAC, CLIENT_VLAN);
+        Capture<HostDescription> capturedHostDesc = newCapture();
+        mockHostProviderService.hostDetected(eq(expectHostId), capture(capturedHostDesc), eq(false));
+        replay(mockHostProviderService);
         // send ack
         packetService.processPacket(new TestDhcpAckPacketContext(CLIENT_CP, CLIENT_MAC,
                                                                  CLIENT_VLAN, INTERFACE_IP.ipAddress().getIp4Address(),
                                                                  false));
-        hosts = ImmutableSet.copyOf(mockHostStore.getHosts());
-        assertEquals(1, hosts.size());
+        verify(mockHostProviderService);
         assertEquals(0, mockRouteStore.routes.size());
 
-        Host host = hosts.iterator().next();
-        assertEquals(CLIENT_MAC, host.mac());
-        assertEquals(CLIENT_VLAN, host.vlan());
+        HostDescription host = capturedHostDesc.getValue();
+        assertEquals(false, host.configured());
         assertEquals(CLIENT_CP.deviceId(), host.location().elementId());
         assertEquals(CLIENT_CP.port(), host.location().port());
-        assertEquals(1, host.ipAddresses().size());
-        assertEquals(IP_FOR_CLIENT, host.ipAddresses().iterator().next());
-        assertEquals(HostId.hostId(CLIENT_MAC, CLIENT_VLAN), host.id());
+        assertEquals(1, host.ipAddress().size());
+        assertEquals(IP_FOR_CLIENT, host.ipAddress().iterator().next());
     }
 
     /**
@@ -340,6 +355,7 @@
      */
     @Test
     public void relayDhcpWithAgentInfo() {
+        replay(mockHostProviderService);
         // Assume outer dhcp relay agent exists in store already
         // send request
         packetService.processPacket(new TestDhcpRequestPacketContext(CLIENT2_MAC,
@@ -347,11 +363,8 @@
                                                                      CLIENT2_CP,
                                                                      INTERFACE_IP.ipAddress().getIp4Address(),
                                                                      true));
-
-        Set<Host> hosts = ImmutableSet.copyOf(mockHostStore.getHosts());
-        assertEquals(0, hosts.size());
+        // No routes
         assertEquals(0, mockRouteStore.routes.size());
-
         // send ack
         packetService.processPacket(new TestDhcpAckPacketContext(CLIENT2_CP,
                                                                  CLIENT2_MAC,
@@ -359,8 +372,10 @@
                                                                  INTERFACE_IP.ipAddress().getIp4Address(),
                                                                  true));
 
-        hosts = ImmutableSet.copyOf(mockHostStore.getHosts());
-        assertEquals(0, hosts.size());
+        // won't trigger the host provider service
+        verify(mockHostProviderService);
+        reset(mockHostProviderService);
+
         assertEquals(1, mockRouteStore.routes.size());
 
         Route route = mockRouteStore.routes.get(0);
@@ -562,6 +577,7 @@
      */
     @Test
     public void relayDhcp6WithoutAgentInfo() {
+        replay(mockHostProviderService);
         // send request
         packetService.processPacket(new TestDhcp6RequestPacketContext(CLIENT_MAC,
                                                                      VlanId.NONE,
@@ -569,27 +585,28 @@
                                                                      INTERFACE_IP_V6.ipAddress().getIp6Address(),
                                                                      0));
 
-        Set<Host> hosts = ImmutableSet.copyOf(mockHostStore.getHosts());
-        assertEquals(0, hosts.size());
+        verify(mockHostProviderService);
+        reset(mockHostProviderService);
         assertEquals(0, mockRouteStore.routes.size());
 
+        Capture<HostDescription> capturedHostDesc = newCapture();
+        mockHostProviderService.hostDetected(eq(HostId.hostId(CLIENT_MAC, CLIENT_VLAN)),
+                                             capture(capturedHostDesc), eq(false));
+        replay(mockHostProviderService);
         // send reply
         packetService.processPacket(new TestDhcp6ReplyPacketContext(CLIENT_CP, CLIENT_MAC,
                                                                     CLIENT_VLAN,
                                                                     INTERFACE_IP_V6.ipAddress().getIp6Address(),
                                                                     0));
-        hosts = ImmutableSet.copyOf(mockHostStore.getHosts());
-        assertEquals(1, hosts.size());
+        verify(mockHostProviderService);
         assertEquals(0, mockRouteStore.routes.size());
 
-        Host host = hosts.iterator().next();
-        assertEquals(CLIENT_MAC, host.mac());
+        HostDescription host = capturedHostDesc.getValue();
         assertEquals(CLIENT_VLAN, host.vlan());
         assertEquals(CLIENT_CP.deviceId(), host.location().elementId());
         assertEquals(CLIENT_CP.port(), host.location().port());
-        assertEquals(1, host.ipAddresses().size());
-        assertEquals(IP_FOR_CLIENT_V6, host.ipAddresses().iterator().next());
-        assertEquals(HostId.hostId(CLIENT_MAC, CLIENT_VLAN), host.id());
+        assertEquals(1, host.ipAddress().size());
+        assertEquals(IP_FOR_CLIENT_V6, host.ipAddress().iterator().next());
     }
 
     /**
@@ -597,6 +614,7 @@
      */
     @Test
     public void relayDhcp6WithAgentInfo() {
+        replay(mockHostProviderService);
         // Assume outer dhcp6 relay agent exists in store already
         // send request
         packetService.processPacket(new TestDhcp6RequestPacketContext(CLIENT2_MAC,
@@ -605,8 +623,6 @@
                 INTERFACE_IP_V6.ipAddress().getIp6Address(),
                 1));
 
-        Set<Host> hosts = ImmutableSet.copyOf(mockHostStore.getHosts());
-        assertEquals(0, hosts.size());
         assertEquals(0, mockRouteStore.routes.size());
 
         // send reply
@@ -616,8 +632,9 @@
                 INTERFACE_IP_V6.ipAddress().getIp6Address(),
                 1));
 
-        hosts = ImmutableSet.copyOf(mockHostStore.getHosts());
-        assertEquals(0, hosts.size());
+        // won't trigger the host provider service
+        verify(mockHostProviderService);
+        reset(mockHostProviderService);
         assertEquals(2, mockRouteStore.routes.size()); // ipAddress and prefix
 
         Route route = mockRouteStore.routes.get(0);
@@ -625,6 +642,64 @@
         assertEquals(IP_FOR_CLIENT_V6.toIpPrefix(), route.prefix());
         assertEquals(Route.Source.STATIC, route.source());
     }
+
+    @Test
+    public void testDhcp4DualHome() {
+        PacketContext packetContext =
+                new TestDhcpAckPacketContext(CLIENT_DH_CP, CLIENT_MAC, CLIENT_VLAN,
+                                             INTERFACE_IP.ipAddress().getIp4Address(),
+                                             false);
+        reset(manager.hostService);
+        expect(manager.hostService.getHost(CLIENT_HOST_ID)).andReturn(EXISTS_HOST).anyTimes();
+        Capture<HostDescription> capturedHostDesc = newCapture();
+        mockHostProviderService.hostDetected(eq(CLIENT_HOST_ID), capture(capturedHostDesc), eq(false));
+        replay(mockHostProviderService, manager.hostService);
+        packetService.processPacket(packetContext);
+        verify(mockHostProviderService);
+
+        HostDescription hostDesc = capturedHostDesc.getValue();
+        Set<HostLocation> hostLocations = hostDesc.locations();
+        assertEquals(2, hostLocations.size());
+        assertTrue(hostLocations.contains(CLIENT_LOCATION));
+        assertTrue(hostLocations.contains(CLIENT_DH_LOCATION));
+    }
+
+    @Test
+    public void testDhcp6DualHome() {
+        PacketContext packetContext =
+                new TestDhcp6ReplyPacketContext(CLIENT_DH_CP, CLIENT_MAC, CLIENT_VLAN,
+                                                INTERFACE_IP_V6.ipAddress().getIp6Address(),
+                                                0);
+        reset(manager.hostService);
+        expect(manager.hostService.getHostsByIp(CLIENT_LL_IP_V6)).andReturn(ImmutableSet.of(EXISTS_HOST)).anyTimes();
+
+        // FIXME: currently DHCPv6 has a bug, we can't get correct vlan of client......
+        // XXX: The vlan relied from DHCP6 handler might be wrong, do hack here
+        HostId hostId = HostId.hostId(CLIENT_MAC, VlanId.NONE);
+        expect(manager.hostService.getHost(hostId)).andReturn(EXISTS_HOST).anyTimes();
+
+        // XXX: sometimes this will work, sometimes not
+         expect(manager.hostService.getHost(CLIENT_HOST_ID)).andReturn(EXISTS_HOST).anyTimes();
+
+        Capture<HostDescription> capturedHostDesc = newCapture();
+
+        // XXX: also a hack here
+        mockHostProviderService.hostDetected(eq(hostId), capture(capturedHostDesc), eq(false));
+        expectLastCall().anyTimes();
+
+        mockHostProviderService.hostDetected(eq(CLIENT_HOST_ID), capture(capturedHostDesc), eq(false));
+        expectLastCall().anyTimes();
+        replay(mockHostProviderService, manager.hostService);
+        packetService.processPacket(packetContext);
+        verify(mockHostProviderService);
+
+        HostDescription hostDesc = capturedHostDesc.getValue();
+        Set<HostLocation> hostLocations = hostDesc.locations();
+        assertEquals(2, hostLocations.size());
+        assertTrue(hostLocations.contains(CLIENT_LOCATION));
+        assertTrue(hostLocations.contains(CLIENT_DH_LOCATION));
+    }
+
     private static class MockDefaultDhcpRelayConfig extends DefaultDhcpRelayConfig {
         @Override
         public boolean isValid() {
@@ -692,36 +767,6 @@
         }
     }
 
-    private class MockHostStore extends HostStoreAdapter {
-
-        private final Map<HostId, HostDescription> hosts = Maps.newHashMap();
-
-        @Override
-        public HostEvent createOrUpdateHost(ProviderId providerId, HostId hostId,
-                                            HostDescription hostDescription,
-                                            boolean replaceIps) {
-            hosts.put(hostId, hostDescription);
-
-            // not necessary to return host event in this test.
-            return null;
-        }
-
-        public HostDescription hostDesc(HostId hostId) {
-            return hosts.get(hostId);
-        }
-
-        @Override
-        public Iterable<Host> getHosts() {
-            return hosts.values().stream()
-                    .map(hd -> new DefaultHost(DhcpRelayManager.PROVIDER_ID,
-                                               HostId.hostId(hd.hwAddress(), hd.vlan()),
-                                               hd.hwAddress(),
-                                               hd.vlan(), hd.locations(),
-                                               hd.ipAddress(), false))
-                    .collect(Collectors.toList());
-        }
-    }
-
     private class MockRouteStore extends RouteStoreAdapter {
         private List<Route> routes = Lists.newArrayList();
 
@@ -1231,10 +1276,10 @@
             byte[] interfaceId = buildInterfaceId(clientMac, clientCp);
             DHCP6 dhcp6 = new DHCP6();
             buildRelayMsg(dhcp6, DHCP6.MsgType.RELAY_REPL.value(),
-                    INTERFACE_IP_V6.ipAddress().getIp6Address(),
-                    OUTER_RELAY_IP_V6,
-                    (byte) 0, interfaceId,
-                    dhcp6Payload);
+                          INTERFACE_IP_V6.ipAddress().getIp6Address(),
+                          CLIENT_LL_IP_V6,
+                          (byte) 0, interfaceId,
+                          dhcp6Payload);
 
             DHCP6 dhcp6Parent = null;
             DHCP6 dhcp6Child = dhcp6;