[SDFAB-753] Improve ONOS cluster event

Main idea of this change is to add an additional parameter
in the event that carries information about the failed instance.

Additionally, prevents several NPE by using hostname as id
when controller hostname cannot be resolved into an ip.

Change-Id: Id9886afe3f1e5ecee0f1414b2722c340680a813e
(cherry picked from commit fa5dc3c137a4deaa020a669388470b511c2b6a8e)
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 980d7f6..7243818 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
@@ -19,6 +19,7 @@
 import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.fasterxml.jackson.databind.node.ObjectNode;
 import com.google.common.collect.ImmutableSet;
+import org.onlab.packet.IpAddress;
 import org.onosproject.cluster.ClusterService;
 import org.onosproject.cluster.ControllerNode;
 import org.onosproject.cluster.NodeId;
@@ -170,27 +171,31 @@
 
         @Override
         public void process(ObjectNode payload) {
+            ObjectNode rootNode = objectNode();
+            ObjectNode data = objectNode();
+            ArrayNode devices = arrayNode();
 
             String id = string(payload, ID);
             ClusterService cs = get(ClusterService.class);
             ControllerNode node = cs.getNode(new NodeId(id));
+            if (node != null) {
+                IpAddress nodeIp = node.ip();
+                List<Device> deviceList = populateDevices(node);
 
-            ObjectNode data = objectNode();
-            ArrayNode devices = arrayNode();
-            List<Device> deviceList = populateDevices(node);
+                data.put(ID, node.id().toString());
+                data.put(IP, nodeIp != null ? nodeIp.toString() : node.host());
 
-            data.put(ID, node.id().toString());
-            data.put(IP, node.ip().toString());
+                for (Device d : deviceList) {
+                    devices.add(deviceData(d));
+                }
 
-            for (Device d : deviceList) {
-                devices.add(deviceData(d));
+            } else {
+                data.put(ID, "NONE");
+                data.put(IP, "NONE");
             }
-
             data.set(DEVICES, devices);
 
             //TODO put more detail info to data
-
-            ObjectNode rootNode = objectNode();
             rootNode.set(DETAILS, data);
             sendMessage(CLUSTER_DETAILS_RESP, rootNode);
         }
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/TopologyViewMessageHandlerBase.java b/web/gui/src/main/java/org/onosproject/ui/impl/TopologyViewMessageHandlerBase.java
index 3bd4985..79a335f 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/TopologyViewMessageHandlerBase.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/TopologyViewMessageHandlerBase.java
@@ -261,10 +261,11 @@
     // Produces a cluster instance message to the client.
     protected ObjectNode instanceMessage(ClusterEvent event, String msgType) {
         ControllerNode node = event.subject();
+        IpAddress nodeIp = node.ip();
         int switchCount = services.mastership().getDevicesOf(node.id()).size();
         ObjectNode payload = objectNode()
                 .put("id", node.id().toString())
-                .put("ip", node.ip().toString())
+                .put("ip", nodeIp != null ? nodeIp.toString() : node.host())
                 .put("online", services.cluster().getState(node.id()).isActive())
                 .put("ready", services.cluster().getState(node.id()).isReady())
                 .put("uiAttached", node.equals(services.cluster().getLocalNode()))
@@ -272,7 +273,7 @@
 
         ArrayNode labels = arrayNode();
         labels.add(node.id().toString());
-        labels.add(node.ip().toString());
+        labels.add(nodeIp != null ? nodeIp.toString() : node.host());
 
         // Add labels, props and stuff the payload into envelope.
         payload.set("labels", labels);
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/UiWebSocket.java b/web/gui/src/main/java/org/onosproject/ui/impl/UiWebSocket.java
index 2e7e643..0ed48ad 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/UiWebSocket.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/UiWebSocket.java
@@ -22,6 +22,7 @@
 import org.eclipse.jetty.websocket.api.WebSocketAdapter;
 import org.onlab.osgi.ServiceDirectory;
 import org.onlab.osgi.ServiceNotFoundException;
+import org.onlab.packet.IpAddress;
 import org.onosproject.cluster.ClusterService;
 import org.onosproject.cluster.ControllerNode;
 import org.onosproject.ui.GlyphConstants;
@@ -438,9 +439,10 @@
         ArrayNode instances = arrayNode();
 
         for (ControllerNode node : service.getNodes()) {
+            IpAddress nodeIp = node.ip();
             ObjectNode instance = objectNode()
                     .put(ID, node.id().toString())
-                    .put(IP, node.ip().toString())
+                    .put(IP, nodeIp != null ? nodeIp.toString() : node.host())
                     .put(GlyphConstants.UI_ATTACHED,
                          node.equals(service.getLocalNode()));
             instances.add(instance);
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/topo/Topo2Jsonifier.java b/web/gui/src/main/java/org/onosproject/ui/impl/topo/Topo2Jsonifier.java
index 0ef6faf..8ae6e6d 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/topo/Topo2Jsonifier.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/topo/Topo2Jsonifier.java
@@ -201,13 +201,7 @@
 
     private ObjectNode json(UiClusterMember member, boolean isUiAttached) {
         int switchCount = mastershipService.getDevicesOf(member.id()).size();
-        ControllerNode.State state = clusterService.getState(member.id());
-        return objectNode()
-                .put("id", member.id().toString())
-                .put("ip", member.ip().toString())
-                .put("online", state.isActive())
-                .put("ready", state.isReady())
-                .put("uiAttached", isUiAttached)
+        return jsonCommon(member).put("uiAttached", isUiAttached)
                 .put("switches", switchCount);
     }
 
@@ -637,14 +631,26 @@
     }
 
     private ObjectNode json(UiClusterMember member) {
+        return jsonCommon(member).put(GlyphConstants.UI_ATTACHED,
+                clusterService.getLocalNode().equals(member.backingNode()));
+    }
+
+    private ObjectNode jsonCommon(UiClusterMember member) {
         ControllerNode.State state = clusterService.getState(member.id());
+        ControllerNode node = member.backingNode();
+        if (node != null) {
+            IpAddress nodeIp = member.backingNode().ip();
+            return objectNode()
+                    .put("id", member.idAsString())
+                    .put("ip", nodeIp != null ? nodeIp.toString() : node.host())
+                    .put("online", state.isActive())
+                    .put("ready", state.isReady());
+        }
         return objectNode()
                 .put("id", member.idAsString())
-                .put("ip", member.ip().toString())
-                .put("online", state.isActive())
-                .put("ready", state.isReady())
-                .put(GlyphConstants.UI_ATTACHED,
-                     member.backingNode().equals(clusterService.getLocalNode()));
+                .put("ip", "NONE")
+                .put("online", false)
+                .put("ready", false);
     }
 
     private ObjectNode jsonClosedRegion(String ridStr, UiRegion region) {