Added CLI and REST support for auxLocations

Change-Id: I04e78f766dcbb18bce4a2f9160d3740ec2fbd846
(cherry picked from commit eb5bd4998289bf78862db927d58b76cb12979459)
diff --git a/cli/src/main/java/org/onosproject/cli/net/HostsListCommand.java b/cli/src/main/java/org/onosproject/cli/net/HostsListCommand.java
index 3a9f860..1eb846d 100644
--- a/cli/src/main/java/org/onosproject/cli/net/HostsListCommand.java
+++ b/cli/src/main/java/org/onosproject/cli/net/HostsListCommand.java
@@ -40,7 +40,7 @@
 public class HostsListCommand extends AbstractShellCommand {
 
     private static final String FMT =
-            "id=%s, mac=%s, locations=%s, vlan=%s, ip(s)=%s%s, innerVlan=%s, outerTPID=%s, " +
+            "id=%s, mac=%s, locations=%s, auxLocations=%s, vlan=%s, ip(s)=%s%s, innerVlan=%s, outerTPID=%s, " +
                     "provider=%s:%s, configured=%s";
 
     private static final String FMT_SHORT =
@@ -95,7 +95,7 @@
                   host.vlan(), host.ipAddresses());
         } else {
             print(FMT, host.id(), host.mac(),
-                  host.locations(),
+                  host.locations(), host.auxLocations(),
                   host.vlan(), host.ipAddresses(), annotations(host.annotations()),
                   host.innerVlan(), host.tpid().toString(),
                   host.providerId().scheme(), host.providerId().id(),
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 3d19003..6e546a8 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
@@ -36,6 +36,7 @@
 
     private static final String IPS = "ips";
     private static final String LOCATIONS = "locations";
+    private static final String AUX_LOCATIONS = "auxLocations";
     private static final String INNER_VLAN = "innerVlan";
     private static final String OUTER_TPID = "outerTpid";
     private static final String DASH = "-";
@@ -56,8 +57,9 @@
                               EthType.EtherType.VLAN.ethType().toShort());
         this.locations();
         this.ipAddresses();
+        this.auxLocations();
         return hasOnlyFields(ALLOWED, NAME, LOC_TYPE, LATITUDE, LONGITUDE, ROLES,
-                             GRID_X, GRID_Y, UI_TYPE, RACK_ADDRESS, OWNER, IPS, LOCATIONS,
+                             GRID_X, GRID_Y, UI_TYPE, RACK_ADDRESS, OWNER, IPS, LOCATIONS, AUX_LOCATIONS,
                              INNER_VLAN, OUTER_TPID);
     }
 
@@ -102,6 +104,29 @@
     }
 
     /**
+     * Returns the auxLocations of the host.
+     *
+     * @return auxLocations of the host or null if none specified
+     * @throws IllegalArgumentException if auxLocations are set but empty or not
+     *                                  specified with correct format
+     */
+    public Set<HostLocation> auxLocations() {
+        if (!object.has(AUX_LOCATIONS)) {
+            return null; //no auxLocations are specified
+        }
+
+        ImmutableSet.Builder<HostLocation> auxLocationsSetBuilder = ImmutableSet.<HostLocation>builder();
+
+        ArrayNode auxLocationNodes = (ArrayNode) object.path(AUX_LOCATIONS);
+        auxLocationNodes.forEach(n -> {
+            ConnectPoint cp = ConnectPoint.deviceConnectPoint((n.asText()));
+            auxLocationsSetBuilder.add(new HostLocation(cp, 0));
+        });
+
+        return auxLocationsSetBuilder.build();
+    }
+
+    /**
      * Sets the location of the host.
      *
      * @param locations location of the host or null to unset
@@ -112,6 +137,17 @@
     }
 
     /**
+     * Sets the auxLocations of the host.
+     *
+     * @param auxLocations auxLocations of the host or null to unset
+     * @return the config of the host
+     */
+    public BasicHostConfig setAuxLocations(Set<HostLocation> auxLocations) {
+        return (BasicHostConfig) setOrClear(AUX_LOCATIONS, auxLocations);
+    }
+
+
+    /**
      * Returns IP addresses of the host.
      *
      * @return IP addresses of the host or null if not set
diff --git a/core/api/src/test/java/org/onosproject/net/config/basics/BasicHostConfigTest.java b/core/api/src/test/java/org/onosproject/net/config/basics/BasicHostConfigTest.java
index dab2c9d..34b53f0 100644
--- a/core/api/src/test/java/org/onosproject/net/config/basics/BasicHostConfigTest.java
+++ b/core/api/src/test/java/org/onosproject/net/config/basics/BasicHostConfigTest.java
@@ -55,6 +55,11 @@
         HostLocation loc2 = new HostLocation(
                 NetTestTools.connectPoint("d2", 2), System.currentTimeMillis());
         Set<HostLocation> locs = ImmutableSet.of(loc1, loc2);
+        HostLocation loc3 = new HostLocation(
+                NetTestTools.connectPoint("d3", 1), System.currentTimeMillis());
+        HostLocation loc4 = new HostLocation(
+                NetTestTools.connectPoint("d4", 2), System.currentTimeMillis());
+        Set<HostLocation> auxLocations = ImmutableSet.of(loc3, loc4);
         VlanId vlanId = VlanId.vlanId((short) 10);
         EthType ethType = EthType.EtherType.lookup((short) 0x88a8).ethType();
 
@@ -62,6 +67,7 @@
 
         config.setIps(ips)
               .setLocations(locs)
+              .setAuxLocations(auxLocations)
               .setInnerVlan(vlanId)
               .setOuterTpid(ethType);
 
@@ -71,6 +77,8 @@
         assertThat(config.ipAddresses(), hasItems(ip1, ip2, ip3));
         assertThat(config.locations(), hasSize(2));
         assertThat(config.locations(), hasItems(loc1, loc2));
+        assertThat(config.auxLocations(), hasSize(2));
+        assertThat(config.auxLocations(), hasItems(loc3, loc4));
         assertThat(config.innerVlan(), is(vlanId));
         assertThat(config.outerTpid(), is(ethType));
     }
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 5f8ca9d..d2890a4 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
@@ -56,6 +56,14 @@
         }
         result.set("locations", jsonLocations);
 
+        if (host.auxLocations() != null) {
+            final ArrayNode jsonAuxLocations = result.putArray("auxLocations");
+            for (final HostLocation auxLocation : host.auxLocations()) {
+                jsonAuxLocations.add(locationCodec.encode(auxLocation, context));
+            }
+            result.set("auxLocations", jsonAuxLocations);
+        }
+
         return annotate(result, host, context);
     }
 
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 b196737..26bfdeb 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
@@ -61,6 +61,14 @@
                     .collect(Collectors.toSet());
         }
 
+        Set<HostLocation> auxLocations = descr.auxLocations();
+        Set<HostLocation> cfgAuxLocations = cfg.auxLocations();
+        if (cfgAuxLocations != null) {
+            auxLocations = cfgAuxLocations.stream()
+                    .map(hostLocation -> new HostLocation(hostLocation, System.currentTimeMillis()))
+                    .collect(Collectors.toSet());
+        }
+
         Set<IpAddress> ipAddresses = descr.ipAddress();
         Set<IpAddress> cfgIpAddresses = cfg.ipAddresses();
         if (cfgIpAddresses != null) {
@@ -69,7 +77,7 @@
 
         SparseAnnotations sa = combine(cfg, descr.annotations());
         return new DefaultHostDescription(descr.hwAddress(), descr.vlan(),
-                                          locations, ipAddresses, descr.innerVlan(),
+                                          locations, auxLocations, ipAddresses, descr.innerVlan(),
                                           descr.tpid(), 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 b820573..310c8fd 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
@@ -126,18 +126,19 @@
      *
      * @param mac       MAC address of the host
      * @param vlan      VLAN ID of the host
-     * @param locations Location of the host
+     * @param locations Locations of the host
+     * @param auxLocations auxiliary locations of the host
      * @param ips       Set of IP addresses of the host
      * @param innerVlan host inner VLAN identifier
      * @param outerTpid outer TPID of a host
      */
-    protected void addHost(MacAddress mac, VlanId vlan, Set<HostLocation> locations, Set<IpAddress> ips,
-                           VlanId innerVlan, EthType outerTpid) {
+    protected void addHost(MacAddress mac, VlanId vlan, Set<HostLocation> locations, Set<HostLocation> auxLocations,
+                           Set<IpAddress> ips, VlanId innerVlan, EthType outerTpid) {
         HostId hid = HostId.hostId(mac, vlan);
         HostDescription desc = (ips != null) ?
-                new DefaultHostDescription(mac, vlan, locations, ips,
+                new DefaultHostDescription(mac, vlan, locations, auxLocations, ips,
                                            innerVlan, outerTpid, true) :
-                new DefaultHostDescription(mac, vlan, locations, Collections.emptySet(),
+                new DefaultHostDescription(mac, vlan, locations, auxLocations, Collections.emptySet(),
                                            innerVlan, outerTpid, true);
         providerService.hostDetected(hid, desc, true);
     }
@@ -163,15 +164,16 @@
      *
      * @param mac       MAC address of the host
      * @param vlan      VLAN ID of the host
-     * @param locations Location of the host
+     * @param locations Locations of the host
+     * @param auxLocations auxiliary locations of the host
      * @param ips       Set of IP addresses of the host
      * @param innerVlan host inner VLAN identifier
      * @param outerTpid outer TPID of a host
      */
-    protected void updateHost(MacAddress mac, VlanId vlan, Set<HostLocation> locations, Set<IpAddress> ips,
-                              VlanId innerVlan, EthType outerTpid) {
+    protected void updateHost(MacAddress mac, VlanId vlan, Set<HostLocation> locations, Set<HostLocation> auxLocations,
+                              Set<IpAddress> ips, VlanId innerVlan, EthType outerTpid) {
         HostId hid = HostId.hostId(mac, vlan);
-        HostDescription desc = new DefaultHostDescription(mac, vlan, locations, ips,
+        HostDescription desc = new DefaultHostDescription(mac, vlan, locations, auxLocations, ips,
                                                           innerVlan, outerTpid, true);
         providerService.hostDetected(hid, desc, true);
     }
@@ -199,9 +201,18 @@
                 Set<HostLocation> locations = locs.stream()
                         .map(hostLocation -> new HostLocation(hostLocation, System.currentTimeMillis()))
                         .collect(Collectors.toSet());
+
+                // auxLocations allows to be null
+                Set<HostLocation> auxLocations = hostConfig.auxLocations();
+                if (auxLocations != null) {
+                    auxLocations = auxLocations.stream()
+                            .map(auxLocation -> new HostLocation(auxLocation, System.currentTimeMillis()))
+                            .collect(Collectors.toSet());
+                }
+
                 VlanId innerVlan = hostConfig.innerVlan();
                 EthType outerTpid = hostConfig.outerTpid();
-                addHost(mac, vlan, locations, ipAddresses, innerVlan, outerTpid);
+                addHost(mac, vlan, locations, auxLocations, ipAddresses, innerVlan, outerTpid);
             } else {
                 log.warn("Host {} configuration {} is missing locations", hostId, hostConfig);
             }
@@ -224,6 +235,7 @@
             BasicHostConfig hostConfig = networkConfigRegistry.getConfig(hostId, BasicHostConfig.class);
             Set<IpAddress> ipAddresses = null;
             Set<HostLocation> locations = null;
+            Set<HostLocation> auxLocations = null;
             VlanId innerVlan = VlanId.NONE;
             EthType outerTpid = EthType.EtherType.UNKNOWN.ethType();
 
@@ -238,16 +250,25 @@
                 locations = locations.stream()
                         .map(hostLocation -> new HostLocation(hostLocation, System.currentTimeMillis()))
                         .collect(Collectors.toSet());
+
+                // auxLocations allows to be null
+                auxLocations = hostConfig.auxLocations();
+                if (auxLocations != null) {
+                    auxLocations = auxLocations.stream()
+                            .map(auxLocation -> new HostLocation(auxLocation, System.currentTimeMillis()))
+                            .collect(Collectors.toSet());
+                }
+
                 innerVlan = hostConfig.innerVlan();
                 outerTpid = hostConfig.outerTpid();
             }
 
             switch (event.type()) {
                 case CONFIG_ADDED:
-                    addHost(mac, vlan, locations, ipAddresses, innerVlan, outerTpid);
+                    addHost(mac, vlan, locations, auxLocations, ipAddresses, innerVlan, outerTpid);
                     break;
                 case CONFIG_UPDATED:
-                    updateHost(mac, vlan, locations, ipAddresses, innerVlan, outerTpid);
+                    updateHost(mac, vlan, locations, auxLocations, ipAddresses, innerVlan, outerTpid);
                     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 36de211..4bba524 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
@@ -52,6 +52,9 @@
     private DeviceId deviceId = DeviceId.deviceId("of:0000000000000001");
     private PortNumber port = PortNumber.portNumber(5);
     private Set<HostLocation> locations = Sets.newHashSet(new HostLocation(deviceId, port, 100));
+    private DeviceId auxDeviceId = DeviceId.deviceId("of:0000000000000002");
+    private PortNumber auxPort = PortNumber.portNumber(7);
+    private Set<HostLocation> auxLocations = Sets.newHashSet(new HostLocation(auxDeviceId, auxPort, 100));
     private Set<IpAddress> ips = new HashSet<>();
     private HostId hostId = HostId.hostId(mac, vlan);
     private HostDescription hostDescription;
@@ -65,13 +68,13 @@
         // Initialize test variables
         ips.add(IpAddress.valueOf("10.0.0.1"));
         ips.add(IpAddress.valueOf("192.168.0.1"));
-        hostDescription = new DefaultHostDescription(mac, vlan, locations, ips,
+        hostDescription = new DefaultHostDescription(mac, vlan, locations, auxLocations, ips,
                                                      innerVlan, outerTpid, true);
     }
 
     @Test
     public void testAddHost() throws Exception {
-        provider.addHost(mac, vlan, locations, ips, innerVlan, outerTpid);
+        provider.addHost(mac, vlan, locations, auxLocations, ips, innerVlan, outerTpid);
         assertThat(providerService.hostId, is(hostId));
         assertThat(providerService.hostDescription, is(hostDescription));
         assertThat(providerService.event, is("hostDetected"));
@@ -80,7 +83,7 @@
 
     @Test
     public void testUpdateHost() throws Exception {
-        provider.updateHost(mac, vlan, locations, ips, innerVlan, outerTpid);
+        provider.updateHost(mac, vlan, locations, auxLocations, ips, innerVlan, outerTpid);
         assertThat(providerService.hostId, is(hostId));
         assertThat(providerService.hostDescription, is(hostDescription));
         assertThat(providerService.event, is("hostDetected"));
diff --git a/web/api/src/test/java/org/onosproject/rest/resources/HostResourceTest.java b/web/api/src/test/java/org/onosproject/rest/resources/HostResourceTest.java
index 5ab2614..808fb42 100644
--- a/web/api/src/test/java/org/onosproject/rest/resources/HostResourceTest.java
+++ b/web/api/src/test/java/org/onosproject/rest/resources/HostResourceTest.java
@@ -161,6 +161,25 @@
                 }
             }
 
+            //  Check host auxLocations
+            if (jsonHost.get("auxLocations") != null) {
+                final JsonArray jsonAuxLocations = jsonHost.get("auxLocations").asArray();
+                final Set<HostLocation> expectedAuxLocations = host.auxLocations();
+                if (jsonAuxLocations.size() != expectedAuxLocations.size()) {
+                    reason = "auxLocations arrays differ in size";
+                    return false;
+                }
+
+                jsonIterator = jsonAuxLocations.iterator();
+                locIterator = expectedAuxLocations.iterator();
+                while (jsonIterator.hasNext()) {
+                    boolean result = verifyLocation(jsonIterator.next().asObject(), locIterator.next());
+                    if (!result) {
+                        return false;
+                    }
+                }
+            }
+
             //  Check Ip Addresses
             final JsonArray jsonHostIps = jsonHost.get("ipAddresses").asArray();
             final Set<IpAddress> expectedHostIps = host.ipAddresses();