Use effectiveLocations rather than locations while processing host events

Change-Id: I5918c2ba6297939453dfbecd46a0d49f23c4d2a9
diff --git a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/HostHandler.java b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/HostHandler.java
index c62e395..dcc77f8 100644
--- a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/HostHandler.java
+++ b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/HostHandler.java
@@ -82,7 +82,7 @@
     }
 
     private void initHost(Host host, DeviceId deviceId) {
-        host.locations().forEach(location -> {
+        effectiveLocations(host).forEach(location -> {
             if (location.deviceId().equals(deviceId) ||
                     location.deviceId().equals(srManager.getPairDeviceId(deviceId).orElse(null))) {
                 processHostAddedAtLocation(host, location);
@@ -96,10 +96,10 @@
     }
 
     private void processHostAdded(Host host) {
-        host.locations().forEach(location -> processHostAddedAtLocation(host, location));
+        effectiveLocations(host).forEach(location -> processHostAddedAtLocation(host, location));
         // ensure dual-homed host locations have viable uplinks
-        if (host.locations().size() > 1 || srManager.singleHomedDown) {
-            host.locations().forEach(loc -> {
+        if (effectiveLocations(host).size() > 1 || srManager.singleHomedDown) {
+            effectiveLocations(host).forEach(loc -> {
                 if (srManager.mastershipService.isLocalMaster(loc.deviceId())) {
                     srManager.linkHandler.checkUplinksForHost(loc);
                 }
@@ -108,11 +108,11 @@
     }
 
     void processHostAddedAtLocation(Host host, HostLocation location) {
-        checkArgument(host.locations().contains(location), "{} is not a location of {}", location, host);
+        checkArgument(effectiveLocations(host).contains(location), "{} is not a location of {}", location, host);
 
         MacAddress hostMac = host.mac();
         VlanId hostVlanId = host.vlan();
-        Set<HostLocation> locations = host.locations();
+        Set<HostLocation> locations = effectiveLocations(host);
         Set<IpAddress> ips = host.ipAddresses();
         log.info("Host {}/{} is added at {}", hostMac, hostVlanId, locations);
 
@@ -132,7 +132,7 @@
         // This do not affect single-homed hosts since the flow will be blocked in
         // processBridgingRule or processRoutingRule due to VLAN or IP mismatch respectively
         srManager.getPairDeviceId(location.deviceId()).ifPresent(pairDeviceId -> {
-            if (host.locations().stream().noneMatch(l -> l.deviceId().equals(pairDeviceId))) {
+            if (effectiveLocations(host).stream().noneMatch(l -> l.deviceId().equals(pairDeviceId))) {
                 srManager.getPairLocalPort(pairDeviceId).ifPresent(pairRemotePort -> {
                     // NOTE: Since the pairLocalPort is trunk port, use assigned vlan of original port
                     //       when the host is untagged
@@ -170,7 +170,7 @@
     private void processHostRemoved(Host host) {
         MacAddress hostMac = host.mac();
         VlanId hostVlanId = host.vlan();
-        Set<HostLocation> locations = host.locations();
+        Set<HostLocation> locations = effectiveLocations(host);
         Set<IpAddress> ips = host.ipAddresses();
         log.info("Host {}/{} is removed from {}", hostMac, hostVlanId, locations);
 
@@ -223,12 +223,22 @@
     }
 
     private void processHostMovedEventInternal(HostEvent event) {
+        // This method will be called when one of the following value has changed:
+        // (1) locations (2) auxLocations or (3) both locations and auxLocations.
+        // We only need to proceed when effectiveLocation has changed.
+        Set<HostLocation> newLocations = effectiveLocations(event.subject());
+        Set<HostLocation> prevLocations = effectiveLocations(event.prevSubject());
+
+        if (newLocations.equals(prevLocations)) {
+            log.info("effectiveLocations of {} has not changed. Skipping {}", event.subject().id(), event);
+            return;
+        }
+
         Host host = event.subject();
+        Host prevHost = event.prevSubject();
         MacAddress hostMac = host.mac();
         VlanId hostVlanId = host.vlan();
-        Set<HostLocation> prevLocations = event.prevSubject().locations();
-        Set<IpAddress> prevIps = event.prevSubject().ipAddresses();
-        Set<HostLocation> newLocations = host.locations();
+        Set<IpAddress> prevIps = prevHost.ipAddresses();
         Set<IpAddress> newIps = host.ipAddresses();
         EthType hostTpid = host.tpid();
         boolean doubleTaggedHost = isDoubleTaggedHost(host);
@@ -385,7 +395,7 @@
         MacAddress hostMac = host.mac();
         VlanId hostVlanId = host.vlan();
         EthType hostTpid = host.tpid();
-        Set<HostLocation> locations = host.locations();
+        Set<HostLocation> locations = effectiveLocations(host);
         Set<IpAddress> prevIps = event.prevSubject().ipAddresses();
         Set<IpAddress> newIps = host.ipAddresses();
         log.info("Host {}/{} is updated", hostMac, hostVlanId);
@@ -451,6 +461,7 @@
      *
      * @param cp connect point
      */
+    // TODO Current implementation does not handle dual-homed hosts with auxiliary locations.
     void processPortUp(ConnectPoint cp) {
         if (cp.port().equals(srManager.getPairLocalPort(cp.deviceId()).orElse(null))) {
             return;
@@ -463,6 +474,7 @@
         }
     }
 
+    // TODO Current implementation does not handle dual-homed hosts with auxiliary locations.
     private void probingIfNecessary(Host host, DeviceId pairDeviceId, ConnectPoint cp) {
         if (isHostInVlanOfPort(host, pairDeviceId, cp)) {
             srManager.probingService.probeHost(host, cp, ProbeMode.DISCOVER);
@@ -482,7 +494,7 @@
         Set<VlanId> taggedVlan = srManager.interfaceService.getTaggedVlanId(cp);
 
         return taggedVlan.contains(host.vlan()) ||
-                (internalVlan != null && host.locations().stream()
+                (internalVlan != null && effectiveLocations(host).stream()
                         .filter(l -> l.deviceId().equals(deviceId))
                         .map(srManager::getInternalVlanId)
                         .anyMatch(internalVlan::equals));
@@ -496,6 +508,7 @@
      * @param pairDeviceId pair device id
      * @param pairRemotePort pair remote port
      */
+    // TODO Current implementation does not handle dual-homed hosts with auxiliary locations.
     private void probe(Host host, ConnectPoint location, DeviceId pairDeviceId, PortNumber pairRemotePort) {
         //Check if the host still exists in the host store
         if (hostService.getHost(host.id()) == null) {
@@ -621,7 +634,7 @@
     void populateAllDoubleTaggedHost() {
         log.info("Enabling routing for all double tagged hosts");
         Sets.newHashSet(srManager.hostService.getHosts()).stream().filter(this::isDoubleTaggedHost)
-                .forEach(h -> h.locations().forEach(l ->
+                .forEach(h -> effectiveLocations(h).forEach(l ->
                     h.ipAddresses().forEach(i ->
                         processDoubleTaggedRoutingRule(l.deviceId(), l.port(), h.mac(), h.innerVlan(),
                                 h.vlan(), h.tpid(), i, false)
@@ -633,7 +646,7 @@
     void revokeAllDoubleTaggedHost() {
         log.info("Disabling routing for all double tagged hosts");
         Sets.newHashSet(srManager.hostService.getHosts()).stream().filter(this::isDoubleTaggedHost)
-                .forEach(h -> h.locations().forEach(l ->
+                .forEach(h -> effectiveLocations(h).forEach(l ->
                     h.ipAddresses().forEach(i ->
                         processDoubleTaggedRoutingRule(l.deviceId(), l.port(), h.mac(), h.innerVlan(),
                             h.vlan(), h.tpid(), i, true)
@@ -674,6 +687,7 @@
      * @param popVlan true to pop Vlan tag at TrafficTreatment, false otherwise
      * @param install true to populate the objective, false to revoke
      */
+    // TODO Current implementation does not handle dual-homed hosts with auxiliary locations.
     void processIntfVlanUpdatedEvent(DeviceId deviceId, PortNumber portNum, VlanId vlanId,
                                      boolean popVlan, boolean install) {
         ConnectPoint connectPoint = new ConnectPoint(deviceId, portNum);
@@ -712,6 +726,7 @@
      * @param ipPrefixSet IP Prefixes added or removed
      * @param install true if IP Prefixes added, false otherwise
      */
+    // TODO Current implementation does not handle dual-homed hosts with auxiliary locations.
     void processIntfIpUpdatedEvent(ConnectPoint cp, Set<IpPrefix> ipPrefixSet, boolean install) {
         Set<Host> hosts = hostService.getConnectedHosts(cp);
 
@@ -752,8 +767,8 @@
     Set<PortNumber> getDualHomedHostPorts(DeviceId deviceId) {
         Set<PortNumber> dualHomedLocations = new HashSet<>();
         srManager.hostService.getConnectedHosts(deviceId).stream()
-            .filter(host -> host.locations().size() == 2)
-            .forEach(host -> host.locations().stream()
+            .filter(host -> effectiveLocations(host).size() == 2)
+            .forEach(host -> effectiveLocations(host).stream()
                      .filter(loc -> loc.deviceId().equals(deviceId))
                         .forEach(loc -> dualHomedLocations.add(loc.port())));
         return dualHomedLocations;
@@ -769,4 +784,14 @@
         return !host.innerVlan().equals(VlanId.NONE);
     }
 
+    /**
+     * Returns effective locations of given host.
+     *
+     * @param host host to check
+     * @return auxLocations of the host if exists, or locations of the host otherwise.
+     */
+    Set<HostLocation> effectiveLocations(Host host) {
+        return (host.auxLocations() != null) ? host.auxLocations() : host.locations();
+    }
+
 }
diff --git a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
index dba6141..97436ab 100644
--- a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
+++ b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
@@ -1455,6 +1455,10 @@
                 } else if (event.type() == HostEvent.Type.HOST_MOVED) {
                     hostHandler.processHostMovedEvent((HostEvent) event);
                     routeHandler.processHostMovedEvent((HostEvent) event);
+                } else if (event.type() == HostEvent.Type.HOST_AUX_MOVED) {
+                    hostHandler.processHostMovedEvent((HostEvent) event);
+                    // TODO RouteHandler also needs to process this event in order to
+                    //      support nexthops that has auxLocations
                 } else if (event.type() == HostEvent.Type.HOST_REMOVED) {
                     hostHandler.processHostRemovedEvent((HostEvent) event);
                 } else if (event.type() == HostEvent.Type.HOST_UPDATED) {
diff --git a/apps/segmentrouting/app/src/test/java/org/onosproject/segmentrouting/HostHandlerTest.java b/apps/segmentrouting/app/src/test/java/org/onosproject/segmentrouting/HostHandlerTest.java
index d97cbc7..0fa9217 100644
--- a/apps/segmentrouting/app/src/test/java/org/onosproject/segmentrouting/HostHandlerTest.java
+++ b/apps/segmentrouting/app/src/test/java/org/onosproject/segmentrouting/HostHandlerTest.java
@@ -24,6 +24,7 @@
 import org.easymock.EasyMock;
 import org.junit.Before;
 import org.junit.Test;
+import org.onlab.packet.EthType;
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.IpPrefix;
 import org.onlab.packet.MacAddress;
@@ -998,4 +999,16 @@
 
         verify(hostHandler.srManager.probingService);
     }
+
+    @Test
+    public void testEffectiveLocations() {
+        Host regularHost = new DefaultHost(PROVIDER_ID, HOST_ID_UNTAGGED, HOST_MAC, HOST_VLAN_TAGGED,
+                Sets.newHashSet(HOST_LOC11, HOST_LOC12), Sets.newHashSet(HOST_IP11), false);
+        Host auxHost = new DefaultHost(PROVIDER_ID, HOST_ID_UNTAGGED, HOST_MAC, HOST_VLAN_TAGGED,
+                Sets.newHashSet(HOST_LOC11, HOST_LOC12), Sets.newHashSet(HOST_LOC21, HOST_LOC22),
+                Sets.newHashSet(HOST_IP11), VlanId.NONE, EthType.EtherType.UNKNOWN.ethType(), false, false);
+
+        assertEquals(Sets.newHashSet(HOST_LOC11, HOST_LOC12), hostHandler.effectiveLocations(regularHost));
+        assertEquals(Sets.newHashSet(HOST_LOC21, HOST_LOC22), hostHandler.effectiveLocations(auxHost));
+    }
 }