Supports downlink trace functionality in OpenstackNetworkingUI

Change-Id: Ide25681e83d34b22393483b3b06e8834693a18ec
(cherry picked from commit 5aef9826f6232d2605bbe11a97b9487c56f575a4)
diff --git a/apps/openstacknetworking/api/BUCK b/apps/openstacknetworking/api/BUCK
index cbc9b22..61b333d 100644
--- a/apps/openstacknetworking/api/BUCK
+++ b/apps/openstacknetworking/api/BUCK
@@ -3,6 +3,7 @@
 COMPILE_DEPS = [
     '//lib:CORE_DEPS',
     '//lib:openstack4j-core',
+    "//apps/openstacknode/api:onos-apps-openstacknode-api",
 ]
 
 osgi_jar_with_tests (
diff --git a/apps/openstacknetworking/api/BUILD b/apps/openstacknetworking/api/BUILD
index 5348b60..9ead932 100644
--- a/apps/openstacknetworking/api/BUILD
+++ b/apps/openstacknetworking/api/BUILD
@@ -7,6 +7,7 @@
 
 COMPILE_DEPS = CORE_DEPS + [
     "@openstack4j_core//jar",
+    "//apps/openstacknode/api:onos-apps-openstacknode-api",
 ]
 
 osgi_jar_with_tests(
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/util/OpenstackNetworkingUtil.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/util/OpenstackNetworkingUtil.java
index d06065c..65a9e9f 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/util/OpenstackNetworkingUtil.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/util/OpenstackNetworkingUtil.java
@@ -700,7 +700,7 @@
      * @param dstIp dst ip address
      * @param srcInstancePort src instance port
      * @param osNetService openstack networking service
-     * @param uplink true if this request if uplink
+     * @param uplink true if this request is for uplink
      * @return flow trace request string
      */
     public static String traceRequestString(String srcIp,
diff --git a/apps/openstacknetworkingui/BUCK b/apps/openstacknetworkingui/BUCK
index 12f1264..2f2fe68 100644
--- a/apps/openstacknetworkingui/BUCK
+++ b/apps/openstacknetworkingui/BUCK
@@ -11,6 +11,7 @@
   '//apps/openstacknode/api:onos-apps-openstacknode-api',
   '//apps/openstacktelemetry/api:onos-apps-openstacktelemetry-api',
   '//apps/openstacknetworking/api:onos-apps-openstacknetworking-api',
+  '//apps/openstacknetworking/app:onos-apps-openstacknetworking-app',
 ]
 
 EXCLUDED_BUNDLES = [
diff --git a/apps/openstacknetworkingui/BUILD b/apps/openstacknetworkingui/BUILD
index 5058dd1..ed4f724 100644
--- a/apps/openstacknetworkingui/BUILD
+++ b/apps/openstacknetworkingui/BUILD
@@ -8,6 +8,7 @@
     "//apps/openstacknode/api:onos-apps-openstacknode-api",
     "//apps/openstacktelemetry/api:onos-apps-openstacktelemetry-api",
     "//apps/openstacknetworking/api:onos-apps-openstacknetworking-api",
+    "//apps/openstacknetworking/app:onos-apps-openstacknetworking-app",
     "@sshd_core//jar",
 ]
 
diff --git a/apps/openstacknetworkingui/src/main/java/org/onosproject/openstacknetworkingui/OpenstackNetworkingUiMessageHandler.java b/apps/openstacknetworkingui/src/main/java/org/onosproject/openstacknetworkingui/OpenstackNetworkingUiMessageHandler.java
index c49944d..32964af 100644
--- a/apps/openstacknetworkingui/src/main/java/org/onosproject/openstacknetworkingui/OpenstackNetworkingUiMessageHandler.java
+++ b/apps/openstacknetworkingui/src/main/java/org/onosproject/openstacknetworkingui/OpenstackNetworkingUiMessageHandler.java
@@ -104,17 +104,20 @@
     private static final String FLOW_TRACE_RESULT = "flowTraceResult";
     private static final String SRC_DEVICE_ID = "srcDeviceId";
     private static final String DST_DEVICE_ID = "dstDeviceId";
+    private static final String UPLINK = "uplink";
     private static final String OVS_VERSION_2_8 = "2.8";
+    private static final String OVS_VERSION_2_7 = "2.7";
     private static final String OVS_VERSION_2_6 = "2.6";
-    private static final String FLAT = "FLAT";
+
     private static final String VXLAN = "VXLAN";
     private static final String VLAN = "VLAN";
     private static final String DL_DST = "dl_dst=";
     private static final String NW_DST = "nw_dst=";
-    private static final String DEFAULT_REQUEST_STRING = "sudo ovs-appctl ofproto/trace br-int ip,in_port=";
+    private static final String DEFAULT_REQUEST_STRING = "sudo ovs-appctl ofproto/trace br-int ip";
+    private static final String IN_PORT = "in_port=";
     private static final String NW_SRC = "nw_src=";
     private static final String COMMA = ",";
-
+    private static final String TUN_ID = "tun_id=";
 
     private static final long TIMEOUT_MS = 5000;
     private static final long WAIT_OUTPUT_STREAM_SECOND = 2;
@@ -230,12 +233,15 @@
             String dstIp = string(payload, DST_IP);
             String srcDeviceId = string(payload, SRC_DEVICE_ID);
             String dstDeviceId = string(payload, DST_DEVICE_ID);
-            log.info("Flow trace request called with src IP: {}, dst IP: {}, src device ID: {}, dst device Id: {}",
+            boolean uplink = bool(payload, UPLINK);
+            log.info("Flow trace request called with" +
+                            "src IP: {}, dst IP: {}, src device ID: {}, dst device Id: {}, uplink: ",
                     srcIp,
                     dstIp,
                     srcDeviceId,
-                    dstDeviceId);
-            eventExecutor.execute(() -> processFlowTraceRequest(srcIp, dstIp, srcDeviceId));
+                    dstDeviceId,
+                    uplink);
+            eventExecutor.execute(() -> processFlowTraceRequest(srcIp, dstIp, srcDeviceId, dstDeviceId, uplink));
         }
     }
 
@@ -443,7 +449,8 @@
         sendMessagetoUi(FLOW_STATS_ADD_RESULT, statsResult);
     }
 
-    private void processFlowTraceRequest(String srcIp, String dstIp, String srcDeviceId) {
+    private void processFlowTraceRequest(String srcIp, String dstIp, String srcDeviceId, String dstDeviceId,
+                                         boolean uplink) {
         boolean traceSuccess = true;
 
         ObjectMapper mapper = new ObjectMapper();
@@ -454,6 +461,7 @@
 
         OpenstackNode srcOpenstackNode = osNodeService.node(DeviceId.deviceId(srcDeviceId));
         if (srcOpenstackNode == null) {
+            log.error("There's no openstack node information for device {}", srcDeviceId);
             return;
         }
 
@@ -463,52 +471,138 @@
             return;
         }
 
-        String traceResultForward = sendTraceRequestToNode(srcIp, dstIp, srcOpenstackNode);
-        if (traceResultForward == null) {
+        String traceResultString = sendTraceRequestToNode(srcIp, dstIp, srcOpenstackNode, uplink);
+
+        if (traceResultString == null) {
             return;
         }
 
-        log.debug("traceResultForward raw data: {}", traceResultForward);
+        log.debug("traceResultString raw data: {}", traceResultString);
 
-        ObjectNode traceResultForwardJson = null;
+        ObjectNode traceResultJson = null;
 
         Device srcDevice = deviceService.getDevice(srcOpenstackNode.intgBridge());
-        if (srcDevice.swVersion().startsWith(OVS_VERSION_2_8)) {
-            traceResultForwardJson = Ovs28FlowTraceResultParser.flowTraceResultInJson(
-                    traceResultForward.trim(), srcOpenstackNode.hostname());
+        if (srcDevice.swVersion().startsWith(OVS_VERSION_2_8) ||
+                srcDevice.swVersion().startsWith(OVS_VERSION_2_7)) {
+            traceResultJson = Ovs28FlowTraceResultParser.flowTraceResultInJson(
+                    traceResultString.trim(), srcOpenstackNode.hostname());
         } else {
             log.error("Currently OVS version {} is not supported",
                     deviceService.getDevice(srcOpenstackNode.intgBridge()));
         }
 
-        if (traceResultForwardJson == null) {
+        if (traceResultJson == null) {
             return;
         }
 
-        traceResultArray.add(traceResultForwardJson);
+        traceResultArray.add(traceResultJson);
 
-        log.debug("traceResultForward Json: {}", traceResultForwardJson);
+        log.debug("traceResultForward Json: {}", traceResultJson);
 
-        if (!traceResultForwardJson.get(IS_SUCCESS).asBoolean()) {
+        if (!traceResultJson.get(IS_SUCCESS).asBoolean()) {
             traceSuccess = false;
         }
 
         traceResult.put(TRACE_SUCCESS, traceSuccess);
+
+        traceResult.put(SRC_IP, srcIp);
+        traceResult.put(DST_IP, dstIp);
+        traceResult.put(SRC_DEVICE_ID, srcDeviceId);
+        traceResult.put(DST_DEVICE_ID, dstDeviceId);
+        traceResult.put(UPLINK, uplink);
+
         log.debug("traceResult Json: {}", traceResult);
 
         sendMessagetoUi(FLOW_TRACE_RESULT, traceResult);
 
     }
 
-    private String sendTraceRequestToNode(String srcIp, String dstIp, OpenstackNode openstackNode) {
+    private String sendTraceRequestToNode(String srcIp, String dstIp, OpenstackNode openstackNode, boolean uplink) {
+
+        Optional<InstancePort> instancePort = instancePortService.instancePorts().stream()
+                .filter(port -> port.ipAddress().getIp4Address().toString().equals(srcIp)
+                        && port.deviceId().equals(openstackNode.intgBridge()))
+                .findAny();
+
+        if (!instancePort.isPresent()) {
+            return null;
+        }
+
+        String requestString = traceRequestString(srcIp, dstIp,
+                instancePort.get(), osNetService, uplink);
+
+        return sendTraceRequestToNode(requestString, openstackNode);
+    }
+
+    private String traceRequestString(String srcIp,
+                                      String dstIp,
+                                      InstancePort srcInstancePort,
+                                      OpenstackNetworkService osNetService,
+                                      boolean uplink) {
+
+        StringBuilder requestStringBuilder = new StringBuilder(DEFAULT_REQUEST_STRING);
+
+        if (uplink) {
+
+            requestStringBuilder.append(COMMA)
+                    .append(IN_PORT)
+                    .append(srcInstancePort.portNumber().toString())
+                    .append(COMMA)
+                    .append(NW_SRC)
+                    .append(srcIp)
+                    .append(COMMA);
+
+            if (osNetService.networkType(srcInstancePort.networkId()).equals(VXLAN) ||
+                    osNetService.networkType(srcInstancePort.networkId()).equals(VLAN)) {
+                if (srcIp.equals(dstIp)) {
+                    dstIp = osNetService.gatewayIp(srcInstancePort.portId());
+                    requestStringBuilder.append(DL_DST)
+                            .append(DEFAULT_GATEWAY_MAC_STR).append(COMMA);
+                } else if (!osNetService.ipPrefix(srcInstancePort.portId()).contains(IpAddress.valueOf(dstIp))) {
+                    requestStringBuilder.append(DL_DST)
+                            .append(DEFAULT_GATEWAY_MAC_STR)
+                            .append(COMMA);
+                }
+            } else {
+                if (srcIp.equals(dstIp)) {
+                    dstIp = osNetService.gatewayIp(srcInstancePort.portId());
+                }
+            }
+
+            requestStringBuilder.append(NW_DST)
+                    .append(dstIp)
+                    .append("\n");
+        } else {
+            requestStringBuilder.append(COMMA)
+                    .append(NW_SRC)
+                    .append(dstIp)
+                    .append(COMMA);
+
+            if (osNetService.networkType(srcInstancePort.networkId()).equals(VXLAN) ||
+                    osNetService.networkType(srcInstancePort.networkId()).equals(VLAN)) {
+                requestStringBuilder.append(TUN_ID)
+                        .append(osNetService.segmentId(srcInstancePort.networkId()))
+                        .append(COMMA);
+            }
+            requestStringBuilder.append(NW_DST)
+                    .append(srcIp)
+                    .append("\n");
+
+        }
+
+        return requestStringBuilder.toString();
+    }
+
+    private String sendTraceRequestToNode(String requestString,
+                                                OpenstackNode node) {
         String traceResult = null;
-        OpenstackSshAuth sshAuth = openstackNode.sshAuthInfo();
+        OpenstackSshAuth sshAuth = node.sshAuthInfo();
 
         try (SshClient client = SshClient.setUpDefaultClient()) {
             client.start();
 
             try (ClientSession session = client
-                    .connect(sshAuth.id(), openstackNode.managementIp().getIp4Address().toString(), SSH_PORT)
+                    .connect(sshAuth.id(), node.managementIp().getIp4Address().toString(), SSH_PORT)
                     .verify(TIMEOUT_MS, TimeUnit.SECONDS).getSession()) {
                 session.addPasswordIdentity(sshAuth.password());
                 session.auth().verify(TIMEOUT_MS, TimeUnit.SECONDS);
@@ -516,11 +610,6 @@
 
                 try (ClientChannel channel = session.createChannel(ClientChannel.CHANNEL_SHELL)) {
 
-                    String requestString = traceRequestString(srcIp, dstIp, openstackNode);
-                    if (requestString == null) {
-                        return null;
-                    }
-
                     log.debug("requestString: {}", requestString);
                     final InputStream inputStream =
                             new ByteArrayInputStream(requestString.getBytes());
@@ -566,40 +655,4 @@
 
         return traceResult;
     }
-
-    private String traceRequestString(String srcIp, String dstIp, OpenstackNode openstackNode) {
-
-        Optional<InstancePort> instancePort = instancePortService.instancePorts().stream()
-                .filter(port -> port.ipAddress().getIp4Address().toString().equals(srcIp)
-                        && port.deviceId().equals(openstackNode.intgBridge()))
-                .findAny();
-
-        if (!instancePort.isPresent()) {
-            return null;
-        }
-
-        String requestString = DEFAULT_REQUEST_STRING
-                + instancePort.get().portNumber().toString()
-                + COMMA
-                + NW_SRC
-                + srcIp
-                + COMMA;
-
-        if (osNetService.networkType(instancePort.get().networkId()).equals(VXLAN)) {
-            if (srcIp.equals(dstIp)) {
-                dstIp = osNetService.gatewayIp(instancePort.get().portId());
-                requestString = requestString + DL_DST + DEFAULT_GATEWAY_MAC_STR + COMMA;
-            } else if (!osNetService.ipPrefix(instancePort.get().portId()).contains(IpAddress.valueOf(dstIp))) {
-                requestString = requestString + DL_DST + DEFAULT_GATEWAY_MAC_STR + COMMA;
-            }
-        } else if (osNetService.networkType(instancePort.get().networkId()).equals(FLAT)) {
-            if (srcIp.equals(dstIp)) {
-                dstIp = osNetService.gatewayIp(instancePort.get().portId());
-            }
-        }
-
-        requestString = requestString + NW_DST + dstIp + "\n";
-
-        return requestString;
-    }
 }
diff --git a/apps/openstacknetworkingui/src/main/resources/app/view/sonaTopov/sonaTopovOverlay.js b/apps/openstacknetworkingui/src/main/resources/app/view/sonaTopov/sonaTopovOverlay.js
index e588cb2..b2d7999 100644
--- a/apps/openstacknetworkingui/src/main/resources/app/view/sonaTopov/sonaTopovOverlay.js
+++ b/apps/openstacknetworkingui/src/main/resources/app/view/sonaTopov/sonaTopovOverlay.js
@@ -28,7 +28,7 @@
     var flowStatsDstIp = null;
 
     var traceInfoDialogId = 'traceInfoDialogId',
-        traceInfoDialogOpt = {
+        traceInfoDialogOpt =   {
             width: 350,
             edge: 'left',
             margin: 20,
@@ -278,7 +278,7 @@
     }
 
     function flowTraceResultBtn() {
-        sts.sendFlowTraceRequest(traceSrc, traceDst, srcDeviceId, dstDeviceId);
+        sts.sendFlowTraceRequest(traceSrc, traceDst, srcDeviceId, dstDeviceId, true);
         ds.closeDialog();
         traceSrc = null;
         traceDst = null;
diff --git a/apps/openstacknetworkingui/src/main/resources/app/view/sonaTopov/sonaTopovService.js b/apps/openstacknetworkingui/src/main/resources/app/view/sonaTopov/sonaTopovService.js
index 0212ea2..21b495b 100644
--- a/apps/openstacknetworkingui/src/main/resources/app/view/sonaTopov/sonaTopovService.js
+++ b/apps/openstacknetworkingui/src/main/resources/app/view/sonaTopov/sonaTopovService.js
@@ -28,6 +28,12 @@
     // injected refs
     var $log, fs, flash, wss, ds;
 
+    var traceSrc = null;
+    var traceDst = null;
+    var srcDeviceId = null;
+    var dstDeviceId = null;
+    var uplink = null;
+
     // constants
     var displayStart = 'openstackNetworkingUiStart',
         displayUpdate = 'openstackNetworkingUiUpdate',
@@ -76,12 +82,13 @@
         flash.flash('sendFlowStatsRemoveRequest called');
     }
 
-    function sendFlowTraceRequest(src, dst, srcDeviceId, dstDeviceId) {
+    function sendFlowTraceRequest(src, dst, srcDeviceId, dstDeviceId, uplink) {
         wss.sendEvent(flowTraceRequest, {
             srcIp: src,
             dstIp: dst,
             srcDeviceId: srcDeviceId,
             dstDeviceId: dstDeviceId,
+            uplink: uplink,
         });
         flash.flash('sendFlowTraceRequest called');
     }
@@ -175,11 +182,32 @@
                 hideMargin: -20
             }
         var traceSuccess = data.traceSuccess == true ? "SUCCESS" : "FALSE";
-        ds.openDialog(flowTraceResultDialogId, flowTraceResultDialogOpt)
-                    .setTitle('Flow Trace Result: ' + traceSuccess)
-                    .addContent(createTraceResultInfoDiv(data))
-                    .addOk(dOk, 'Close')
-                    .bindKeys();
+        traceSrc = data.srcIp;
+        traceDst = data.dstIp;
+        srcDeviceId = data.srcDeviceId;
+        dstDeviceId = data.dstDeviceId;
+        uplink = data.uplink;
+
+        if (data.uplink == true) {
+            ds.openDialog(flowTraceResultDialogId, flowTraceResultDialogOpt)
+                .setTitle('Flow Trace Result: ' + traceSuccess)
+                .addContent(createTraceResultInfoDiv(data))
+                .addOk(downlinkTraceRequestBtn, 'Downlink Trace')
+                .bindKeys();
+        } else {
+            ds.openDialog(flowTraceResultDialogId, flowTraceResultDialogOpt)
+                .setTitle('Flow Trace Result: ' + traceSuccess)
+                .addContent(createTraceResultInfoDiv(data))
+                .addOk(dOk, 'Close')
+                .bindKeys();
+        }
+
+    }
+
+    function downlinkTraceRequestBtn() {
+        sendFlowTraceRequest(traceSrc, traceDst, srcDeviceId, dstDeviceId, false);
+        ds.closeDialog();
+        flash.flash('Send Downlink Flow Trace Request')
     }
 
     function createTraceResultInfoDiv(data) {