Thomas Vachuska

Starting on STC monitor.

Change-Id: I279ef5f26a0e3a5a44c6f597be6c2980f8c955ed
......@@ -17,6 +17,7 @@ ssh $remote "
cd /tmp/$ONOS_BITS
bin/onos-service server 1>/tmp/onos.out 2>/tmp/onos.err &
# Setup a symlink to allow other tools to work
# Setup a few symlinks to allow other tools to work
sudo ln -s /tmp/$ONOS_BITS $ONOS_INSTALL_DIR
sudo ln -s /tmp/$ONOS_BITS/$KARAF_DIST/data/log $ONOS_INSTALL_DIR/log
"
......
......@@ -51,6 +51,22 @@
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
<version>8.1.17.v20150415</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-servlet</artifactId>
<version>8.1.17.v20150415</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-websocket</artifactId>
<version>8.1.17.v20150415</version>
</dependency>
</dependencies>
<build>
......@@ -61,11 +77,22 @@
<version>2.3</version>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>org.onlab.stc.Main
</mainClass>
</transformer>
</transformers>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
</configuration>
<executions>
<execution>
......
......@@ -16,6 +16,9 @@
package org.onlab.stc;
import com.google.common.collect.ImmutableList;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletHandler;
import org.eclipse.jetty.util.log.Logger;
import org.onlab.stc.Coordinator.Status;
import java.io.FileInputStream;
......@@ -106,6 +109,9 @@ public final class Main {
coordinator = new Coordinator(scenario, compiler.processFlow(),
compiler.logDir());
coordinator.addListener(delegate);
startMonitorServer();
processCommand();
} catch (FileNotFoundException e) {
......@@ -113,6 +119,20 @@ public final class Main {
}
}
// Initiates a web-server for the monitor GUI.
private static void startMonitorServer() {
org.eclipse.jetty.util.log.Log.setLog(new NullLogger());
Server server = new Server(9999);
ServletHandler handler = new ServletHandler();
server.setHandler(handler);
handler.addServletWithMapping(MonitorWebSocketServlet.class, "/*");
try {
server.start();
} catch (Exception e) {
e.printStackTrace();
}
}
// Processes the appropriate command
private void processCommand() {
switch (command) {
......@@ -224,4 +244,65 @@ public final class Main {
}
}
// Logger to quiet Jetty down
private static class NullLogger implements Logger {
@Override
public String getName() {
return "quiet";
}
@Override
public void warn(String msg, Object... args) {
}
@Override
public void warn(Throwable thrown) {
}
@Override
public void warn(String msg, Throwable thrown) {
}
@Override
public void info(String msg, Object... args) {
}
@Override
public void info(Throwable thrown) {
}
@Override
public void info(String msg, Throwable thrown) {
}
@Override
public boolean isDebugEnabled() {
return false;
}
@Override
public void setDebugEnabled(boolean enabled) {
}
@Override
public void debug(String msg, Object... args) {
}
@Override
public void debug(Throwable thrown) {
}
@Override
public void debug(String msg, Throwable thrown) {
}
@Override
public Logger getLogger(String name) {
return this;
}
@Override
public void ignore(Throwable ignored) {
}
}
}
......
/*
* 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.onlab.stc;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.eclipse.jetty.websocket.WebSocket;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
/**
* Web socket capable of interacting with the STC monitor GUI.
*/
public class MonitorWebSocket implements WebSocket.OnTextMessage, WebSocket.OnControl {
private static final Logger log = LoggerFactory.getLogger(MonitorWebSocket.class);
private static final long MAX_AGE_MS = 30_000;
private static final byte PING = 0x9;
private static final byte PONG = 0xA;
private static final byte[] PING_DATA = new byte[]{(byte) 0xde, (byte) 0xad};
private Connection connection;
private FrameConnection control;
private final ObjectMapper mapper = new ObjectMapper();
private long lastActive = System.currentTimeMillis();
/**
* Issues a close on the connection.
*/
synchronized void close() {
destroyHandlers();
if (connection.isOpen()) {
connection.close();
}
}
/**
* Indicates if this connection is idle.
*
* @return true if idle or closed
*/
synchronized boolean isIdle() {
long quietFor = System.currentTimeMillis() - lastActive;
boolean idle = quietFor > MAX_AGE_MS;
if (idle || (connection != null && !connection.isOpen())) {
log.debug("IDLE (or closed) websocket [{} ms]", quietFor);
return true;
} else if (connection != null) {
try {
control.sendControl(PING, PING_DATA, 0, PING_DATA.length);
} catch (IOException e) {
log.warn("Unable to send ping message due to: ", e);
}
}
return false;
}
@Override
public void onOpen(Connection connection) {
this.connection = connection;
this.control = (FrameConnection) connection;
try {
createHandlers();
log.info("GUI client connected");
} catch (Exception e) {
log.warn("Unable to open monitor connection: {}", e);
this.connection.close();
this.connection = null;
this.control = null;
}
}
@Override
public synchronized void onClose(int closeCode, String message) {
destroyHandlers();
log.info("GUI client disconnected [close-code={}, message={}]",
closeCode, message);
}
@Override
public boolean onControl(byte controlCode, byte[] data, int offset, int length) {
lastActive = System.currentTimeMillis();
return true;
}
@Override
public void onMessage(String data) {
lastActive = System.currentTimeMillis();
try {
ObjectNode message = (ObjectNode) mapper.reader().readTree(data);
// TODO:
log.info("Got message: {}", message);
} catch (Exception e) {
log.warn("Unable to parse GUI message {} due to {}", data, e);
log.debug("Boom!!!", e);
}
}
public synchronized void sendMessage(ObjectNode message) {
try {
if (connection.isOpen()) {
connection.sendMessage(message.toString());
}
} catch (IOException e) {
log.warn("Unable to send message {} to GUI due to {}", message, e);
log.debug("Boom!!!", e);
}
}
public synchronized 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 synchronized void createHandlers() {
}
// Destroys message handlers.
private synchronized void destroyHandlers() {
}
}
/*
* 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.onlab.stc;
import com.google.common.io.ByteStreams;
import com.google.common.net.MediaType;
import org.eclipse.jetty.websocket.WebSocket;
import org.eclipse.jetty.websocket.WebSocketServlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
/**
* Web socket servlet capable of creating web sockets for the STC monitor.
*/
public class MonitorWebSocketServlet extends WebSocketServlet {
private static final long PING_DELAY_MS = 5000;
private static final String DOT = ".";
private static MonitorWebSocketServlet instance;
private final Set<MonitorWebSocket> sockets = new HashSet<>();
private final Timer timer = new Timer();
private final TimerTask pruner = new Pruner();
/**
* Closes all currently open monitor web-sockets.
*/
public static void closeAll() {
if (instance != null) {
instance.sockets.forEach(MonitorWebSocket::close);
instance.sockets.clear();
}
}
@Override
public void init() throws ServletException {
super.init();
instance = this;
System.out.println("Yo!!!!");
timer.schedule(pruner, PING_DELAY_MS, PING_DELAY_MS);
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String uri = req.getRequestURI();
uri = uri.length() <= 1 ? "/index.html" : uri;
InputStream resource = getClass().getResourceAsStream(uri);
if (resource == null) {
resp.setStatus(HttpServletResponse.SC_NOT_FOUND);
} else {
byte[] entity = ByteStreams.toByteArray(resource);
resp.setStatus(HttpServletResponse.SC_OK);
resp.setContentType(contentType(uri).toString());
resp.setContentLength(entity.length);
resp.getOutputStream().write(entity);
}
}
private MediaType contentType(String uri) {
int sep = uri.lastIndexOf(DOT);
String ext = sep > 0 ? uri.substring(sep + 1) : null;
return ext == null ? MediaType.APPLICATION_BINARY :
ext.equals("html") ? MediaType.HTML_UTF_8 :
ext.equals("js") ? MediaType.JAVASCRIPT_UTF_8 :
ext.equals("css") ? MediaType.CSS_UTF_8 :
MediaType.APPLICATION_BINARY;
}
@Override
public WebSocket doWebSocketConnect(HttpServletRequest request, String protocol) {
System.out.println("Wazup????");
MonitorWebSocket socket = new MonitorWebSocket();
synchronized (sockets) {
sockets.add(socket);
}
return socket;
}
// Task for pruning web-sockets that are idle.
private class Pruner extends TimerTask {
@Override
public void run() {
synchronized (sockets) {
Iterator<MonitorWebSocket> it = sockets.iterator();
while (it.hasNext()) {
MonitorWebSocket socket = it.next();
if (socket.isIdle()) {
it.remove();
socket.close();
}
}
}
}
}
}
<!DOCTYPE html>
<!--
~ 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.
-->
<html>
<head lang="en">
<meta charset="UTF-8">
<title>Scenario Test Coordinator</title>
<script src="stc.js"></script>
<link rel="stylesheet" href="stc.css">
</head>
<body>
<h1>Scenario Test Coordinator</h1>
</body>
</html>
\ No newline at end of file
/*
* 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.
*/
.body {
font-family: Helvetica, Arial;
}
/*
* 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.
*/
(function () {
})();
\ No newline at end of file
......@@ -39,7 +39,7 @@
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-websocket</artifactId>
<version>8.1.15.v20140411</version>
<version>8.1.17.v20150415</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
......