Extend Network Config Host Provider to support multihoming

Change-Id: I6e9dd18a5189a7bf35a617a00bd46e4a32acf524
diff --git a/core/api/src/main/java/org/onosproject/net/config/basics/BasicHostConfig.java b/core/api/src/main/java/org/onosproject/net/config/basics/BasicHostConfig.java
index cb99aaf..7b870b4 100644
--- a/core/api/src/main/java/org/onosproject/net/config/basics/BasicHostConfig.java
+++ b/core/api/src/main/java/org/onosproject/net/config/basics/BasicHostConfig.java
@@ -19,6 +19,7 @@
 import org.onlab.packet.IpAddress;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.HostId;
+import org.onosproject.net.HostLocation;
 
 import java.util.HashSet;
 import java.util.Set;
@@ -29,15 +30,15 @@
 public final class BasicHostConfig extends BasicElementConfig<HostId> {
 
     private static final String IPS = "ips";
-    private static final String LOCATION = "location";
+    private static final String LOCATIONS = "locations";
 
     @Override
     public boolean isValid() {
         // Location and IP addresses can be absent, but if present must be valid.
-        this.location();
+        this.locations();
         this.ipAddresses();
         return hasOnlyFields(ALLOWED, NAME, LOC_TYPE, LATITUDE, LONGITUDE,
-                GRID_Y, GRID_Y, UI_TYPE, RACK_ADDRESS, OWNER, IPS, LOCATION);
+                GRID_Y, GRID_Y, UI_TYPE, RACK_ADDRESS, OWNER, IPS, LOCATIONS);
     }
 
     /**
@@ -46,19 +47,27 @@
      * @return location of the host or null if not set
      * @throws IllegalArgumentException if not specified with correct format
      */
-    public ConnectPoint location() {
-        String location = get(LOCATION, null);
-        return location != null ? ConnectPoint.deviceConnectPoint(location) : null;
+    public Set<HostLocation> locations() {
+        HashSet<HostLocation> locations = new HashSet<>();
+        if (object.has(LOCATIONS)) {
+            ArrayNode locationNodes = (ArrayNode) object.path(LOCATIONS);
+            locationNodes.forEach(n -> {
+                ConnectPoint cp = ConnectPoint.deviceConnectPoint((n.asText()));
+                locations.add(new HostLocation(cp, 0));
+            });
+            return locations;
+        }
+        return null;
     }
 
     /**
      * Sets the location of the host.
      *
-     * @param location location of the host or null to unset
+     * @param locations location of the host or null to unset
      * @return the config of the host
      */
-    public BasicHostConfig setLocation(String location) {
-        return (BasicHostConfig) setOrClear(LOCATION, location);
+    public BasicHostConfig setLocations(Set<HostLocation> locations) {
+        return (BasicHostConfig) setOrClear(LOCATIONS, locations);
     }
 
     /**
diff --git a/core/net/src/main/java/org/onosproject/net/host/impl/BasicHostOperator.java b/core/net/src/main/java/org/onosproject/net/host/impl/BasicHostOperator.java
index 87f908a..7fcfa55 100644
--- a/core/net/src/main/java/org/onosproject/net/host/impl/BasicHostOperator.java
+++ b/core/net/src/main/java/org/onosproject/net/host/impl/BasicHostOperator.java
@@ -16,7 +16,6 @@
 package org.onosproject.net.host.impl;
 
 import org.onlab.packet.IpAddress;
-import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.DefaultAnnotations;
 import org.onosproject.net.Host;
 import org.onosproject.net.HostLocation;
@@ -27,6 +26,7 @@
 import org.onosproject.net.host.HostDescription;
 
 import java.util.Set;
+import java.util.stream.Collectors;
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
@@ -53,10 +53,12 @@
             return descr;
         }
 
-        HostLocation location = descr.location();
-        ConnectPoint cfgLocation = cfg.location();
-        if (cfgLocation != null) {
-            location = new HostLocation(cfgLocation, System.currentTimeMillis());
+        Set<HostLocation> locations = descr.locations();
+        Set<HostLocation> cfgLocations = cfg.locations();
+        if (cfgLocations != null) {
+            locations = cfgLocations.stream()
+                    .map(hostLocation -> new HostLocation(hostLocation, System.currentTimeMillis()))
+                    .collect(Collectors.toSet());
         }
 
         Set<IpAddress> ipAddresses = descr.ipAddress();
@@ -67,7 +69,7 @@
 
         SparseAnnotations sa = combine(cfg, descr.annotations());
         return new DefaultHostDescription(descr.hwAddress(), descr.vlan(),
-                                          location, ipAddresses,
+                                          locations, ipAddresses,
                                           descr.configured(), sa);
     }
 
diff --git a/providers/netcfghost/src/main/java/org/onosproject/provider/netcfghost/NetworkConfigHostProvider.java b/providers/netcfghost/src/main/java/org/onosproject/provider/netcfghost/NetworkConfigHostProvider.java
index 5e7c4e9..59e8234 100644
--- a/providers/netcfghost/src/main/java/org/onosproject/provider/netcfghost/NetworkConfigHostProvider.java
+++ b/providers/netcfghost/src/main/java/org/onosproject/provider/netcfghost/NetworkConfigHostProvider.java
@@ -26,7 +26,6 @@
 import org.onlab.packet.VlanId;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
-import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.Host;
 import org.onosproject.net.HostId;
 import org.onosproject.net.HostLocation;
@@ -43,7 +42,10 @@
 import org.onosproject.net.provider.ProviderId;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+
+import java.util.Collections;
 import java.util.Set;
+import java.util.stream.Collectors;
 
 /**
  * Host provider that uses network config service to discover hosts.
@@ -106,14 +108,14 @@
      *
      * @param mac MAC address of the host
      * @param vlan VLAN ID of the host
-     * @param hloc Location of the host
+     * @param locations Location of the host
      * @param ips Set of IP addresses of the host
      */
-    protected void addHost(MacAddress mac, VlanId vlan, HostLocation hloc, Set<IpAddress> ips) {
+    protected void addHost(MacAddress mac, VlanId vlan, Set<HostLocation> locations, Set<IpAddress> ips) {
         HostId hid = HostId.hostId(mac, vlan);
         HostDescription desc = (ips != null) ?
-                new DefaultHostDescription(mac, vlan, hloc, ips, true) :
-                new DefaultHostDescription(mac, vlan, hloc, true);
+                new DefaultHostDescription(mac, vlan, locations, ips, true) :
+                new DefaultHostDescription(mac, vlan, locations, Collections.emptySet(), true);
         providerService.hostDetected(hid, desc, false);
     }
 
@@ -123,12 +125,12 @@
      *
      * @param mac MAC address of the host
      * @param vlan VLAN ID of the host
-     * @param hloc Location of the host
+     * @param locations Location of the host
      * @param ips Set of IP addresses of the host
      */
-    protected void updateHost(MacAddress mac, VlanId vlan, HostLocation hloc, Set<IpAddress> ips) {
+    protected void updateHost(MacAddress mac, VlanId vlan, Set<HostLocation> locations, Set<IpAddress> ips) {
         HostId hid = HostId.hostId(mac, vlan);
-        HostDescription desc = new DefaultHostDescription(mac, vlan, hloc, ips, true);
+        HostDescription desc = new DefaultHostDescription(mac, vlan, locations, ips, true);
         providerService.hostDetected(hid, desc, true);
     }
 
@@ -150,9 +152,10 @@
             BasicHostConfig hostConfig =
                     networkConfigRegistry.getConfig(hostId, BasicHostConfig.class);
             Set<IpAddress> ipAddresses = hostConfig.ipAddresses();
-            ConnectPoint location = hostConfig.location();
-            HostLocation hloc = new HostLocation(location, System.currentTimeMillis());
-            addHost(mac, vlan, hloc, ipAddresses);
+            Set<HostLocation> locations = hostConfig.locations().stream()
+                    .map(hostLocation -> new HostLocation(hostLocation, System.currentTimeMillis()))
+                    .collect(Collectors.toSet());
+            addHost(mac, vlan, locations, ipAddresses);
         });
     }
 
@@ -172,21 +175,22 @@
             BasicHostConfig hostConfig =
                     networkConfigRegistry.getConfig(hostId, BasicHostConfig.class);
             Set<IpAddress> ipAddresses = null;
-            HostLocation hloc = null;
+            Set<HostLocation> locations = null;
 
             // Note: There will be no config presented in the CONFIG_REMOVE case
             if (hostConfig != null) {
                 ipAddresses = hostConfig.ipAddresses();
-                ConnectPoint location = hostConfig.location();
-                hloc = new HostLocation(location, System.currentTimeMillis());
+                locations = hostConfig.locations().stream()
+                        .map(hostLocation -> new HostLocation(hostLocation, System.currentTimeMillis()))
+                        .collect(Collectors.toSet());
             }
 
             switch (event.type()) {
                 case CONFIG_ADDED:
-                    addHost(mac, vlan, hloc, ipAddresses);
+                    addHost(mac, vlan, locations, ipAddresses);
                     break;
                 case CONFIG_UPDATED:
-                    updateHost(mac, vlan, hloc, ipAddresses);
+                    updateHost(mac, vlan, locations, ipAddresses);
                     break;
                 case CONFIG_REMOVED:
                     removeHost(mac, vlan);
diff --git a/providers/netcfghost/src/test/java/org/onosproject/provider/netcfghost/NetworkConfigHostProviderTest.java b/providers/netcfghost/src/test/java/org/onosproject/provider/netcfghost/NetworkConfigHostProviderTest.java
index 81422c8..ac47dba 100644
--- a/providers/netcfghost/src/test/java/org/onosproject/provider/netcfghost/NetworkConfigHostProviderTest.java
+++ b/providers/netcfghost/src/test/java/org/onosproject/provider/netcfghost/NetworkConfigHostProviderTest.java
@@ -16,6 +16,7 @@
 
 package org.onosproject.provider.netcfghost;
 
+import com.google.common.collect.Sets;
 import org.junit.Before;
 import org.junit.Test;
 import org.onlab.packet.IpAddress;
@@ -49,7 +50,7 @@
     private VlanId vlan = VlanId.vlanId(VlanId.UNTAGGED);
     private DeviceId deviceId = DeviceId.deviceId("of:0000000000000001");
     private PortNumber port = PortNumber.portNumber(5);
-    private HostLocation hloc = new HostLocation(deviceId, port, 100);
+    private Set<HostLocation> locations = Sets.newHashSet(new HostLocation(deviceId, port, 100));
     private Set<IpAddress> ips = new HashSet<>();
     private HostId hostId = HostId.hostId(mac, vlan);
     private HostDescription hostDescription;
@@ -61,12 +62,12 @@
         // Initialize test variables
         ips.add(IpAddress.valueOf("10.0.0.1"));
         ips.add(IpAddress.valueOf("192.168.0.1"));
-        hostDescription = new DefaultHostDescription(mac, vlan, hloc, ips);
+        hostDescription = new DefaultHostDescription(mac, vlan, locations, ips, true);
     }
 
     @Test
     public void testAddHost() throws Exception {
-        provider.addHost(mac, vlan, hloc, ips);
+        provider.addHost(mac, vlan, locations, ips);
         assertThat(providerService.hostId, is(hostId));
         assertThat(providerService.hostDescription, is(hostDescription));
         assertThat(providerService.event, is("hostDetected"));
@@ -75,7 +76,7 @@
 
     @Test
     public void testUpdateHost() throws Exception {
-        provider.updateHost(mac, vlan, hloc, ips);
+        provider.updateHost(mac, vlan, locations, ips);
         assertThat(providerService.hostId, is(hostId));
         assertThat(providerService.hostDescription, is(hostDescription));
         assertThat(providerService.event, is("hostDetected"));