Adding code to enable GUI failover.

Change-Id: I8423f17349411d24332db8670840438d0d8ec8ba
diff --git a/core/api/src/main/java/org/onosproject/ui/UiConnection.java b/core/api/src/main/java/org/onosproject/ui/UiConnection.java
index 719b788..123acb9 100644
--- a/core/api/src/main/java/org/onosproject/ui/UiConnection.java
+++ b/core/api/src/main/java/org/onosproject/ui/UiConnection.java
@@ -29,4 +29,13 @@
      */
     void sendMessage(ObjectNode message);
 
+    /**
+     * Composes a message into JSON and sends it to the user interface client.
+     *
+     * @param type    message type
+     * @param sid     message sequence number
+     * @param payload message payload
+     */
+    void sendMessage(String type, long sid, ObjectNode payload);
+
 }
\ No newline at end of file
diff --git a/tools/test/cells/beastMod b/tools/test/cells/beastMod
new file mode 100644
index 0000000..b0c8a5a
--- /dev/null
+++ b/tools/test/cells/beastMod
@@ -0,0 +1,18 @@
+# Bare metal cluster with rearranged nodes
+
+# Use the 10G NIC for cluster communications
+export ONOS_NIC="10.254.1.*"
+
+# ONOS Test proxy
+export OCT=10.254.1.200
+
+# Use the 1G NICs for external access
+export OC1=10.254.1.207
+export OC2=10.254.1.202
+export OC3=10.254.1.203
+export OC4=10.254.1.204
+export OC5=10.254.1.206
+
+export OCI=${OC1}
+
+export ONOS_FEATURES=webconsole,onos-api,onos-core,onos-cli,onos-rest,onos-null
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/BootstrapMessageHandler.java b/web/gui/src/main/java/org/onosproject/ui/impl/BootstrapMessageHandler.java
deleted file mode 100644
index 1ec23d7..0000000
--- a/web/gui/src/main/java/org/onosproject/ui/impl/BootstrapMessageHandler.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * 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.ui.impl;
-
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.node.ArrayNode;
-import com.fasterxml.jackson.databind.node.ObjectNode;
-import com.google.common.collect.ImmutableSet;
-import org.onlab.osgi.ServiceDirectory;
-import org.onosproject.cluster.ClusterService;
-import org.onosproject.cluster.ControllerNode;
-import org.onosproject.ui.UiConnection;
-import org.onosproject.ui.UiMessageHandler;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-
-
-/**
- * Facility for creating and sending initial bootstrap message to the GUI.
- */
-public class BootstrapMessageHandler extends UiMessageHandler {
-
-    private static final String BOOTSTRAP = "bootstrap";
-
-    private static final Comparator<? super ControllerNode> NODE_COMPARATOR =
-            new Comparator<ControllerNode>() {
-                @Override
-                public int compare(ControllerNode o1, ControllerNode o2) {
-                    return o1.id().toString().compareTo(o2.id().toString());
-                }
-            };
-
-    private final ObjectMapper mapper = new ObjectMapper();
-
-    private ClusterService clusterService;
-
-    // TODO: ClusterEventListener - update GUI when instances come or go...
-
-    /**
-     * Creates a new bootstrap message handler for bootstrapping the GUI.
-     */
-    protected BootstrapMessageHandler() {
-        super(ImmutableSet.of(BOOTSTRAP));
-    }
-
-    @Override
-    public void init(UiConnection connection, ServiceDirectory directory) {
-        super.init(connection, directory);
-        clusterService = directory.get(ClusterService.class);
-
-        // Obtain list of cluster nodes and send bootstrap message to GUI
-        List<ControllerNode> nodes = new ArrayList<>(clusterService.getNodes());
-        Collections.sort(nodes, NODE_COMPARATOR);
-
-        ObjectNode payload = mapper.createObjectNode();
-
-        ArrayNode anode = mapper.createArrayNode();
-        for (ControllerNode node : nodes) {
-            anode.add(clusterNodeData(node));
-        }
-        payload.set("instances", anode);
-        connection.sendMessage(envelope(BOOTSTRAP, 0, payload));
-    }
-
-    private ObjectNode clusterNodeData(ControllerNode node) {
-        return mapper.createObjectNode()
-                .put("id", node.id().toString())
-                .put("ip", node.ip().toString())
-                .put("uiAttached", node.equals(clusterService.getLocalNode()));
-    }
-
-
-    @Override
-    public void process(ObjectNode message) {
-        // We registered for "bootstrap" events, but we don't expect
-        // the GUI to send us any; it was just that we had to define a
-        // non-empty set of events that we handle, in the constructor.
-    }
-
-}
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 2137ffa..ade30ea 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
@@ -305,7 +305,7 @@
                 .put("id", node.id().toString())
                 .put("ip", node.ip().toString())
                 .put("online", clusterService.getState(node.id()) == ACTIVE)
-                .put("uiAttached", event.subject().equals(clusterService.getLocalNode()))
+                .put("uiAttached", node.equals(clusterService.getLocalNode()))
                 .put("switches", switchCount);
 
         ArrayNode labels = mapper.createArrayNode();
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/UiExtensionManager.java b/web/gui/src/main/java/org/onosproject/ui/impl/UiExtensionManager.java
index 803ce94..c601a5f 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/UiExtensionManager.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/UiExtensionManager.java
@@ -61,7 +61,6 @@
                                     new UiView("device", "Devices"));
         UiMessageHandlerFactory messageHandlerFactory =
                 () -> ImmutableList.of(
-                        new BootstrapMessageHandler(),
                         new TopologyViewMessageHandler()
                 );
         return new UiExtension(coreViews, messageHandlerFactory, "core",
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 99ca2e9..dd00d49 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
@@ -16,9 +16,12 @@
 package org.onosproject.ui.impl;
 
 import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.fasterxml.jackson.databind.node.ObjectNode;
 import org.eclipse.jetty.websocket.WebSocket;
 import org.onlab.osgi.ServiceDirectory;
+import org.onosproject.cluster.ClusterService;
+import org.onosproject.cluster.ControllerNode;
 import org.onosproject.ui.UiConnection;
 import org.onosproject.ui.UiExtensionService;
 import org.onosproject.ui.UiMessageHandler;
@@ -99,6 +102,7 @@
         this.connection = connection;
         this.control = (FrameConnection) connection;
         createHandlers();
+        sendInstanceData();
     }
 
     @Override
@@ -143,6 +147,18 @@
         }
     }
 
+    @Override
+    public void sendMessage(String type, long sid, ObjectNode payload) {
+        ObjectNode message = mapper.createObjectNode();
+        message.put("event", type);
+        if (sid > 0) {
+            message.put("sid", sid);
+        }
+        message.set("payload", payload);
+        sendMessage(message);
+
+    }
+
     // Creates new message handlers.
     private void createHandlers() {
         handlers = new HashMap<>();
@@ -163,5 +179,24 @@
         handlers.forEach((type, handler) -> handler.destroy());
         handlers.clear();
     }
+
+    // Sends cluster node/instance information to allow GUI to fail-over.
+    private void sendInstanceData() {
+        ClusterService service = directory.get(ClusterService.class);
+        ArrayNode instances = mapper.createArrayNode();
+
+        for (ControllerNode node : service.getNodes()) {
+            ObjectNode instance = mapper.createObjectNode()
+                    .put("id", node.id().toString())
+                    .put("ip", node.ip().toString())
+                    .put("uiAttached", node.equals(service.getLocalNode()));
+            instances.add(instance);
+        }
+
+        ObjectNode payload = mapper.createObjectNode();
+        payload.set("instances", instances);
+        sendMessage("onosInstances", 0, payload);
+    }
+
 }
 
diff --git a/web/gui/src/main/webapp/app/fw/remote/websocket.js b/web/gui/src/main/webapp/app/fw/remote/websocket.js
index 16c1a2b..861bb40 100644
--- a/web/gui/src/main/webapp/app/fw/remote/websocket.js
+++ b/web/gui/src/main/webapp/app/fw/remote/websocket.js
@@ -29,8 +29,14 @@
         sid = 0,                // event sequence identifier
         handlers = {},          // event handler bindings
         pendingEvents = [],     // events TX'd while socket not up
-        url;                    // web socket URL
+        url,                    // web socket URL
+        instances = [];
 
+    var builtinHandlers = {
+            onosInstances: function (data) {
+                instances = data.instances;
+            }
+    }
 
     // ==========================
     // === Web socket callbacks
@@ -186,6 +192,11 @@
             wsock = _wsock_;
             vs = _vs_;
 
+            // Bind instance handlers
+            bindHandlers({
+                onosInstances: builtinHandlers
+            });
+
             return {
                 resetSid: resetSid,
                 createWebSocket: createWebSocket,