Soft Patch Panel
 help / color / mirror / Atom feed
From: ogawa.yasufumi@lab.ntt.co.jp
To: ferruh.yigit@intel.com, spp@dpdk.org
Cc: Yasufumi Ogawa <ogawa.yasufumi@lab.ntt.co.jp>
Subject: [spp] [PATCH 1/2] controller: add websocket server for topo cmd
Date: Tue, 12 Jun 2018 16:03:42 +0900	[thread overview]
Message-ID: <20180612070343.5350-2-ogawa.yasufumi@lab.ntt.co.jp> (raw)
In-Reply-To: <20180612070343.5350-1-ogawa.yasufumi@lab.ntt.co.jp>

From: Yasufumi Ogawa <ogawa.yasufumi@lab.ntt.co.jp>

Topo command is for showing SPP network configuration graphically.
This update is to add the feature to output in browser. Network
topology is shown on a browser by runnign 'topo http' from SPP
controller.

It is implemented with 'tornado'[1] for websocket and 'vis.js'[2] for
parsing dot language and depicting on browser. Using websocket means
that you can display the topology immediately if it is updated.

Expression of dot of 'vis.js' is poor comparing with graphviz actually.
However, you can see the topology on remote node with 'topo http', and
'topo term' cannot do it.

To activate this 'topo http' command, you should install requried
packages with pip (or pip3).

  * tornado
  * websocket-client

[1] http://www.tornadoweb.org/en/stable/
[2] http://visjs.org/

Signed-off-by: Yasufumi Ogawa <ogawa.yasufumi@lab.ntt.co.jp>
---
 src/controller/websocket/spp_ws.py            | 98 +++++++++++++++++++
 src/controller/websocket/static/main.css      | 67 +++++++++++++
 src/controller/websocket/static/spp_ws.js     | 70 +++++++++++++
 src/controller/websocket/templates/index.html | 40 ++++++++
 4 files changed, 275 insertions(+)
 create mode 100755 src/controller/websocket/spp_ws.py
 create mode 100644 src/controller/websocket/static/main.css
 create mode 100644 src/controller/websocket/static/spp_ws.js
 create mode 100644 src/controller/websocket/templates/index.html

diff --git a/src/controller/websocket/spp_ws.py b/src/controller/websocket/spp_ws.py
new file mode 100755
index 0000000..848945a
--- /dev/null
+++ b/src/controller/websocket/spp_ws.py
@@ -0,0 +1,98 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2017-2018 Nippon Telegraph and Telephone Corporation
+
+import logging
+import os
+import tornado.escape
+import tornado.ioloop
+import tornado.options
+import tornado.web
+import tornado.websocket
+
+from tornado.options import define
+from tornado.options import options
+
+curdir = os.path.abspath(os.path.dirname(__file__))
+logging.basicConfig(
+    filename='%s/server.log' % curdir, level=logging.DEBUG)
+
+# Server address
+ipaddr = '127.0.0.1'  # used only for start message
+port = 8989
+
+define("port", default=port, help="Wait for 'topo http' command", type=int)
+
+
+class Application(tornado.web.Application):
+    def __init__(self):
+        handlers = [
+            (r"/", MainHandler),
+            (r"/spp_ws", SppWsHandler),
+        ]
+        settings = dict(
+            template_path=os.path.join(
+                os.path.dirname(__file__), "templates"),
+            static_path=os.path.join(
+                os.path.dirname(__file__), "static"),
+            cookie_secret="yasupyasup",
+            xsrf_cookies=True,
+        )
+        super(Application, self).__init__(handlers, **settings)
+
+
+class MainHandler(tornado.web.RequestHandler):
+    def get(self):
+        logging.info("Render to browser\n%s", SppWsHandler.dot)
+        self.render(
+            "index.html",
+            dotData=SppWsHandler.dot.replace('\n', ''))
+
+
+class SppWsHandler(tornado.websocket.WebSocketHandler):
+    waiters = set()
+    dot = ""
+
+    def get_compression_options(self):
+        # Non-None enables compression with default options.
+        return {}
+
+    def open(self):
+        SppWsHandler.waiters.add(self)
+
+    def on_close(self):
+        SppWsHandler.waiters.remove(self)
+
+    @classmethod
+    def update_dot(cls, msg):
+        cls.dot = msg
+
+    @classmethod
+    def send_updates(cls, msg):
+        logging.info(
+            "Send message to %d clients", len(cls.waiters))
+        for waiter in cls.waiters:
+            try:
+                waiter.write_message(msg)
+            except Exception:
+                pass
+                logging.error("Error sending message", exc_info=True)
+
+    def on_message(self, message):
+        logging.info("Received from client\n%r", message)
+        self.dot = message
+        SppWsHandler.update_dot(message)
+        SppWsHandler.send_updates(message)
+
+
+def main():
+    tornado.options.parse_command_line()
+    app = Application()
+    app.listen(options.port)
+    print('Running SPP topo server on http://%s:%d ...' % (
+        ipaddr, port))
+    tornado.ioloop.IOLoop.current().start()
+
+
+if __name__ == "__main__":
+    main()
diff --git a/src/controller/websocket/static/main.css b/src/controller/websocket/static/main.css
new file mode 100644
index 0000000..d2fdfa9
--- /dev/null
+++ b/src/controller/websocket/static/main.css
@@ -0,0 +1,67 @@
+body, html {
+  font: 10pt sans;
+  line-height: 1.5em;;
+  width: 100%;
+  height: 100%;
+  padding: 0;
+  margin: 0;
+  color: #4d4d4d;
+  box-sizing: border-box;
+  overflow: hidden;
+}
+
+#header {
+  margin: 0;
+  padding: 10px;
+  box-sizing: border-box;
+}
+
+#contents {
+  height: 100%;
+  margin: 0;
+  padding: 0;
+  box-sizing: border-box;
+  position: relative;
+}
+
+#right {
+  position: absolute;
+  width: 100%;
+  height: 100%;
+  margin: 0;
+  padding: 10px;
+  box-sizing: border-box;
+  display: inline-block;
+}
+
+#right {
+  top: 0;
+  right: 0;
+}
+
+#error {
+  color: red;
+}
+
+#data {
+  width: 100%;
+  height: 100%;
+  border: 1px solid #d3d3d3;
+  box-sizing: border-box;
+  resize: none;
+}
+
+#draw, #reset {
+  padding: 5px 15px;
+}
+
+#spp_topo_network{
+  width: 100%;
+  height: 100%;
+  border: 1px solid #d3d3d3;
+  box-sizing: border-box;
+}
+
+a:hover {
+  color: red;
+}
diff --git a/src/controller/websocket/static/spp_ws.js b/src/controller/websocket/static/spp_ws.js
new file mode 100644
index 0000000..80b70b6
--- /dev/null
+++ b/src/controller/websocket/static/spp_ws.js
@@ -0,0 +1,70 @@
+/**
+ * @license
+ * SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2017-2018 Nippon Telegraph and Telephone Corporation
+ */
+
+$(document).ready(function() {
+  if (!window.console) window.console = {};
+  if (!window.console.log) window.console.log = function() {};
+
+  updater.start();
+});
+
+var updater = {
+  socket: null,
+
+  start: function() {
+    var url = "ws://" + location.host + "/spp_ws";
+    updater.socket = new WebSocket(url);
+    updater.socket.onmessage = function(event) {
+      updater.showDot(event.data);
+    }
+  },
+
+  showDot: function(dot) {
+    console.log(dot);
+    dotData = dot
+    draw();
+  }
+};
+
+
+// create a network
+var container = document.getElementById('spp_topo_network');
+var options = {
+  physics: {
+    stabilization: false,
+    barnesHut: {
+      springLength: 200
+    }
+  }
+};
+var data = {};
+var network = new vis.Network(container, data, options);
+
+$(window).resize(resize);
+$(window).load(draw);
+
+function resize() {
+  $('#contents').height($('body').height() - $('#header').height() - 30);
+}
+
+function draw() {
+  try {
+    resize();
+    $('#error').html('');
+
+    data = vis.network.convertDot(dotData);
+
+    network.setData(data);
+  }
+  catch (err) {
+    // show an error message
+    $('#error').html(err.toString());
+  }
+}
+
+window.onload = function() {
+  draw();
+}
diff --git a/src/controller/websocket/templates/index.html b/src/controller/websocket/templates/index.html
new file mode 100644
index 0000000..ceedc21
--- /dev/null
+++ b/src/controller/websocket/templates/index.html
@@ -0,0 +1,40 @@
+<!doctype html>
+<html>
+  <head>
+    <title>SPP Topo Viewer</title>
+
+    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"
+      type="text/javascript"></script>
+    <script src="http://visjs.org/dist/vis.js" type="text/javascript"></script>
+    <link rel="stylesheet" href="{{ static_url("main.css") }}" type="text/css" />
+    <link rel="stylesheet" href="http://visjs.org/dist/vis-network.min.css"
+    type="text/css" />
+  </head>
+  <body>
+
+    <div id="header">
+      <h1>SPP Topo Viewer</h1>
+      <div>
+        Show topology of
+        <a href="https://spp.readthedocs.io/en/latest/index.html">SPP</a> network
+        from '<i>topo http</i>' command.
+        Please refer
+        <a href="https://spp.readthedocs.io/en/latest/commands/experimental.html#topo">
+          topo
+        </a> command reference.
+      </div>
+    </div>
+
+    <div id="contents">
+      <div id="right">
+        <div id="spp_topo_network"></div>
+      </div>
+    </div>
+
+    <script type="text/javascript">
+      {% autoescape None %}
+      var dotData = '{{ dotData }}';
+    </script>
+    <script src="{{ static_url("spp_ws.js") }}" type="text/javascript"></script>
+  </body>
+</html>
-- 
2.17.1

  reply	other threads:[~2018-06-12  7:03 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-06-12  7:03 [spp] [PATCH 0/2] Add topo http command ogawa.yasufumi
2018-06-12  7:03 ` ogawa.yasufumi [this message]
2018-06-12  7:03 ` [spp] [PATCH 2/2] docs: update topo command manual ogawa.yasufumi
2018-06-27 10:52 ` [spp] [PATCH 0/2] Add topo http command Ferruh Yigit

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20180612070343.5350-2-ogawa.yasufumi@lab.ntt.co.jp \
    --to=ogawa.yasufumi@lab.ntt.co.jp \
    --cc=ferruh.yigit@intel.com \
    --cc=spp@dpdk.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).