CORD-471 Added physical port and data plane IP as node attribtes

Change-Id: I4c28053151e61feb4b9b0ca60e98f7e0e4af0207
diff --git a/src/main/java/org/onosproject/cordvtn/CordVtn.java b/src/main/java/org/onosproject/cordvtn/CordVtn.java
index 1a56b42..107f3e7 100644
--- a/src/main/java/org/onosproject/cordvtn/CordVtn.java
+++ b/src/main/java/org/onosproject/cordvtn/CordVtn.java
@@ -105,9 +105,13 @@
             .register(KryoNamespaces.API)
             .register(CordVtnNode.class)
             .register(NodeState.class);
+
     private static final String DEFAULT_BRIDGE = "br-int";
     private static final String VPORT_PREFIX = "tap";
     private static final String DEFAULT_TUNNEL = "vxlan";
+    private static final String OK = "OK";
+    private static final String NO = "NO";
+
     private static final Map<String, String> DEFAULT_TUNNEL_OPTIONS = new HashMap<String, String>() {
         {
             put("key", "flow");
@@ -213,6 +217,8 @@
             }
         };
 
+        // TODO Add physical port add state
+
         public abstract void process(CordVtn cordVtn, CordVtnNode node);
     }
 
@@ -307,6 +313,27 @@
     }
 
     @Override
+    public String checkNodeInitState(CordVtnNode node) {
+        checkNotNull(node);
+
+        NodeState state = getNodeState(node);
+        if (state == null) {
+            log.warn("Failed to get init state of {}", node.hostname());
+            return null;
+        }
+
+        String result = String.format(
+                "Integration bridge created/connected : %s (%s)%n" +
+                        "VXLAN interface created : %s%n" +
+                        "Physical interface added : %s (%s)",
+                checkIntegrationBridge(node) ? OK : NO, DEFAULT_BRIDGE,
+                checkTunnelInterface(node) ? OK : NO,
+                checkPhyInterface(node) ? OK : NO, node.phyPortName());
+
+        return result;
+    }
+
+    @Override
     public void createServiceDependency(CordServiceId tServiceId, CordServiceId pServiceId) {
         CordService tService = getCordService(tServiceId);
         CordService pService = getCordService(pServiceId);
@@ -374,7 +401,12 @@
         checkNotNull(node);
 
         if (checkIntegrationBridge(node) && checkTunnelInterface(node)) {
-            return NodeState.COMPLETE;
+            // TODO add physical port add state
+            if (checkPhyInterface(node)) {
+                return NodeState.COMPLETE;
+            } else {
+                return NodeState.INCOMPLETE;
+            }
         } else if (checkIntegrationBridge(node)) {
             return NodeState.BRIDGE_CREATED;
         } else if (getOvsdbConnectionState(node)) {
@@ -488,6 +520,16 @@
     }
 
     /**
+     * Returns port name.
+     *
+     * @param port port
+     * @return port name
+     */
+    private String getPortName(Port port) {
+        return port.annotations().value("portName");
+    }
+
+    /**
      * Returns OVSDB client for a given node.
      *
      * @param node cordvtn node
@@ -520,6 +562,7 @@
                     ControllerInfo ctrlInfo = new ControllerInfo(controller.ip(), OFPORT, "tcp");
                     controllers.add(ctrlInfo);
                 });
+
         String dpid = node.intBrId().toString().substring(DPID_BEGIN);
 
         try {
@@ -545,9 +588,11 @@
         for (String key : DEFAULT_TUNNEL_OPTIONS.keySet()) {
             optionBuilder.set(key, DEFAULT_TUNNEL_OPTIONS.get(key));
         }
-        TunnelDescription description =
-                new DefaultTunnelDescription(null, null, VXLAN, TunnelName.tunnelName(DEFAULT_TUNNEL),
-                                             optionBuilder.build());
+
+        TunnelDescription description = new DefaultTunnelDescription(
+                null, null, VXLAN, TunnelName.tunnelName(DEFAULT_TUNNEL),
+                optionBuilder.build());
+
         try {
             DriverHandler handler = driverService.createHandler(node.ovsdbId());
             TunnelConfig tunnelConfig =  handler.behaviour(TunnelConfig.class);
@@ -578,7 +623,26 @@
         try {
             deviceService.getPorts(node.intBrId())
                     .stream()
-                    .filter(p -> p.annotations().value("portName").contains(DEFAULT_TUNNEL)
+                    .filter(p -> getPortName(p).contains(DEFAULT_TUNNEL)
+                            && p.isEnabled())
+                    .findAny().get();
+            return true;
+        } catch (NoSuchElementException e) {
+            return false;
+        }
+    }
+
+    /**
+     * Checks if physical interface exists.
+     *
+     * @param node cordvtn node
+     * @return true if the interface exists, false otherwise
+     */
+    private boolean checkPhyInterface(CordVtnNode node) {
+        try {
+            deviceService.getPorts(node.intBrId())
+                    .stream()
+                    .filter(p -> getPortName(p).contains(node.phyPortName())
                             && p.isEnabled())
                     .findAny().get();
             return true;
@@ -596,7 +660,7 @@
     private PortNumber getTunnelPort(DeviceId bridgeId) {
         try {
             return deviceService.getPorts(bridgeId).stream()
-                    .filter(p -> p.annotations().value("portName").contains(DEFAULT_TUNNEL)
+                    .filter(p -> getPortName(p).contains(DEFAULT_TUNNEL)
                             && p.isEnabled())
                     .findFirst().get().number();
         } catch (NoSuchElementException e) {
@@ -613,8 +677,7 @@
     private Ip4Address getRemoteIp(DeviceId bridgeId) {
         CordVtnNode node = getNodeByBridgeId(bridgeId);
         if (node != null) {
-            // TODO get data plane IP for tunneling
-            return node.ovsdbIp().getIp4Address();
+            return node.localIp().getIp4Address();
         } else {
             return null;
         }
@@ -712,7 +775,7 @@
     private boolean isVm(Host host) {
         Port port = deviceService.getPort(host.location().deviceId(),
                                           host.location().port());
-        return port.annotations().value("portName").contains(VPORT_PREFIX);
+        return getPortName(port).contains(VPORT_PREFIX);
     }
 
     /**
@@ -866,41 +929,48 @@
 
         /**
          * Handles port added situation.
-         * If the added port is tunnel port, proceed remaining node initialization.
-         * Otherwise, do nothing.
+         * If the added port is tunnel or physical port, proceed remaining node
+         * initialization. Otherwise, do nothing.
          *
          * @param port port
          */
         public void portAdded(Port port) {
-            // TODO add host by updating network config
-            if (!port.annotations().value("portName").contains(DEFAULT_TUNNEL)) {
+            CordVtnNode node = getNodeByBridgeId((DeviceId) port.element().id());
+            if (node == null) {
                 return;
             }
 
-            CordVtnNode node = getNodeByBridgeId((DeviceId) port.element().id());
-            if (node != null) {
-                setNodeState(node, checkNodeState(node));
+            // TODO add host by updating network config
+            String portName = getPortName(port);
+            if (!portName.contains(DEFAULT_TUNNEL) && !portName.equals(node.phyPortName())) {
+                return;
             }
+
+            log.info("Port {} is added to {}", portName, node.hostname());
+            setNodeState(node, checkNodeState(node));
         }
 
         /**
          * Handles port removed situation.
-         * If the removed port is tunnel port, proceed remaining node initialization.
-         * Others, do nothing.
+         * If the removed port is tunnel or physical port, proceed remaining node
+         * initialization.Others, do nothing.
          *
          * @param port port
          */
         public void portRemoved(Port port) {
-            // TODO remove host by updating network config
-            if (!port.annotations().value("portName").contains(DEFAULT_TUNNEL)) {
+            CordVtnNode node = getNodeByBridgeId((DeviceId) port.element().id());
+            if (node == null) {
                 return;
             }
 
-            CordVtnNode node = getNodeByBridgeId((DeviceId) port.element().id());
-            if (node != null) {
-                log.info("Tunnel interface is removed from {}", node.hostname());
-                setNodeState(node, NodeState.INCOMPLETE);
+            // TODO remove host by updating network config
+            String portName = getPortName(port);
+            if (!portName.contains(DEFAULT_TUNNEL) && !portName.equals(node.phyPortName())) {
+                return;
             }
+
+            log.info("Port {} is removed from {}", portName, node.hostname());
+            setNodeState(node, NodeState.INCOMPLETE);
         }
     }
 
diff --git a/src/main/java/org/onosproject/cordvtn/CordVtnConfig.java b/src/main/java/org/onosproject/cordvtn/CordVtnConfig.java
index 827ce05..57842dc 100644
--- a/src/main/java/org/onosproject/cordvtn/CordVtnConfig.java
+++ b/src/main/java/org/onosproject/cordvtn/CordVtnConfig.java
@@ -37,6 +37,8 @@
     public static final String OVSDB_IP = "ovsdbIp";
     public static final String OVSDB_PORT = "ovsdbPort";
     public static final String BRIDGE_ID = "bridgeId";
+    public static final String PHYSICAL_PORT_NAME = "phyPortName";
+    public static final String LOCAL_IP = "localIp";
 
     /**
      * Returns the set of nodes read from network config.
@@ -54,7 +56,9 @@
             jsonNode.path(HOSTNAME).asText(),
             IpAddress.valueOf(jsonNode.path(OVSDB_IP).asText()),
             TpPort.tpPort(jsonNode.path(OVSDB_PORT).asInt()),
-            DeviceId.deviceId(jsonNode.path(BRIDGE_ID).asText()))));
+            DeviceId.deviceId(jsonNode.path(BRIDGE_ID).asText()),
+            jsonNode.path(PHYSICAL_PORT_NAME).asText(),
+            IpAddress.valueOf(jsonNode.path(LOCAL_IP).asText()))));
 
         return nodes;
     }
@@ -68,12 +72,17 @@
         private final IpAddress ovsdbIp;
         private final TpPort ovsdbPort;
         private final DeviceId bridgeId;
+        private final String phyPortName;
+        private final IpAddress localIp;
 
-        public CordVtnNodeConfig(String hostname, IpAddress ovsdbIp, TpPort ovsdbPort, DeviceId bridgeId) {
+        public CordVtnNodeConfig(String hostname, IpAddress ovsdbIp, TpPort ovsdbPort,
+                                 DeviceId bridgeId, String phyPortName, IpAddress localIp) {
             this.hostname = checkNotNull(hostname);
             this.ovsdbIp = checkNotNull(ovsdbIp);
             this.ovsdbPort = checkNotNull(ovsdbPort);
             this.bridgeId = checkNotNull(bridgeId);
+            this.phyPortName = checkNotNull(phyPortName);
+            this.localIp = checkNotNull(localIp);
         }
 
         /**
@@ -111,5 +120,23 @@
         public DeviceId bridgeId() {
             return this.bridgeId;
         }
+
+        /**
+         * Returns physical port name.
+         *
+         * @return physical port name
+         */
+        public String phyPortName() {
+            return this.phyPortName;
+        }
+
+        /**
+         * Returns local IP address.
+         *
+         * @return ip address
+         */
+        public IpAddress localIp() {
+            return this.localIp;
+        }
     }
 }
diff --git a/src/main/java/org/onosproject/cordvtn/CordVtnConfigManager.java b/src/main/java/org/onosproject/cordvtn/CordVtnConfigManager.java
index 525135d..4ff133b 100644
--- a/src/main/java/org/onosproject/cordvtn/CordVtnConfigManager.java
+++ b/src/main/java/org/onosproject/cordvtn/CordVtnConfigManager.java
@@ -97,7 +97,13 @@
 
         config.cordVtnNodes().forEach(node -> {
             CordVtnNode cordVtnNode = new CordVtnNode(
-                    node.hostname(), node.ovsdbIp(), node.ovsdbPort(), node.bridgeId());
+                    node.hostname(),
+                    node.ovsdbIp(),
+                    node.ovsdbPort(),
+                    node.bridgeId(),
+                    node.phyPortName(),
+                    node.localIp());
+
             cordVtnService.addNode(cordVtnNode);
         });
     }
diff --git a/src/main/java/org/onosproject/cordvtn/CordVtnNode.java b/src/main/java/org/onosproject/cordvtn/CordVtnNode.java
index 439d16e..c11ba42 100644
--- a/src/main/java/org/onosproject/cordvtn/CordVtnNode.java
+++ b/src/main/java/org/onosproject/cordvtn/CordVtnNode.java
@@ -34,6 +34,8 @@
     private final IpAddress ovsdbIp;
     private final TpPort ovsdbPort;
     private final DeviceId bridgeId;
+    private final String phyPortName;
+    private final IpAddress localIp;
 
     public static final Comparator<CordVtnNode> CORDVTN_NODE_COMPARATOR =
             (node1, node2) -> node1.hostname().compareTo(node2.hostname());
@@ -45,12 +47,17 @@
      * @param ovsdbIp OVSDB server IP address
      * @param ovsdbPort OVSDB server port number
      * @param bridgeId integration bridge identifier
+     * @param phyPortName physical port name
+     * @param localIp local ip address of data plane
      */
-    public CordVtnNode(String hostname, IpAddress ovsdbIp, TpPort ovsdbPort, DeviceId bridgeId) {
+    public CordVtnNode(String hostname, IpAddress ovsdbIp, TpPort ovsdbPort,
+                       DeviceId bridgeId, String phyPortName, IpAddress localIp) {
         this.hostname = checkNotNull(hostname);
         this.ovsdbIp = checkNotNull(ovsdbIp);
         this.ovsdbPort = checkNotNull(ovsdbPort);
         this.bridgeId = checkNotNull(bridgeId);
+        this.phyPortName = checkNotNull(phyPortName);
+        this.localIp = checkNotNull(localIp);
     }
 
     /**
@@ -98,6 +105,24 @@
         return DeviceId.deviceId("ovsdb:" + this.ovsdbIp.toString());
     }
 
+    /**
+     * Returns physical port name.
+     *
+     * @return physical port name
+     */
+    public String phyPortName() {
+        return this.phyPortName;
+    }
+
+    /**
+     * Returns local IP address.
+     *
+     * @return ip address
+     */
+    public IpAddress localIp() {
+        return this.localIp;
+    }
+
     @Override
     public boolean equals(Object obj) {
         if (this == obj) {
@@ -106,10 +131,7 @@
 
         if (obj instanceof CordVtnNode) {
             CordVtnNode that = (CordVtnNode) obj;
-            if (Objects.equals(hostname, that.hostname) &&
-                    Objects.equals(ovsdbIp, that.ovsdbIp) &&
-                    Objects.equals(ovsdbPort, that.ovsdbPort) &&
-                    Objects.equals(bridgeId, that.bridgeId)) {
+            if (Objects.equals(hostname, that.hostname)) {
                 return true;
             }
         }
@@ -128,6 +150,8 @@
                 .add("ip", ovsdbIp)
                 .add("port", ovsdbPort)
                 .add("bridgeId", bridgeId)
+                .add("phyPortName", phyPortName)
+                .add("localIp", localIp)
                 .toString();
     }
 }
diff --git a/src/main/java/org/onosproject/cordvtn/CordVtnService.java b/src/main/java/org/onosproject/cordvtn/CordVtnService.java
index 2c3c23b..fee857a 100644
--- a/src/main/java/org/onosproject/cordvtn/CordVtnService.java
+++ b/src/main/java/org/onosproject/cordvtn/CordVtnService.java
@@ -60,6 +60,19 @@
     boolean getNodeInitState(CordVtnNode node);
 
     /**
+     * Returns detailed node initialization state.
+     * Return string includes the following information.
+     *
+     * Integration bridge created/connected: OK or NO
+     * VXLAN interface created: OK or NO
+     * Physical interface added: OK or NO
+     *
+     * @param node cordvtn node
+     * @return string including detailed node init state
+     */
+    String checkNodeInitState(CordVtnNode node);
+
+    /**
      * Returns all nodes known to the service.
      *
      * @return list of nodes
diff --git a/src/main/java/org/onosproject/cordvtn/cli/CordVtnNodeAddCommand.java b/src/main/java/org/onosproject/cordvtn/cli/CordVtnNodeAddCommand.java
index 1b7d986..5448ef1 100644
--- a/src/main/java/org/onosproject/cordvtn/cli/CordVtnNodeAddCommand.java
+++ b/src/main/java/org/onosproject/cordvtn/cli/CordVtnNodeAddCommand.java
@@ -48,6 +48,16 @@
             required = true, multiValued = false)
     private String bridgeId = null;
 
+    @Argument(index = 3, name = "phyPortName",
+            description = "Physical port name",
+            required = true, multiValued = false)
+    private String phyPortName = null;
+
+    @Argument(index = 4, name = "localIp",
+            description = "Local data plane IP address",
+            required = true, multiValued = false)
+    private String localIp = null;
+
     @Override
     protected void execute() {
         checkArgument(ovsdb.contains(":"), "OVSDB address should be ip:port format");
@@ -58,7 +68,9 @@
         CordVtnNode node = new CordVtnNode(hostname,
                                            IpAddress.valueOf(ipPort[0]),
                                            TpPort.tpPort(Integer.parseInt(ipPort[1])),
-                                           DeviceId.deviceId(bridgeId));
+                                           DeviceId.deviceId(bridgeId),
+                                           phyPortName,
+                                           IpAddress.valueOf(localIp));
         service.addNode(node);
     }
 }
diff --git a/src/main/java/org/onosproject/cordvtn/cli/CordVtnNodeCheckCommand.java b/src/main/java/org/onosproject/cordvtn/cli/CordVtnNodeCheckCommand.java
new file mode 100644
index 0000000..185acd7
--- /dev/null
+++ b/src/main/java/org/onosproject/cordvtn/cli/CordVtnNodeCheckCommand.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.cordvtn.cli;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.cordvtn.CordVtnNode;
+import org.onosproject.cordvtn.CordVtnService;
+
+/**
+ * Checks detailed node init state.
+ */
+@Command(scope = "onos", name = "cordvtn-node-check",
+        description = "Shows detailed node init state")
+public class CordVtnNodeCheckCommand extends AbstractShellCommand {
+
+    @Argument(index = 0, name = "hostname", description = "Hostname",
+            required = true, multiValued = false)
+    private String hostname = null;
+
+    @Override
+    protected void execute() {
+        CordVtnService service = AbstractShellCommand.get(CordVtnService.class);
+        CordVtnNode node = service.getNodes()
+                .stream()
+                .filter(n -> n.hostname().equals(hostname))
+                .findFirst()
+                .orElse(null);
+
+        if (node == null) {
+            print("Cannot find %s from registered nodes", hostname);
+            return;
+        }
+
+        print(service.checkNodeInitState(node));
+    }
+}
diff --git a/src/main/java/org/onosproject/cordvtn/cli/CordVtnNodeListCommand.java b/src/main/java/org/onosproject/cordvtn/cli/CordVtnNodeListCommand.java
index 83e5859..51641c4 100644
--- a/src/main/java/org/onosproject/cordvtn/cli/CordVtnNodeListCommand.java
+++ b/src/main/java/org/onosproject/cordvtn/cli/CordVtnNodeListCommand.java
@@ -44,10 +44,12 @@
             print("%s", json(service, nodes));
         } else {
             for (CordVtnNode node : nodes) {
-                print("hostname=%s, ovsdb=%s, br-int=%s, init=%s",
+                print("hostname=%s, ovsdb=%s, br-int=%s, phyPort=%s, localIp=%s, init=%s",
                       node.hostname(),
                       node.ovsdbIp().toString() + ":" + node.ovsdbPort().toString(),
                       node.intBrId().toString(),
+                      node.phyPortName(),
+                      node.localIp().toString(),
                       getState(service, node));
             }
             print("Total %s nodes", service.getNodeCount());
@@ -63,6 +65,8 @@
                                .put("hostname", node.hostname())
                                .put("ovsdb", ipPort)
                                .put("brInt", node.intBrId().toString())
+                               .put("phyPort", node.phyPortName())
+                               .put("localIp", node.localIp().toString())
                                .put("init", getState(service, node)));
         }
         return result;
diff --git a/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/src/main/resources/OSGI-INF/blueprint/shell-config.xml
index ad28bc7..b5c6009 100644
--- a/src/main/resources/OSGI-INF/blueprint/shell-config.xml
+++ b/src/main/resources/OSGI-INF/blueprint/shell-config.xml
@@ -28,5 +28,8 @@
         <command>
             <action class="org.onosproject.cordvtn.cli.CordVtnNodeInitCommand"/>
         </command>
+        <command>
+            <action class="org.onosproject.cordvtn.cli.CordVtnNodeCheckCommand"/>
+        </command>
     </command-bundle>
 </blueprint>