Thomas Vachuska

Adding code to enable GUI failover.

Change-Id: I8423f17349411d24332db8670840438d0d8ec8ba
......@@ -29,4 +29,13 @@ public interface UiConnection {
*/
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
......
# 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
/*
* 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.
}
}
......@@ -305,7 +305,7 @@ public abstract class TopologyViewMessageHandlerBase extends UiMessageHandler {
.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();
......
......@@ -61,7 +61,6 @@ public class UiExtensionManager implements UiExtensionService {
new UiView("device", "Devices"));
UiMessageHandlerFactory messageHandlerFactory =
() -> ImmutableList.of(
new BootstrapMessageHandler(),
new TopologyViewMessageHandler()
);
return new UiExtension(coreViews, messageHandlerFactory, "core",
......
......@@ -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 @@ public class UiWebSocket
this.connection = connection;
this.control = (FrameConnection) connection;
createHandlers();
sendInstanceData();
}
@Override
......@@ -143,6 +147,18 @@ public class UiWebSocket
}
}
@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 @@ public class UiWebSocket
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);
}
}
......
......@@ -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,
......