Extend host structures to store multiple locations

Also update host location format in CLI and REST API

Change-Id: I0fbd655f642627dd3eb8a2925f83a3ee016fe4aa
diff --git a/core/api/src/main/java/org/onosproject/net/DefaultHost.java b/core/api/src/main/java/org/onosproject/net/DefaultHost.java
index d9cfa25..defb5a3 100644
--- a/core/api/src/main/java/org/onosproject/net/DefaultHost.java
+++ b/core/api/src/main/java/org/onosproject/net/DefaultHost.java
@@ -21,6 +21,7 @@
 import org.onlab.packet.VlanId;
 
 import java.util.Collections;
+import java.util.Comparator;
 import java.util.HashSet;
 import java.util.Objects;
 import java.util.Set;
@@ -34,7 +35,7 @@
 
     private final MacAddress mac;
     private final VlanId vlan;
-    private final HostLocation location;
+    private final Set<HostLocation> locations;
     private final Set<IpAddress> ips;
     private final boolean configured;
 
@@ -70,10 +71,29 @@
     public DefaultHost(ProviderId providerId, HostId id, MacAddress mac,
                        VlanId vlan, HostLocation location, Set<IpAddress> ips,
                        boolean configured, Annotations... annotations) {
+        this(providerId, id, mac, vlan, Collections.singleton(location), ips,
+                configured, annotations);
+    }
+
+    /**
+     * Creates an end-station host using the supplied information.
+     *
+     * @param providerId  provider identity
+     * @param id          host identifier
+     * @param mac         host MAC address
+     * @param vlan        host VLAN identifier
+     * @param locations   set of host locations
+     * @param ips         host IP addresses
+     * @param configured  true if configured via NetworkConfiguration
+     * @param annotations optional key/value annotations
+     */
+    public DefaultHost(ProviderId providerId, HostId id, MacAddress mac,
+                       VlanId vlan, Set<HostLocation> locations, Set<IpAddress> ips,
+                       boolean configured, Annotations... annotations) {
         super(providerId, id, annotations);
         this.mac = mac;
         this.vlan = vlan;
-        this.location = location;
+        this.locations = new HashSet<>(locations);
         this.ips = new HashSet<>(ips);
         this.configured = configured;
     }
@@ -99,7 +119,14 @@
 
     @Override
     public HostLocation location() {
-        return location;
+        return locations.stream()
+                .sorted(Comparator.comparingLong(HostLocation::time).reversed())
+                .findFirst().orElse(null);
+    }
+
+    @Override
+    public Set<HostLocation> locations() {
+        return locations;
     }
 
     @Override
@@ -114,7 +141,7 @@
 
     @Override
     public int hashCode() {
-        return Objects.hash(id, mac, vlan, location);
+        return Objects.hash(id, mac, vlan, locations);
     }
 
     @Override
@@ -127,7 +154,7 @@
             return Objects.equals(this.id, other.id) &&
                     Objects.equals(this.mac, other.mac) &&
                     Objects.equals(this.vlan, other.vlan) &&
-                    Objects.equals(this.location, other.location) &&
+                    Objects.equals(this.locations, other.locations) &&
                     Objects.equals(this.ipAddresses(), other.ipAddresses()) &&
                     Objects.equals(this.annotations(), other.annotations());
         }
@@ -140,7 +167,7 @@
                 .add("id", id())
                 .add("mac", mac())
                 .add("vlan", vlan())
-                .add("location", location())
+                .add("locations", locations())
                 .add("ipAddresses", ipAddresses())
                 .add("annotations", annotations())
                 .add("configured", configured())
diff --git a/core/api/src/main/java/org/onosproject/net/Host.java b/core/api/src/main/java/org/onosproject/net/Host.java
index 5094727..86ef79f 100644
--- a/core/api/src/main/java/org/onosproject/net/Host.java
+++ b/core/api/src/main/java/org/onosproject/net/Host.java
@@ -59,11 +59,18 @@
      * Returns the most recent host location where the host attaches to the
      * network edge.
      *
-     * @return host location
+     * @return the most recent host location
      */
     HostLocation location();
 
     /**
+     * Returns all host locations where the host attaches to the network edge.
+     *
+     * @return all host locations
+     */
+    Set<HostLocation> locations();
+
+    /**
      * Returns true if configured by NetworkConfiguration.
      * @return configured/learnt dynamically
      */
@@ -73,4 +80,3 @@
     // TODO: explore capturing list of recent locations to aid in mobility
 
 }
-
diff --git a/core/api/src/main/java/org/onosproject/net/HostLocation.java b/core/api/src/main/java/org/onosproject/net/HostLocation.java
index f7868c6..94409b6 100644
--- a/core/api/src/main/java/org/onosproject/net/HostLocation.java
+++ b/core/api/src/main/java/org/onosproject/net/HostLocation.java
@@ -64,4 +64,8 @@
         return time;
     }
 
+    @Override
+    public String toString() {
+        return deviceId() + "/" + port();
+    }
 }
diff --git a/core/api/src/main/java/org/onosproject/net/host/DefaultHostDescription.java b/core/api/src/main/java/org/onosproject/net/host/DefaultHostDescription.java
index 5f9cfd8..c2531f6 100644
--- a/core/api/src/main/java/org/onosproject/net/host/DefaultHostDescription.java
+++ b/core/api/src/main/java/org/onosproject/net/host/DefaultHostDescription.java
@@ -16,6 +16,8 @@
 package org.onosproject.net.host;
 
 import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashSet;
 import java.util.Set;
 
 import org.onosproject.net.AbstractDescription;
@@ -38,7 +40,7 @@
 
     private final MacAddress mac;
     private final VlanId vlan;
-    private final HostLocation location;
+    private final Set<HostLocation> locations;
     private final Set<IpAddress> ip;
     private final boolean configured;
 
@@ -53,8 +55,7 @@
     public DefaultHostDescription(MacAddress mac, VlanId vlan,
                                   HostLocation location,
                                   SparseAnnotations... annotations) {
-        this(mac, vlan, location, Collections.emptySet(),
-             annotations);
+        this(mac, vlan, location, Collections.emptySet(), annotations);
     }
 
     /**
@@ -100,8 +101,7 @@
                                   HostLocation location,
                                   boolean configured,
                                   SparseAnnotations... annotations) {
-        this(mac, vlan, location, Collections.<IpAddress>emptySet(),
-             configured, annotations);
+        this(mac, vlan, location, Collections.emptySet(), configured, annotations);
     }
 
     /**
@@ -118,11 +118,28 @@
                                   HostLocation location, Set<IpAddress> ip,
                                   boolean configured,
                                   SparseAnnotations... annotations) {
+        this(mac, vlan, Collections.singleton(location), ip, configured, annotations);
+    }
+
+    /**
+     * Creates a host description using the supplied information.
+     *
+     * @param mac          host MAC address
+     * @param vlan         host VLAN identifier
+     * @param locations    host locations
+     * @param ip           host IP address
+     * @param configured   true if configured via NetworkConfiguration
+     * @param annotations  optional key/value annotations map
+     */
+    public DefaultHostDescription(MacAddress mac, VlanId vlan,
+                                  Set<HostLocation> locations,
+                                  Set<IpAddress> ip, boolean configured,
+                                  SparseAnnotations... annotations) {
         super(annotations);
         this.mac = mac;
         this.vlan = vlan;
-        this.location = location;
-        this.ip = ImmutableSet.copyOf(ip);
+        this.locations = new HashSet<>(locations);
+        this.ip = new HashSet<>(ip);
         this.configured = configured;
     }
 
@@ -138,7 +155,14 @@
 
     @Override
     public HostLocation location() {
-        return location;
+        return locations.stream()
+                .sorted(Comparator.comparingLong(HostLocation::time).reversed())
+                .findFirst().orElse(null);
+    }
+
+    @Override
+    public Set<HostLocation> locations() {
+        return locations;
     }
 
     @Override
@@ -156,7 +180,7 @@
         return toStringHelper(this)
                 .add("mac", mac)
                 .add("vlan", vlan)
-                .add("location", location)
+                .add("locations", locations)
                 .add("ipAddress", ip)
                 .add("configured", configured)
                 .toString();
@@ -164,7 +188,7 @@
 
     @Override
     public int hashCode() {
-        return Objects.hashCode(super.hashCode(), mac, vlan, location, ip);
+        return Objects.hashCode(super.hashCode(), mac, vlan, locations, ip);
     }
 
     @Override
@@ -176,7 +200,7 @@
             DefaultHostDescription that = (DefaultHostDescription) object;
             return Objects.equal(this.mac, that.mac)
                     && Objects.equal(this.vlan, that.vlan)
-                    && Objects.equal(this.location, that.location)
+                    && Objects.equal(this.locations, that.locations)
                     && Objects.equal(this.ip, that.ip);
         }
         return false;
diff --git a/core/api/src/main/java/org/onosproject/net/host/HostDescription.java b/core/api/src/main/java/org/onosproject/net/host/HostDescription.java
index d7687ac..bba9dc9 100644
--- a/core/api/src/main/java/org/onosproject/net/host/HostDescription.java
+++ b/core/api/src/main/java/org/onosproject/net/host/HostDescription.java
@@ -43,13 +43,20 @@
     VlanId vlan();
 
     /**
-     * Returns the location of the host on the network edge.
+     * Returns the most recent location of the host on the network edge.
      *
-     * @return the network location
+     * @return the most recent host location
      */
     HostLocation location();
 
     /**
+     * Returns all locations of the host on the network edge.
+     *
+     * @return all host locations
+     */
+    Set<HostLocation> locations();
+
+    /**
      * Returns the IP address associated with this host's MAC.
      *
      * @return host IP address
diff --git a/core/api/src/test/java/org/onosproject/net/DefaultHostTest.java b/core/api/src/test/java/org/onosproject/net/DefaultHostTest.java
index 62012ba..e36d0e2 100644
--- a/core/api/src/test/java/org/onosproject/net/DefaultHostTest.java
+++ b/core/api/src/test/java/org/onosproject/net/DefaultHostTest.java
@@ -16,12 +16,19 @@
 package org.onosproject.net;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
 
+import com.google.common.collect.ImmutableSet;
+import com.google.common.testing.EqualsTester;
+import java.util.Set;
 import org.junit.Test;
 
-import com.google.common.testing.EqualsTester;
-
-public class DefaultHostTest extends  TestDeviceParams {
+public class DefaultHostTest extends TestDeviceParams {
+    private static final Set<HostLocation> LOCATIONS = ImmutableSet.of(LOC1, LOC2, LOC3);
+    private static final Host SINGLE_HOMED_HOST =
+            new DefaultHost(PID, HID1, MAC1, VLAN1, LOC1, IPSET1);
+    private static final Host MULTI_HOMED_HOST =
+            new DefaultHost(PID, HID1, MAC1, VLAN1, LOCATIONS, IPSET1, false);
 
     @Test
     public void testEquality() {
@@ -39,13 +46,27 @@
 
     @Test
     public void basics() {
-        Host host = new DefaultHost(PID, HID1, MAC1, VLAN1, LOC1, IPSET1);
-        assertEquals("incorrect provider", PID, host.providerId());
-        assertEquals("incorrect id", HID1, host.id());
-        assertEquals("incorrect type", MAC1, host.mac());
-        assertEquals("incorrect VLAN", VLAN1, host.vlan());
-        assertEquals("incorrect location", LOC1, host.location());
-        assertEquals("incorrect IP's", IPSET1, host.ipAddresses());
+        assertEquals("incorrect provider", PID, SINGLE_HOMED_HOST.providerId());
+        assertEquals("incorrect id", HID1, SINGLE_HOMED_HOST.id());
+        assertEquals("incorrect type", MAC1, SINGLE_HOMED_HOST.mac());
+        assertEquals("incorrect VLAN", VLAN1, SINGLE_HOMED_HOST.vlan());
+        assertEquals("incorrect location", LOC1, SINGLE_HOMED_HOST.location());
+        assertEquals("incorrect IPs", IPSET1, SINGLE_HOMED_HOST.ipAddresses());
+    }
+
+    @Test
+    public void testLocation() {
+        assertEquals("Latest location should be LOC3", LOC3, MULTI_HOMED_HOST.location());
+    }
+
+    @Test
+    public void testLocations() {
+        Set<HostLocation> locations = MULTI_HOMED_HOST.locations();
+
+        assertEquals("There should be 3 locations", locations.size(), 3);
+        assertTrue("Host location contains 1st location", locations.contains(LOC1));
+        assertTrue("Host location contains 2nd location", locations.contains(LOC2));
+        assertTrue("Host location contains 3rd location", locations.contains(LOC3));
     }
 
 }
diff --git a/core/api/src/test/java/org/onosproject/net/TestDeviceParams.java b/core/api/src/test/java/org/onosproject/net/TestDeviceParams.java
index 2de364a..4fa7baf 100644
--- a/core/api/src/test/java/org/onosproject/net/TestDeviceParams.java
+++ b/core/api/src/test/java/org/onosproject/net/TestDeviceParams.java
@@ -51,7 +51,8 @@
     protected static final HostId HID1 = HostId.hostId(MAC1, VLAN1);
     protected static final HostId HID2 = HostId.hostId(MAC2, VLAN2);
     protected static final HostLocation LOC1 = new HostLocation(DID1, P1, 123L);
-    protected static final HostLocation LOC2 = new HostLocation(DID2, P2, 123L);
+    protected static final HostLocation LOC2 = new HostLocation(DID2, P2, 456L);
+    protected static final HostLocation LOC3 = new HostLocation(DID3, P1, 789L);
     protected static final Set<IpAddress> IPSET1 = Sets.newHashSet(IP1, IP2);
     protected static final Set<IpAddress> IPSET2 = Sets.newHashSet(IP1, IP3);
 
diff --git a/core/api/src/test/java/org/onosproject/net/host/DefaultHostDecriptionTest.java b/core/api/src/test/java/org/onosproject/net/host/DefaultHostDecriptionTest.java
index 26a1cde..0b36035 100644
--- a/core/api/src/test/java/org/onosproject/net/host/DefaultHostDecriptionTest.java
+++ b/core/api/src/test/java/org/onosproject/net/host/DefaultHostDecriptionTest.java
@@ -16,14 +16,12 @@
 package org.onosproject.net.host;
 
 import org.junit.Test;
-import org.onosproject.net.DeviceId;
 import org.onosproject.net.HostLocation;
-import org.onosproject.net.PortNumber;
-import org.onlab.packet.IpAddress;
-import org.onlab.packet.MacAddress;
-import org.onlab.packet.VlanId;
 
 import com.google.common.collect.ImmutableSet;
+import org.onosproject.net.TestDeviceParams;
+
+import java.util.Set;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
@@ -31,27 +29,34 @@
 /**
  * Test for the default host description.
  */
-public class DefaultHostDecriptionTest {
-
-    private static final MacAddress MAC = MacAddress.valueOf("00:00:11:00:00:01");
-    private static final VlanId VLAN = VlanId.vlanId((short) 10);
-    private static final IpAddress IP = IpAddress.valueOf("10.0.0.1");
-
-    private static final HostLocation LOC = new HostLocation(
-            DeviceId.deviceId("of:foo"),
-            PortNumber.portNumber(100),
-            123L
-    );
+public class DefaultHostDecriptionTest extends TestDeviceParams {
+    private static final Set<HostLocation> LOCATIONS = ImmutableSet.of(LOC1, LOC2, LOC3);
+    private static final HostDescription SINGLE_HOMED_HOST_DESCR =
+            new DefaultHostDescription(MAC1, VLAN1, LOC1, IP1);
+    private static final HostDescription MULTI_HOMED_HOST_DESCR =
+            new DefaultHostDescription(MAC1, VLAN1, LOCATIONS, IPSET1, false);
 
     @Test
     public void basics() {
-        HostDescription host =
-                new DefaultHostDescription(MAC, VLAN, LOC, IP);
-        assertEquals("incorrect mac", MAC, host.hwAddress());
-        assertEquals("incorrect vlan", VLAN, host.vlan());
-        assertEquals("incorrect location", LOC, host.location());
-        assertEquals("incorrect ip's", ImmutableSet.of(IP), host.ipAddress());
-        assertTrue("incorrect toString", host.toString().contains("vlan=10"));
+        assertEquals("incorrect mac", MAC1, SINGLE_HOMED_HOST_DESCR.hwAddress());
+        assertEquals("incorrect vlan", VLAN1, SINGLE_HOMED_HOST_DESCR.vlan());
+        assertEquals("incorrect location", LOC1, SINGLE_HOMED_HOST_DESCR.location());
+        assertEquals("incorrect IPs", ImmutableSet.of(IP1), SINGLE_HOMED_HOST_DESCR.ipAddress());
+        assertTrue("incorrect toString", SINGLE_HOMED_HOST_DESCR.toString().contains("vlan=11"));
     }
 
+    @Test
+    public void testLocation() {
+        assertEquals("Latest location should be LOC3", LOC3, MULTI_HOMED_HOST_DESCR.location());
+    }
+
+    @Test
+    public void testLocations() {
+        Set<HostLocation> locations = MULTI_HOMED_HOST_DESCR.locations();
+
+        assertEquals("There should be 3 locations", locations.size(), 3);
+        assertTrue("Host location contains 1st location", locations.contains(LOC1));
+        assertTrue("Host location contains 2nd location", locations.contains(LOC2));
+        assertTrue("Host location contains 3rd location", locations.contains(LOC3));
+    }
 }
diff --git a/core/api/src/test/java/org/onosproject/ui/topo/NodeSelectionTest.java b/core/api/src/test/java/org/onosproject/ui/topo/NodeSelectionTest.java
index 97f9843..63a4444 100644
--- a/core/api/src/test/java/org/onosproject/ui/topo/NodeSelectionTest.java
+++ b/core/api/src/test/java/org/onosproject/ui/topo/NodeSelectionTest.java
@@ -53,7 +53,7 @@
 
     private static class FakeHost extends DefaultHost {
         FakeHost(HostId id) {
-            super(null, id, null, null, null, ImmutableSet.of());
+            super(null, id, null, null, ImmutableSet.of(), ImmutableSet.of(), false);
         }
     }
 
diff --git a/core/common/src/main/java/org/onosproject/codec/impl/HostCodec.java b/core/common/src/main/java/org/onosproject/codec/impl/HostCodec.java
index b5f9b02..233d61d 100644
--- a/core/common/src/main/java/org/onosproject/codec/impl/HostCodec.java
+++ b/core/common/src/main/java/org/onosproject/codec/impl/HostCodec.java
@@ -47,7 +47,12 @@
             jsonIpAddresses.add(ipAddress.toString());
         }
         result.set("ipAddresses", jsonIpAddresses);
-        result.set("location", locationCodec.encode(host.location(), context));
+
+        final ArrayNode jsonLocations = result.putArray("locations");
+        for (final HostLocation location : host.locations()) {
+            jsonLocations.add(locationCodec.encode(location, context));
+        }
+        result.set("locations", jsonLocations);
 
         return annotate(result, host, context);
     }
diff --git a/core/common/src/main/java/org/onosproject/codec/impl/VirtualHostCodec.java b/core/common/src/main/java/org/onosproject/codec/impl/VirtualHostCodec.java
index 05ce81e..d6a7c80 100644
--- a/core/common/src/main/java/org/onosproject/codec/impl/VirtualHostCodec.java
+++ b/core/common/src/main/java/org/onosproject/codec/impl/VirtualHostCodec.java
@@ -49,7 +49,7 @@
     static final String MAC_ADDRESS = "mac";
     static final String VLAN = "vlan";
     static final String IP_ADDRESSES = "ipAddresses";
-    static final String HOST_LOCATION = "location";
+    static final String HOST_LOCATION = "locations";
 
     private static final String NULL_OBJECT_MSG = "VirtualHost cannot be null";
     private static final String MISSING_MEMBER_MSG = " member is required in VirtualHost";
@@ -71,7 +71,12 @@
             jsonIpAddresses.add(ipAddress.toString());
         }
         result.set(IP_ADDRESSES, jsonIpAddresses);
-        result.set(HOST_LOCATION, locationCodec.encode(vHost.location(), context));
+
+        final ArrayNode jsonLocations = result.putArray("locations");
+        for (final HostLocation location : vHost.locations()) {
+            jsonLocations.add(locationCodec.encode(location, context));
+        }
+        result.set("locations", jsonLocations);
 
         return result;
     }
@@ -85,10 +90,15 @@
         NetworkId nId = NetworkId.networkId(Long.parseLong(extractMember(NETWORK_ID, json)));
         MacAddress mac = MacAddress.valueOf(json.get("mac").asText());
         VlanId vlanId = VlanId.vlanId((short) json.get("vlan").asInt(VlanId.UNTAGGED));
-        JsonNode locationNode = json.get("location");
-        PortNumber portNumber = PortNumber.portNumber(locationNode.get("port").asText());
-        DeviceId deviceId = DeviceId.deviceId(locationNode.get("elementId").asText());
-        HostLocation hostLocation = new HostLocation(deviceId, portNumber, 0);
+
+        Set<HostLocation> locations = new HashSet<>();
+        JsonNode locationNodes = json.get("locations");
+        locationNodes.forEach(locationNode -> {
+            PortNumber portNumber = PortNumber.portNumber(locationNode.get("port").asText());
+            DeviceId deviceId = DeviceId.deviceId(locationNode.get("elementId").asText());
+            locations.add(new HostLocation(deviceId, portNumber, 0));
+        });
+
         HostId id = HostId.hostId(mac, vlanId);
 
         Iterator<JsonNode> ipStrings = json.get("ipAddresses").elements();
@@ -97,7 +107,7 @@
             ips.add(IpAddress.valueOf(ipStrings.next().asText()));
         }
 
-        return new DefaultVirtualHost(nId, id, mac, vlanId, hostLocation, ips);
+        return new DefaultVirtualHost(nId, id, mac, vlanId, locations, ips);
     }
 
     /**
diff --git a/core/common/src/test/java/org/onosproject/codec/impl/VirtualHostCodecTest.java b/core/common/src/test/java/org/onosproject/codec/impl/VirtualHostCodecTest.java
index 1774242..661a1f3 100644
--- a/core/common/src/test/java/org/onosproject/codec/impl/VirtualHostCodecTest.java
+++ b/core/common/src/test/java/org/onosproject/codec/impl/VirtualHostCodecTest.java
@@ -81,9 +81,9 @@
                    is(TEST_MAC_ADDRESS));
         assertThat(node.get(VirtualHostCodec.VLAN).asInt(),
                    is((int) TEST_VLAN_ID));
-        assertThat(node.get(VirtualHostCodec.HOST_LOCATION).get("elementId").asText(),
+        assertThat(node.get(VirtualHostCodec.HOST_LOCATION).get(0).get("elementId").asText(),
                    is(location.deviceId().toString()));
-        assertThat(node.get(VirtualHostCodec.HOST_LOCATION).get("port").asLong(),
+        assertThat(node.get(VirtualHostCodec.HOST_LOCATION).get(0).get("port").asLong(),
                    is(location.port().toLong()));
 
         JsonNode jsonIps = node.get(VirtualHostCodec.IP_ADDRESSES);
diff --git a/core/common/src/test/resources/org/onosproject/codec/impl/VirtualHost.json b/core/common/src/test/resources/org/onosproject/codec/impl/VirtualHost.json
index ab5a81b..818ce0b 100644
--- a/core/common/src/test/resources/org/onosproject/codec/impl/VirtualHost.json
+++ b/core/common/src/test/resources/org/onosproject/codec/impl/VirtualHost.json
@@ -6,8 +6,10 @@
     "1.1.1.1",
     "2.2.2.2"
   ],
-  "location": {
-    "elementId": "of:d1",
-    "port": "1"
-  }
+  "locations": [
+    {
+      "elementId": "of:d1",
+      "port": "1"
+    }
+  ]
 }
diff --git a/core/store/dist/src/main/java/org/onosproject/store/host/impl/DistributedHostStore.java b/core/store/dist/src/main/java/org/onosproject/store/host/impl/DistributedHostStore.java
index c65ca4b..64d24a7 100644
--- a/core/store/dist/src/main/java/org/onosproject/store/host/impl/DistributedHostStore.java
+++ b/core/store/dist/src/main/java/org/onosproject/store/host/impl/DistributedHostStore.java
@@ -160,7 +160,7 @@
         if (!Objects.equals(existingHost.providerId(), providerId) ||
                 !Objects.equals(existingHost.mac(), hostDescription.hwAddress()) ||
                 !Objects.equals(existingHost.vlan(), hostDescription.vlan()) ||
-                !Objects.equals(existingHost.location(), hostDescription.location())) {
+                !Objects.equals(existingHost.locations(), hostDescription.locations())) {
             return true;
         }
 
@@ -194,7 +194,6 @@
                        existingHost -> shouldUpdate(existingHost, providerId,
                                                     hostDescription, replaceIPs),
                        (id, existingHost) -> {
-                           HostLocation location = hostDescription.location();
 
                            final Set<IpAddress> addresses;
                            if (existingHost == null || replaceIPs) {
@@ -219,7 +218,7 @@
                                                   hostId,
                                                   hostDescription.hwAddress(),
                                                   hostDescription.vlan(),
-                                                  location,
+                                                  hostDescription.locations(),
                                                   addresses,
                                                   configured,
                                                   annotations);
@@ -253,8 +252,9 @@
                             hostId,
                             existingHost.mac(),
                             existingHost.vlan(),
-                            existingHost.location(),
+                            existingHost.locations(),
                             ImmutableSet.copyOf(addresses),
+                            existingHost.configured(),
                             existingHost.annotations());
                 } else {
                     return existingHost;
@@ -299,7 +299,7 @@
     @Override
     public Set<Host> getConnectedHosts(ConnectPoint connectPoint) {
         Set<Host> filtered = hosts.entrySet().stream()
-                .filter(entry -> entry.getValue().location().equals(connectPoint))
+                .filter(entry -> entry.getValue().locations().contains(connectPoint))
                 .map(Map.Entry::getValue)
                 .collect(Collectors.toSet());
         return ImmutableSet.copyOf(filtered);
@@ -308,7 +308,8 @@
     @Override
     public Set<Host> getConnectedHosts(DeviceId deviceId) {
         Set<Host> filtered = hosts.entrySet().stream()
-                .filter(entry -> entry.getValue().location().deviceId().equals(deviceId))
+                .filter(entry -> entry.getValue().locations().stream()
+                        .map(HostLocation::deviceId).anyMatch(dpid -> dpid.equals(deviceId)))
                 .map(Map.Entry::getValue)
                 .collect(Collectors.toSet());
         return ImmutableSet.copyOf(filtered);
@@ -382,7 +383,7 @@
                 case UPDATE:
                     updateHostsByIp(host);
                     DefaultHost prevHost = checkNotNull(event.oldValue().value());
-                    if (!Objects.equals(prevHost.location(), host.location())) {
+                    if (!Objects.equals(prevHost.locations(), host.locations())) {
                         notifyDelegate(new HostEvent(HOST_MOVED, host, prevHost));
                     } else if (!Objects.equals(prevHost, host)) {
                         notifyDelegate(new HostEvent(HOST_UPDATED, host, prevHost));