Add endpoint attribute to openstack controller node

Change-Id: I8f8a13964f6025e2e9ce0f2f0ff493b72c7c3b28
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackSyncStateCommand.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackSyncStateCommand.java
index 29823ae..9f3f4a7 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackSyncStateCommand.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackSyncStateCommand.java
@@ -243,13 +243,7 @@
         StringBuilder endpointSb = new StringBuilder();
         endpointSb.append(auth.protocol().name().toLowerCase());
         endpointSb.append("://");
-
-        if (node.managementIp() == null) {
-            endpointSb.append(node.hostname());
-        } else {
-            endpointSb.append(node.managementIp());
-        }
-
+        endpointSb.append(node.endPoint());
         endpointSb.append(":");
         endpointSb.append(auth.port());
         endpointSb.append("/");
diff --git a/apps/openstacknode/api/src/main/java/org/onosproject/openstacknode/api/OpenstackNode.java b/apps/openstacknode/api/src/main/java/org/onosproject/openstacknode/api/OpenstackNode.java
index 25b795c..14acbea 100644
--- a/apps/openstacknode/api/src/main/java/org/onosproject/openstacknode/api/OpenstackNode.java
+++ b/apps/openstacknode/api/src/main/java/org/onosproject/openstacknode/api/OpenstackNode.java
@@ -193,6 +193,13 @@
     OpenstackAuth authentication();
 
     /**
+     * Returns the endpoint URL info.
+     *
+     * @return keystone authentication info
+     */
+    String endPoint();
+
+    /**
      * Builder of new node entities.
      */
     interface Builder {
@@ -283,6 +290,14 @@
          * @return openstack node builder
          */
         Builder authentication(OpenstackAuth auth);
+
+        /**
+         * Returns openstack node builder with supplied endpoint info.
+         *
+         * @param endPoint endpoint info
+         * @return openstack node builder
+         */
+        Builder endPoint(String endPoint);
     }
 }
 
diff --git a/apps/openstacknode/app/src/main/java/org/onosproject/openstacknode/codec/OpenstackNodeCodec.java b/apps/openstacknode/app/src/main/java/org/onosproject/openstacknode/codec/OpenstackNodeCodec.java
index cf7a150..31f32bf 100644
--- a/apps/openstacknode/app/src/main/java/org/onosproject/openstacknode/codec/OpenstackNodeCodec.java
+++ b/apps/openstacknode/app/src/main/java/org/onosproject/openstacknode/codec/OpenstackNodeCodec.java
@@ -56,6 +56,7 @@
     private static final String STATE = "state";
     private static final String PHYSICAL_INTERFACES = "phyIntfs";
     private static final String AUTHENTICATION = "authentication";
+    private static final String END_POINT = "endPoint";
 
     private static final String MISSING_MESSAGE = " is required in OpenstackNode";
 
@@ -66,7 +67,8 @@
         ObjectNode result = context.mapper().createObjectNode()
                 .put(HOST_NAME, node.hostname())
                 .put(TYPE, node.type().name())
-                .put(STATE, node.state().name());
+                .put(STATE, node.state().name())
+                .put(MANAGEMENT_IP, node.managementIp().toString());
 
         OpenstackNode.NodeType type = node.type();
 
@@ -75,12 +77,9 @@
         }
 
         if (type != OpenstackNode.NodeType.CONTROLLER) {
-            result.put(MANAGEMENT_IP, node.managementIp().toString());
             result.put(INTEGRATION_BRIDGE, node.intgBridge().toString());
         } else {
-            if (node.managementIp() != null) {
-                result.put(MANAGEMENT_IP, node.managementIp().toString());
-            }
+            result.put(END_POINT, node.endPoint());
         }
 
         if (node.vlanIntf() != null) {
@@ -120,11 +119,14 @@
                 HOST_NAME + MISSING_MESSAGE);
         String type = nullIsIllegal(json.get(TYPE).asText(),
                 TYPE + MISSING_MESSAGE);
+        String mIp = nullIsIllegal(json.get(MANAGEMENT_IP).asText(),
+                MANAGEMENT_IP + MISSING_MESSAGE);
 
         DefaultOpenstackNode.Builder nodeBuilder = DefaultOpenstackNode.builder()
                 .hostname(hostname)
                 .type(OpenstackNode.NodeType.valueOf(type))
-                .state(NodeState.INIT);
+                .state(NodeState.INIT)
+                .managementIp(IpAddress.valueOf(mIp));
 
         if (type.equals(GATEWAY)) {
             nodeBuilder.uplinkPort(nullIsIllegal(json.get(UPLINK_PORT).asText(),
@@ -134,14 +136,10 @@
             String iBridge = nullIsIllegal(json.get(INTEGRATION_BRIDGE).asText(),
                     INTEGRATION_BRIDGE + MISSING_MESSAGE);
             nodeBuilder.intgBridge(DeviceId.deviceId(iBridge));
-
-            String mIp = nullIsIllegal(json.get(MANAGEMENT_IP).asText(),
-                    MANAGEMENT_IP + MISSING_MESSAGE);
-            nodeBuilder.managementIp(IpAddress.valueOf(mIp));
         } else {
-            if (json.get(MANAGEMENT_IP) != null) {
-                nodeBuilder.managementIp(IpAddress.valueOf(json.get(MANAGEMENT_IP).asText()));
-            }
+            String endPoint = nullIsIllegal(json.get(END_POINT).asText(),
+                    END_POINT + MISSING_MESSAGE);
+            nodeBuilder.endPoint(endPoint);
         }
         if (json.get(VLAN_INTF_NAME) != null) {
             nodeBuilder.vlanIntf(json.get(VLAN_INTF_NAME).asText());
diff --git a/apps/openstacknode/app/src/main/java/org/onosproject/openstacknode/impl/DefaultOpenstackNode.java b/apps/openstacknode/app/src/main/java/org/onosproject/openstacknode/impl/DefaultOpenstackNode.java
index aad1fbb..73c8e1b 100644
--- a/apps/openstacknode/app/src/main/java/org/onosproject/openstacknode/impl/DefaultOpenstackNode.java
+++ b/apps/openstacknode/app/src/main/java/org/onosproject/openstacknode/impl/DefaultOpenstackNode.java
@@ -58,6 +58,7 @@
     private final NodeState state;
     private final Collection<OpenstackPhyInterface> phyIntfs;
     private final OpenstackAuth auth;
+    private final String endPoint;
 
     private static final String NOT_NULL_MSG = "Node % cannot be null";
 
@@ -76,6 +77,7 @@
      * @param state         node state
      * @param phyIntfs      physical interfaces
      * @param auth          keystone authentication info
+     * @param endPoint      openstack endpoint URL
      */
     protected DefaultOpenstackNode(String hostname, NodeType type,
                                    DeviceId intgBridge,
@@ -85,7 +87,8 @@
                                    String uplinkPort,
                                    NodeState state,
                                    Collection<OpenstackPhyInterface> phyIntfs,
-                                   OpenstackAuth auth) {
+                                   OpenstackAuth auth,
+                                   String endPoint) {
         this.hostname = hostname;
         this.type = type;
         this.intgBridge = intgBridge;
@@ -96,6 +99,7 @@
         this.state = state;
         this.phyIntfs = phyIntfs;
         this.auth = auth;
+        this.endPoint = endPoint;
     }
 
     @Override
@@ -235,7 +239,8 @@
                     Objects.equals(uplinkPort, that.uplinkPort) &&
                     Objects.equals(vlanIntf, that.vlanIntf) &&
                     Objects.equals(phyIntfs, that.phyIntfs) &&
-                    Objects.equals(auth, that.auth);
+                    Objects.equals(auth, that.auth) &&
+                    Objects.equals(endPoint, that.endPoint);
         }
         return false;
     }
@@ -250,7 +255,8 @@
                 vlanIntf,
                 uplinkPort,
                 phyIntfs,
-                auth);
+                auth,
+                endPoint);
     }
 
     @Override
@@ -266,6 +272,7 @@
                 .add("state", state)
                 .add("phyIntfs", phyIntfs)
                 .add("auth", auth)
+                .add("endpoint", endPoint)
                 .toString();
     }
 
@@ -282,6 +289,7 @@
                 .state(newState)
                 .phyIntfs(phyIntfs)
                 .authentication(auth)
+                .endPoint(endPoint)
                 .build();
     }
 
@@ -319,6 +327,11 @@
         return auth;
     }
 
+    @Override
+    public String endPoint() {
+        return endPoint;
+    }
+
     /**
      * Returns new builder instance.
      *
@@ -345,7 +358,8 @@
                 .uplinkPort(osNode.uplinkPort())
                 .state(osNode.state())
                 .phyIntfs(osNode.phyIntfs())
-                .authentication(osNode.authentication());
+                .authentication(osNode.authentication())
+                .endPoint(osNode.endPoint());
     }
 
     /**
@@ -363,6 +377,7 @@
         private NodeState state;
         private Collection<OpenstackPhyInterface> phyIntfs;
         private OpenstackAuth auth;
+        private String endPoint;
 
         // private constructor not intended to use from external
         private Builder() {
@@ -373,16 +388,17 @@
             checkArgument(hostname != null, NOT_NULL_MSG, "hostname");
             checkArgument(type != null, NOT_NULL_MSG, "type");
             checkArgument(state != null, NOT_NULL_MSG, "state");
+            checkArgument(managementIp != null, NOT_NULL_MSG, "management IP");
 
             if (type != NodeType.CONTROLLER) {
-                checkArgument(managementIp != null, NOT_NULL_MSG, "management IP");
-
                 checkArgument(intgBridge != null, NOT_NULL_MSG, "integration bridge");
 
                 if (dataIp == null && Strings.isNullOrEmpty(vlanIntf)) {
                     throw new IllegalArgumentException("Either data IP or VLAN interface is required");
                 }
             } else {
+                checkArgument(endPoint != null, NOT_NULL_MSG, "endpoint URL");
+
                 // we force controller node to have COMPLETE state for now
                 state = NodeState.COMPLETE;
             }
@@ -400,7 +416,8 @@
                     uplinkPort,
                     state,
                     phyIntfs,
-                    auth);
+                    auth,
+                    endPoint);
         }
 
         @Override
@@ -464,6 +481,12 @@
             this.auth = auth;
             return this;
         }
+
+        @Override
+        public Builder endPoint(String endPoint) {
+            this.endPoint = endPoint;
+            return this;
+        }
     }
 }
 
diff --git a/apps/openstacknode/app/src/test/java/org/onosproject/openstacknode/codec/OpenstackNodeCodecTest.java b/apps/openstacknode/app/src/test/java/org/onosproject/openstacknode/codec/OpenstackNodeCodecTest.java
index 5019234..da4680c 100644
--- a/apps/openstacknode/app/src/test/java/org/onosproject/openstacknode/codec/OpenstackNodeCodecTest.java
+++ b/apps/openstacknode/app/src/test/java/org/onosproject/openstacknode/codec/OpenstackNodeCodecTest.java
@@ -155,6 +155,7 @@
                 .type(OpenstackNode.NodeType.CONTROLLER)
                 .state(NodeState.INIT)
                 .managementIp(IpAddress.valueOf("172.16.130.10"))
+                .endPoint("keystone-end-point-url")
                 .authentication(auth)
                 .build();
 
@@ -172,6 +173,7 @@
         assertThat(node.hostname(), is("controller"));
         assertThat(node.type().name(), is("CONTROLLER"));
         assertThat(node.managementIp().toString(), is("172.16.130.10"));
+        assertThat(node.endPoint(), is("keystone-end-point-url"));
 
         OpenstackAuth auth = node.authentication();
 
diff --git a/apps/openstacknode/app/src/test/java/org/onosproject/openstacknode/codec/OpenstackNodeJsonMatcher.java b/apps/openstacknode/app/src/test/java/org/onosproject/openstacknode/codec/OpenstackNodeJsonMatcher.java
index ac1d471..603edac 100644
--- a/apps/openstacknode/app/src/test/java/org/onosproject/openstacknode/codec/OpenstackNodeJsonMatcher.java
+++ b/apps/openstacknode/app/src/test/java/org/onosproject/openstacknode/codec/OpenstackNodeJsonMatcher.java
@@ -37,6 +37,7 @@
     private static final String STATE = "state";
     private static final String PHYSICAL_INTERFACES = "phyIntfs";
     private static final String AUTHENTICATION = "authentication";
+    private static final String END_POINT = "endPoint";
 
     private OpenstackNodeJsonMatcher(OpenstackNode node) {
         this.node = node;
@@ -117,6 +118,16 @@
             }
         }
 
+        // check endpoint URL
+        JsonNode jsonEndPoint = jsonNode.get(END_POINT);
+        if (jsonEndPoint != null) {
+            String endPoint = node.endPoint();
+            if (!jsonEndPoint.asText().equals(endPoint)) {
+                description.appendText("endpoint URL was " + jsonEndPoint);
+                return false;
+            }
+        }
+
         // check physical interfaces
         JsonNode jsonPhyIntfs = jsonNode.get(PHYSICAL_INTERFACES);
         if (jsonPhyIntfs != null) {
diff --git a/apps/openstacknode/app/src/test/java/org/onosproject/openstacknode/impl/DefaultOpenstackNodeHandlerTest.java b/apps/openstacknode/app/src/test/java/org/onosproject/openstacknode/impl/DefaultOpenstackNodeHandlerTest.java
index ff59237..b07044f 100644
--- a/apps/openstacknode/app/src/test/java/org/onosproject/openstacknode/impl/DefaultOpenstackNodeHandlerTest.java
+++ b/apps/openstacknode/app/src/test/java/org/onosproject/openstacknode/impl/DefaultOpenstackNodeHandlerTest.java
@@ -408,7 +408,7 @@
                 intgBridge.id(),
                 ipAddr,
                 ipAddr,
-                null, null, state, phyIntfs, null);
+                null, null, state, phyIntfs, null, null);
     }
 
     private static OpenstackNode createGatewayNode(String hostname,
@@ -423,7 +423,7 @@
                 intgBridge.id(),
                 ipAddr,
                 ipAddr,
-                null, uplinkPort, state, null, null);
+                null, uplinkPort, state, null, null, null);
     }
 
     private static final class TestDevice extends DefaultDevice {
@@ -482,7 +482,8 @@
                                   String uplinkPort,
                                   NodeState state,
                                   Set<OpenstackPhyInterface> phyIntfs,
-                                  OpenstackAuth auth) {
+                                  OpenstackAuth auth,
+                                  String endPoint) {
             super(hostname,
                     type,
                     intgBridge,
@@ -492,7 +493,8 @@
                     uplinkPort,
                     state,
                     phyIntfs,
-                    auth);
+                    auth,
+                    endPoint);
         }
 
         @Override
diff --git a/apps/openstacknode/app/src/test/resources/org/onosproject/openstacknode/codec/OpenstackControllerNode.json b/apps/openstacknode/app/src/test/resources/org/onosproject/openstacknode/codec/OpenstackControllerNode.json
index 8745d27..705a977 100644
--- a/apps/openstacknode/app/src/test/resources/org/onosproject/openstacknode/codec/OpenstackControllerNode.json
+++ b/apps/openstacknode/app/src/test/resources/org/onosproject/openstacknode/codec/OpenstackControllerNode.json
@@ -2,6 +2,7 @@
   "hostname": "controller",
   "type": "CONTROLLER",
   "managementIp": "172.16.130.10",
+  "endPoint": "keystone-end-point-url",
   "authentication": {
     "version": "v2.0",
     "port": 35357,