Added changes for ONOS-5652

Change-Id: Iefb148afd6585527eb8c23b21d9ec22b911d913b
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/ClusterViewMessageHandler.java b/web/gui/src/main/java/org/onosproject/ui/impl/ClusterViewMessageHandler.java
index a84d38a..dbc7a83 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/ClusterViewMessageHandler.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/ClusterViewMessageHandler.java
@@ -16,12 +16,15 @@
 
 package org.onosproject.ui.impl;
 
+import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.fasterxml.jackson.databind.node.ObjectNode;
 import com.google.common.collect.ImmutableSet;
 import org.joda.time.DateTime;
 import org.onosproject.cluster.ClusterService;
 import org.onosproject.cluster.ControllerNode;
 import org.onosproject.cluster.NodeId;
+import org.onosproject.net.Device;
+import org.onosproject.net.device.DeviceService;
 import org.onosproject.ui.RequestHandler;
 import org.onosproject.ui.UiMessageHandler;
 import org.onosproject.ui.table.TableModel;
@@ -29,6 +32,11 @@
 import org.onosproject.ui.table.cell.TimeFormatter;
 
 import java.util.Collection;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static com.google.common.collect.ImmutableList.copyOf;
+import static org.onosproject.net.MastershipRole.MASTER;
 
 
 /**
@@ -44,6 +52,7 @@
     private static final String CLUSTER_DETAILS_RESP = "clusterDetailsResponse";
     private static final String DETAILS = "details";
 
+    private static final String DEVICES = "devices";
     private static final String ID = "id";
     private static final String IP = "ip";
     private static final String TCP_PORT = "tcp";
@@ -55,6 +64,16 @@
             ID, IP, TCP_PORT, STATE_IID, STARTED_IID, UPDATED
     };
 
+    private static final String URI = "id";
+    private static final String TYPE = "type";
+    private static final String CHASSIS_ID = "chassisid";
+    private static final String HW = "hw";
+    private static final String SW = "sw";
+    private static final String MFR = "mfr";
+    private static final String PROTOCOL = "protocol";
+    private static final String SERIAL = "serial";
+
+
     private static final String ICON_ID_ONLINE = "active";
     private static final String ICON_ID_OFFLINE = "inactive";
 
@@ -121,6 +140,33 @@
             super(CLUSTER_DETAILS_REQ);
         }
 
+        private List<Device> populateDevices() {
+            DeviceService ds = get(DeviceService.class);
+            return copyOf(ds.getDevices()).stream()
+                    .filter(d -> ds.getRole(d.id()) == MASTER)
+                    .collect(Collectors.toList());
+        }
+
+        private String deviceProtocol(Device device) {
+            String protocol = device.annotations().value(PROTOCOL);
+            return protocol != null ? protocol : "";
+        }
+
+        private ObjectNode deviceData(Device d) {
+            ObjectNode device = objectNode();
+
+            device.put(URI, d.id().toString());
+            device.put(TYPE, d.type().toString());
+            device.put(CHASSIS_ID, d.chassisId().toString());
+            device.put(MFR, d.manufacturer());
+            device.put(HW, d.hwVersion());
+            device.put(SW, d.swVersion());
+            device.put(PROTOCOL, deviceProtocol(d));
+            device.put(SERIAL, d.serialNumber());
+
+            return device;
+        }
+
         @Override
         public void process(long sid, ObjectNode payload) {
 
@@ -129,9 +175,18 @@
             ControllerNode node = cs.getNode(new NodeId(id));
 
             ObjectNode data = objectNode();
+            ArrayNode devices = arrayNode();
+            List<Device> deviceList = populateDevices();
+
             data.put(ID, node.id().toString());
             data.put(IP, node.ip().toString());
 
+            for (Device d : deviceList) {
+                devices.add(deviceData(d));
+            }
+
+            data.set(DEVICES, devices);
+
             //TODO put more detail info to data
 
             ObjectNode rootNode = objectNode();
diff --git a/web/gui/src/main/webapp/app/view/cluster/cluster.js b/web/gui/src/main/webapp/app/view/cluster/cluster.js
index b4dd391..887b943 100644
--- a/web/gui/src/main/webapp/app/view/cluster/cluster.js
+++ b/web/gui/src/main/webapp/app/view/cluster/cluster.js
@@ -53,6 +53,15 @@
         ],
         friendlyProps = [
             'Node ID', 'IP Address'
+        ],
+        deviceCols = [
+            'id', 'type', 'chassisid', 'mfr',
+            'hw', 'sw', 'protocol', 'serial'
+        ],
+        friendlyDeviceCols = [
+            'URI', 'Type', 'Chassis ID', 'Vendor',
+            'H/W Version', 'S/W Version', 'Protocol',
+            'Serial #'
         ];
 
     function closePanel() {
@@ -88,6 +97,9 @@
             .append('table');
         top.append('hr');
 
+        bottom = container.append('div').classed('bottom', true);
+        bottom.append('h2').classed('devices-title', true).html('Devices');
+        bottom.append('table');
         //ToDo add more details
     }
 
@@ -112,6 +124,45 @@
         });
     }
 
+    function addDeviceRow(tbody, device) {
+        var tr = tbody.append('tr');
+
+        deviceCols.forEach(function (col) {
+            tr.append('td').html(device[col]);
+        });
+    }
+
+    function populateBottom(devices) {
+        var table = bottom.select('table'),
+            theader = table.append('thead').append('tr'),
+            tbody = table.append('tbody'),
+            tbWidth, tbHeight;
+
+        friendlyDeviceCols.forEach(function (col) {
+            theader.append('th').html(col);
+        });
+        devices.forEach(function (device) {
+            addDeviceRow(tbody, device);
+        });
+
+        tbWidth = fs.noPxStyle(tbody, 'width') + scrollSize;
+        tbHeight = pHeight
+                    - (fs.noPxStyle(detailsPanel.el()
+                                        .select('.top'), 'height')
+                    + fs.noPxStyle(detailsPanel.el()
+                                        .select('.devices-title'), 'height')
+                    + portsTblPdg);
+
+        table.style({
+            height: tbHeight + 'px',
+            width: tbWidth + 'px',
+            overflow: 'auto',
+            display: 'block'
+        });
+
+        detailsPanel.width(tbWidth + ctnrPdg);
+    }
+
     function createDetailsPane() {
         detailsPanel = ps.createPanel(pName, {
             width: wSize.width,
@@ -128,11 +179,13 @@
 
     function populateDetails(details) {
         setUpPanel();
+
         populateTop(details);
+        populateBottom(details.devices);
 
         //ToDo add more details
         detailsPanel.height(pHeight);
-        detailsPanel.width(wtPdg);
+        //detailsPanel.width(wtPdg); ToDO Use this when needed!
     }
 
     function respDetailsCb(data) {