blob: 83e54b4310c3a53b7f5f24d125cf02b6569f1bd5 [file] [log] [blame]
/*
* Copyright 2014 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.onlab.onos.gui;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.eclipse.jetty.websocket.WebSocket;
import org.onlab.onos.cluster.ClusterEvent;
import org.onlab.onos.cluster.ClusterEventListener;
import org.onlab.onos.cluster.ControllerNode;
import org.onlab.onos.core.ApplicationId;
import org.onlab.onos.core.CoreService;
import org.onlab.onos.mastership.MastershipEvent;
import org.onlab.onos.mastership.MastershipListener;
import org.onlab.onos.net.Device;
import org.onlab.onos.net.Host;
import org.onlab.onos.net.HostId;
import org.onlab.onos.net.Link;
import org.onlab.onos.net.Path;
import org.onlab.onos.net.device.DeviceEvent;
import org.onlab.onos.net.device.DeviceListener;
import org.onlab.onos.net.flow.DefaultTrafficSelector;
import org.onlab.onos.net.flow.DefaultTrafficTreatment;
import org.onlab.onos.net.host.HostEvent;
import org.onlab.onos.net.host.HostListener;
import org.onlab.onos.net.intent.HostToHostIntent;
import org.onlab.onos.net.intent.Intent;
import org.onlab.onos.net.intent.IntentEvent;
import org.onlab.onos.net.intent.IntentId;
import org.onlab.onos.net.intent.IntentListener;
import org.onlab.onos.net.intent.PathIntent;
import org.onlab.onos.net.link.LinkEvent;
import org.onlab.onos.net.link.LinkListener;
import org.onlab.osgi.ServiceDirectory;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import static org.onlab.onos.cluster.ClusterEvent.Type.INSTANCE_ADDED;
import static org.onlab.onos.net.DeviceId.deviceId;
import static org.onlab.onos.net.HostId.hostId;
import static org.onlab.onos.net.device.DeviceEvent.Type.DEVICE_ADDED;
import static org.onlab.onos.net.host.HostEvent.Type.HOST_ADDED;
import static org.onlab.onos.net.link.LinkEvent.Type.LINK_ADDED;
/**
* Web socket capable of interacting with the GUI topology view.
*/
public class TopologyWebSocket
extends TopologyMessages implements WebSocket.OnTextMessage {
private static final String APP_ID = "org.onlab.onos.gui";
private final ApplicationId appId;
private Connection connection;
private final ClusterEventListener clusterListener = new InternalClusterListener();
private final DeviceListener deviceListener = new InternalDeviceListener();
private final LinkListener linkListener = new InternalLinkListener();
private final HostListener hostListener = new InternalHostListener();
private final MastershipListener mastershipListener = new InternalMastershipListener();
private final IntentListener intentListener = new InternalIntentListener();
// Intents that are being monitored for the GUI
private static Map<IntentId, Long> intentsToMonitor = new ConcurrentHashMap<>();
/**
* Creates a new web-socket for serving data to GUI topology view.
*
* @param directory service directory
*/
public TopologyWebSocket(ServiceDirectory directory) {
super(directory);
appId = directory.get(CoreService.class).registerApplication(APP_ID);
}
@Override
public void onOpen(Connection connection) {
this.connection = connection;
addListeners();
sendAllInstances();
sendAllDevices();
sendAllLinks();
sendAllHosts();
}
@Override
public void onClose(int closeCode, String message) {
removeListeners();
}
@Override
public void onMessage(String data) {
try {
ObjectNode event = (ObjectNode) mapper.reader().readTree(data);
String type = string(event, "event", "unknown");
if (type.equals("showDetails")) {
showDetails(event);
} else if (type.equals("updateMeta")) {
updateMetaUi(event);
} else if (type.equals("requestPath")) {
createHostIntent(event);
} else if (type.equals("requestTraffic")) {
sendTraffic(event);
} else if (type.equals("cancelTraffic")) {
cancelTraffic(event);
}
} catch (Exception e) {
System.out.println("WTF?! " + data);
e.printStackTrace();
}
}
// Sends the specified data to the client.
private void sendMessage(ObjectNode data) {
try {
connection.sendMessage(data.toString());
} catch (IOException e) {
e.printStackTrace();
}
}
// Sends all controller nodes to the client as node-added messages.
private void sendAllInstances() {
for (ControllerNode node : clusterService.getNodes()) {
sendMessage(instanceMessage(new ClusterEvent(INSTANCE_ADDED, node)));
}
}
// Sends all devices to the client as device-added messages.
private void sendAllDevices() {
for (Device device : deviceService.getDevices()) {
sendMessage(deviceMessage(new DeviceEvent(DEVICE_ADDED, device)));
}
}
// Sends all links to the client as link-added messages.
private void sendAllLinks() {
for (Link link : linkService.getLinks()) {
sendMessage(linkMessage(new LinkEvent(LINK_ADDED, link)));
}
}
// Sends all hosts to the client as host-added messages.
private void sendAllHosts() {
for (Host host : hostService.getHosts()) {
sendMessage(hostMessage(new HostEvent(HOST_ADDED, host)));
}
}
// Sends back device or host details.
private void showDetails(ObjectNode event) {
ObjectNode payload = payload(event);
String type = string(payload, "type", "unknown");
if (type.equals("device")) {
sendMessage(deviceDetails(deviceId(string(payload, "id")),
number(event, "sid")));
} else if (type.equals("host")) {
sendMessage(hostDetails(hostId(string(payload, "id")),
number(event, "sid")));
}
}
// Creates host-to-host intent.
private void createHostIntent(ObjectNode event) {
ObjectNode payload = payload(event);
long id = number(event, "sid");
// TODO: add protection against device ids and non-existent hosts.
HostId one = hostId(string(payload, "one"));
HostId two = hostId(string(payload, "two"));
HostToHostIntent hostIntent = new HostToHostIntent(appId, one, two,
DefaultTrafficSelector.builder().build(),
DefaultTrafficTreatment.builder().build());
intentsToMonitor.put(hostIntent.id(), number(event, "sid"));
intentService.submit(hostIntent);
}
// Sends traffic message.
private void sendTraffic(ObjectNode event) {
ObjectNode payload = payload(event);
long id = number(event, "sid");
IntentId intentId = IntentId.valueOf(payload.path("intentId").asLong());
if (payload != null) {
payload.put("traffic", true);
sendMessage(envelope("showPath", id, payload));
} else {
sendMessage(warning(id, "No path found"));
}
}
// Cancels sending traffic messages.
private void cancelTraffic(ObjectNode event) {
// TODO: implement this
}
// Adds all internal listeners.
private void addListeners() {
clusterService.addListener(clusterListener);
deviceService.addListener(deviceListener);
linkService.addListener(linkListener);
hostService.addListener(hostListener);
mastershipService.addListener(mastershipListener);
intentService.addListener(intentListener);
}
// Removes all internal listeners.
private void removeListeners() {
clusterService.removeListener(clusterListener);
deviceService.removeListener(deviceListener);
linkService.removeListener(linkListener);
hostService.removeListener(hostListener);
mastershipService.removeListener(mastershipListener);
}
// Cluster event listener.
private class InternalClusterListener implements ClusterEventListener {
@Override
public void event(ClusterEvent event) {
sendMessage(instanceMessage(event));
}
}
// Device event listener.
private class InternalDeviceListener implements DeviceListener {
@Override
public void event(DeviceEvent event) {
sendMessage(deviceMessage(event));
}
}
// Link event listener.
private class InternalLinkListener implements LinkListener {
@Override
public void event(LinkEvent event) {
sendMessage(linkMessage(event));
}
}
// Host event listener.
private class InternalHostListener implements HostListener {
@Override
public void event(HostEvent event) {
sendMessage(hostMessage(event));
}
}
// Mastership event listener.
private class InternalMastershipListener implements MastershipListener {
@Override
public void event(MastershipEvent event) {
// TODO: Is DeviceEvent.Type.DEVICE_MASTERSHIP_CHANGED the same?
}
}
// Intent event listener.
private class InternalIntentListener implements IntentListener {
@Override
public void event(IntentEvent event) {
Intent intent = event.subject();
Long sid = intentsToMonitor.get(intent.id());
if (sid != null) {
List<Intent> installable = intentService.getInstallableIntents(intent.id());
if (installable != null && !installable.isEmpty()) {
PathIntent pathIntent = (PathIntent) installable.iterator().next();
Path path = pathIntent.path();
ObjectNode payload = pathMessage(path).put("intentId", intent.id().toString());
sendMessage(envelope("showPath", sid, payload));
}
}
}
}
}