Extend host structures to store multiple locations

Also update host location format in CLI and REST API

Change-Id: I0fbd655f642627dd3eb8a2925f83a3ee016fe4aa
diff --git a/web/api/src/main/java/org/onosproject/rest/resources/HostsWebResource.java b/web/api/src/main/java/org/onosproject/rest/resources/HostsWebResource.java
index 7d5dfad..4d85ba0 100644
--- a/web/api/src/main/java/org/onosproject/rest/resources/HostsWebResource.java
+++ b/web/api/src/main/java/org/onosproject/rest/resources/HostsWebResource.java
@@ -68,7 +68,7 @@
     @Context
     private UriInfo uriInfo;
     private static final String HOST_NOT_FOUND = "Host is not found";
-    private static final String[] REMOVAL_KEYS = {"mac", "vlan", "location", "ipAddresses"};
+    private static final String[] REMOVAL_KEYS = {"mac", "vlan", "locations", "ipAddresses"};
 
     /**
      * Get all end-station hosts.
@@ -219,15 +219,21 @@
         private HostId parseHost(JsonNode node) {
             MacAddress mac = MacAddress.valueOf(node.get("mac").asText());
             VlanId vlanId = VlanId.vlanId((short) node.get("vlan").asInt(VlanId.UNTAGGED));
-            JsonNode locationNode = node.get("location");
-            String deviceAndPort = locationNode.get("elementId").asText() + "/" +
-                    locationNode.get("port").asText();
-            HostLocation hostLocation = new HostLocation(ConnectPoint.deviceConnectPoint(deviceAndPort), 0);
 
-            Iterator<JsonNode> ipStrings = node.get("ipAddresses").elements();
+            Iterator<JsonNode> locationNodes = node.get("locations").elements();
+            Set<HostLocation> locations = new HashSet<>();
+            while (locationNodes.hasNext()) {
+                JsonNode locationNode = locationNodes.next();
+                String deviceAndPort = locationNode.get("elementId").asText() + "/" +
+                        locationNode.get("port").asText();
+                HostLocation hostLocation = new HostLocation(ConnectPoint.deviceConnectPoint(deviceAndPort), 0);
+                locations.add(hostLocation);
+            }
+
+            Iterator<JsonNode> ipNodes = node.get("ipAddresses").elements();
             Set<IpAddress> ips = new HashSet<>();
-            while (ipStrings.hasNext()) {
-                ips.add(IpAddress.valueOf(ipStrings.next().asText()));
+            while (ipNodes.hasNext()) {
+                ips.add(IpAddress.valueOf(ipNodes.next().asText()));
             }
 
             // try to remove elements from json node after reading them
@@ -235,7 +241,7 @@
             // Update host inventory
 
             HostId hostId = HostId.hostId(mac, vlanId);
-            DefaultHostDescription desc = new DefaultHostDescription(mac, vlanId, hostLocation, ips, true, annotations);
+            DefaultHostDescription desc = new DefaultHostDescription(mac, vlanId, locations, ips, true, annotations);
             hostProviderService.hostDetected(hostId, desc, false);
             return hostId;
         }
diff --git a/web/api/src/main/resources/definitions/Host.json b/web/api/src/main/resources/definitions/Host.json
index 208bf1f..b29e828 100644
--- a/web/api/src/main/resources/definitions/Host.json
+++ b/web/api/src/main/resources/definitions/Host.json
@@ -32,21 +32,24 @@
         "example": "127.0.0.1"
       }
     },
-    "location": {
-      "type": "object",
-      "title": "location",
-      "required": [
-        "elementId",
-        "port"
-      ],
-      "properties": {
-        "elementId": {
-          "type": "string",
-          "example": "of:0000000000000002"
-        },
-        "port": {
-          "type": "string",
-          "example": "3"
+    "locations": {
+      "type": "array",
+      "items": {
+        "type": "object",
+        "title": "location",
+        "required": [
+          "elementId",
+          "port"
+        ],
+        "properties": {
+          "elementId": {
+            "type": "string",
+            "example": "of:0000000000000002"
+          },
+          "port": {
+            "type": "string",
+            "example": "3"
+          }
         }
       }
     }
diff --git a/web/api/src/main/resources/definitions/HostPut.json b/web/api/src/main/resources/definitions/HostPut.json
index 66bf654..26e1c41 100644
--- a/web/api/src/main/resources/definitions/HostPut.json
+++ b/web/api/src/main/resources/definitions/HostPut.json
@@ -27,21 +27,24 @@
         "example": "127.0.0.1"
       }
     },
-    "location": {
-      "type": "object",
-      "title": "location",
-      "required": [
-        "elementId",
-        "port"
-      ],
-      "properties": {
-        "elementId": {
-          "type": "string",
-          "example": "of:0000000000000002"
-        },
-        "port": {
-          "type": "string",
-          "example": "3"
+    "locations": {
+      "type": "array",
+      "items": {
+        "type": "object",
+        "title": "location",
+        "required": [
+          "elementId",
+          "port"
+        ],
+        "properties": {
+          "elementId": {
+            "type": "string",
+            "example": "of:0000000000000002"
+          },
+          "port": {
+            "type": "string",
+            "example": "3"
+          }
         }
       }
     }
diff --git a/web/api/src/main/resources/definitions/Hosts.json b/web/api/src/main/resources/definitions/Hosts.json
index 235a083..3615ba4 100644
--- a/web/api/src/main/resources/definitions/Hosts.json
+++ b/web/api/src/main/resources/definitions/Hosts.json
@@ -45,21 +45,24 @@
               "example": "127.0.0.1"
             }
           },
-          "location": {
-            "type": "object",
-            "title": "location",
-            "required": [
-              "elementId",
-              "port"
-            ],
-            "properties": {
-              "elementId": {
-                "type": "string",
-                "example": "of:0000000000000002"
-              },
-              "port": {
-                "type": "string",
-                "example": "3"
+          "locations": {
+            "type": "array",
+            "items": {
+              "type": "object",
+              "title": "location",
+              "required": [
+                "elementId",
+                "port"
+              ],
+              "properties": {
+                "elementId": {
+                  "type": "string",
+                  "example": "of:0000000000000002"
+                },
+                "port": {
+                  "type": "string",
+                  "example": "3"
+                }
               }
             }
           }
diff --git a/web/api/src/main/resources/definitions/VirtualHost.json b/web/api/src/main/resources/definitions/VirtualHost.json
index e27d148..4035694 100644
--- a/web/api/src/main/resources/definitions/VirtualHost.json
+++ b/web/api/src/main/resources/definitions/VirtualHost.json
@@ -38,21 +38,24 @@
         "example": "127.0.0.1"
       }
     },
-    "location": {
-      "type": "object",
-      "title": "location",
-      "required": [
-        "elementId",
-        "port"
-      ],
-      "properties": {
-        "elementId": {
-          "type": "string",
-          "example": "of:0000000000000002"
-        },
-        "port": {
-          "type": "string",
-          "example": "3"
+    "locations": {
+      "type": "array",
+      "items": {
+        "type": "object",
+        "title": "location",
+        "required": [
+          "elementId",
+          "port"
+        ],
+        "properties": {
+          "elementId": {
+            "type": "string",
+            "example": "of:0000000000000002"
+          },
+          "port": {
+            "type": "string",
+            "example": "3"
+          }
         }
       }
     }
diff --git a/web/api/src/main/resources/definitions/VirtualHostPut.json b/web/api/src/main/resources/definitions/VirtualHostPut.json
index c0b8eba..3026478 100644
--- a/web/api/src/main/resources/definitions/VirtualHostPut.json
+++ b/web/api/src/main/resources/definitions/VirtualHostPut.json
@@ -33,21 +33,24 @@
         "example": "127.0.0.1"
       }
     },
-    "location": {
-      "type": "object",
-      "title": "location",
-      "required": [
-        "elementId",
-        "port"
-      ],
-      "properties": {
-        "elementId": {
-          "type": "string",
-          "example": "of:0000000000000002"
-        },
-        "port": {
-          "type": "string",
-          "example": "3"
+    "locations": {
+      "type": "array",
+      "items": {
+        "type": "object",
+        "title": "location",
+        "required": [
+          "elementId",
+          "port"
+        ],
+        "properties": {
+          "elementId": {
+            "type": "string",
+            "example": "of:0000000000000002"
+          },
+          "port": {
+            "type": "string",
+            "example": "3"
+          }
         }
       }
     }
diff --git a/web/api/src/main/resources/definitions/VirtualHosts.json b/web/api/src/main/resources/definitions/VirtualHosts.json
index 001867e..979a3f7 100644
--- a/web/api/src/main/resources/definitions/VirtualHosts.json
+++ b/web/api/src/main/resources/definitions/VirtualHosts.json
@@ -51,21 +51,24 @@
               "example": "127.0.0.1"
             }
           },
-          "location": {
-            "type": "object",
-            "title": "location",
-            "required": [
-              "elementId",
-              "port"
-            ],
-            "properties": {
-              "elementId": {
-                "type": "string",
-                "example": "of:0000000000000002"
-              },
-              "port": {
-                "type": "string",
-                "example": "3"
+          "locations": {
+            "type": "array",
+            "items": {
+              "type": "object",
+              "title": "location",
+              "required": [
+                "elementId",
+                "port"
+              ],
+              "properties": {
+                "elementId": {
+                  "type": "string",
+                  "example": "of:0000000000000002"
+                },
+                "port": {
+                  "type": "string",
+                  "example": "3"
+                }
               }
             }
           }
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 4c0ea91..94c390d 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
@@ -19,6 +19,7 @@
 import com.eclipsesource.json.Json;
 import com.eclipsesource.json.JsonArray;
 import com.eclipsesource.json.JsonObject;
+import com.eclipsesource.json.JsonValue;
 import com.google.common.collect.ImmutableSet;
 import org.hamcrest.Description;
 import org.hamcrest.Matchers;
@@ -52,6 +53,7 @@
 import java.io.InputStream;
 import java.net.HttpURLConnection;
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.Set;
 
 import static org.easymock.EasyMock.anyBoolean;
@@ -143,19 +145,21 @@
                 return false;
             }
 
-            // Check location element id
-            final JsonObject jsonLocation = jsonHost.get("location").asObject();
-            final String jsonLocationElementId = jsonLocation.get("elementId").asString();
-            if (!jsonLocationElementId.equals(host.location().elementId().toString())) {
-                reason = "location element id " + host.location().elementId().toString();
+            //  Check host locations
+            final JsonArray jsonLocations = jsonHost.get("locations").asArray();
+            final Set<HostLocation> expectedLocations = host.locations();
+            if (jsonLocations.size() != expectedLocations.size()) {
+                reason = "locations arrays differ in size";
                 return false;
             }
 
-            // Check location port number
-            final String jsonLocationPortNumber = jsonLocation.get("port").asString();
-            if (!jsonLocationPortNumber.equals(host.location().port().toString())) {
-                reason = "location portNumber " + host.location().port().toString();
-                return false;
+            Iterator<JsonValue> jsonIterator = jsonLocations.iterator();
+            Iterator<HostLocation> locIterator = expectedLocations.iterator();
+            while (jsonIterator.hasNext()) {
+                boolean result = verifyLocation(jsonIterator.next().asObject(), locIterator.next());
+                if (!result) {
+                    return false;
+                }
             }
 
             //  Check Ip Addresses
@@ -173,6 +177,20 @@
         public void describeTo(Description description) {
             description.appendText(reason);
         }
+
+        private boolean verifyLocation(JsonObject jsonLocation, HostLocation expectedLocation) {
+            final String jsonLocationElementId = jsonLocation.get("elementId").asString();
+            if (!jsonLocationElementId.equals(expectedLocation.elementId().toString())) {
+                reason = "location element id " + host.location().elementId().toString();
+                return false;
+            }
+            final String jsonLocationPortNumber = jsonLocation.get("port").asString();
+            if (!jsonLocationPortNumber.equals(expectedLocation.port().toString())) {
+                reason = "location portNumber " + expectedLocation.port().toString();
+                return false;
+            }
+            return true;
+        }
     }
 
     /**
diff --git a/web/api/src/test/resources/org/onosproject/rest/resources/post-host.json b/web/api/src/test/resources/org/onosproject/rest/resources/post-host.json
index 26c6292..b5fe3a1 100644
--- a/web/api/src/test/resources/org/onosproject/rest/resources/post-host.json
+++ b/web/api/src/test/resources/org/onosproject/rest/resources/post-host.json
@@ -2,10 +2,16 @@
   "id": "11:22:33:44:55:66/-1",
   "vlan": "-1",
   "mac": "11:22:33:44:55:66",
-  "location": {
-    "port": 3,
-    "elementId": "of:0000000000000001"
-  },
+  "locations": [
+    {
+      "port": 3,
+      "elementId": "of:0000000000000001"
+    },
+    {
+      "port": 4,
+      "elementId": "of:0000000000000002"
+    }
+  ],
   "ipAddresses": [
     "10.10.10.10"
   ]
diff --git a/web/api/src/test/resources/org/onosproject/rest/resources/post-virtual-host.json b/web/api/src/test/resources/org/onosproject/rest/resources/post-virtual-host.json
index 3621798..557dc32 100644
--- a/web/api/src/test/resources/org/onosproject/rest/resources/post-virtual-host.json
+++ b/web/api/src/test/resources/org/onosproject/rest/resources/post-virtual-host.json
@@ -3,10 +3,12 @@
   "id": "00:11:00:00:00:01/11",
   "mac": "00:11:00:00:00:01",
   "vlan": "11",
-  "location": {
-    "elementId": "devid1",
-    "port": "100" 
-  },
+  "locations": [
+    {
+      "elementId": "devid1",
+      "port": "100"
+    }
+  ],
   "ipAddresses": [
     "10.0.0.1",
     "10.0.0.2"