From: peusterm Date: Tue, 18 Jun 2019 14:08:47 +0000 (+0200) Subject: Fixes of the REST interface dashboard. X-Git-Tag: v7.0.0rc1~26 X-Git-Url: https://osm.etsi.org/gitweb/?p=osm%2Fvim-emu.git;a=commitdiff_plain;h=d87400255e1997f85b9cdebc583811b70cca4ab7 Fixes of the REST interface dashboard. Change-Id: I4aad748e68a2f56017d2327831cf0bba098c0f29 Signed-off-by: peusterm --- diff --git a/src/emuvim/api/rest/rest_api_endpoint.py b/src/emuvim/api/rest/rest_api_endpoint.py index cefda5f..b9c11ab 100755 --- a/src/emuvim/api/rest/rest_api_endpoint.py +++ b/src/emuvim/api/rest/rest_api_endpoint.py @@ -26,7 +26,7 @@ import logging import threading -from flask import Flask +from flask import Flask, send_from_directory from flask_restful import Api from gevent.pywsgi import WSGIServer @@ -64,18 +64,17 @@ class RestApiEndpoint(object): self.connectDCNetwork(DCnetwork) # setup Flask - # find directory of dashboard files - dashboard_file = pkg_resources.resource_filename( - 'emuvim.dashboard', "index.html") - dashboard_dir = path.dirname(dashboard_file) - logging.info("Started emu dashboard: {0}".format(dashboard_dir)) - - self.app = Flask(__name__, static_folder=dashboard_dir, - static_url_path='/dashboard') + self.app = Flask(__name__) self.api = Api(self.app) - # setup endpoints + # define dashboard endpoints + db_dir, db_file = self.get_dashboard_path() + @self.app.route('/dashboard/') + def db_file(path): + logging.info("[DB] Serving: {}".format(path)) + return send_from_directory(db_dir, path) + # define REST API endpoints # compute related actions (start/stop VNFs, get info) self.api.add_resource( Compute, "/restapi/compute//") @@ -118,6 +117,17 @@ class RestApiEndpoint(object): logging.debug("Created API endpoint %s(%s:%d)" % (self.__class__.__name__, self.ip, self.port)) + def get_dashboard_path(self): + """ + Return absolute path to dashboard files. + """ + db_file = pkg_resources.resource_filename( + 'emuvim.dashboard', "index.html") + db_dir = path.dirname(db_file) + logging.info("[DB] Serving emulator dashboard from: {} and {}" + .format(db_dir, db_file)) + return db_dir, db_file + def connectDatacenter(self, dc): compute.dcs[dc.label] = dc logging.info( diff --git a/src/emuvim/dashboard/README.md b/src/emuvim/dashboard/README.md index 57409f2..ca92062 100755 --- a/src/emuvim/dashboard/README.md +++ b/src/emuvim/dashboard/README.md @@ -26,7 +26,7 @@ partner consortium (www.sonata-nfv.eu). --> -# son-emu Dashboard +# vim-emu dashbaord -A simple web-based dashboard that polls the REST API and displays running services etc. It does not do much more than son-cli but it looks nicer and improves the visualization of the emulator state for live demos. +Reachable under `:/dashboard/dashboard.html`. diff --git a/src/emuvim/dashboard/dashboard.html b/src/emuvim/dashboard/dashboard.html new file mode 100755 index 0000000..293e7e7 --- /dev/null +++ b/src/emuvim/dashboard/dashboard.html @@ -0,0 +1,124 @@ + + + + + + + + + + VIM-EMU Dashboard + + + + + + + + + + + + + + + + + + + + + + +
+
+

VIM-EMU Dashboard

+
+ + + +
+
+
+ +
+ + +
+ +
Emulated Datacenters  0Lateness: -
+ + +
+
+ +
 
+ +
+ +
Running Containers  0Lateness: -
+ + +
+
+ +
 
+ +
+
Deployment Graph  
+ + +
+
+ + + + +
+ +
(c) 2019 by ETSI OSM, 5GTANGO consortium, SONATA-NFV consortium, Paderborn University and IMEC
+ + + + + diff --git a/src/emuvim/dashboard/index.html b/src/emuvim/dashboard/index.html deleted file mode 100755 index c87fc5d..0000000 --- a/src/emuvim/dashboard/index.html +++ /dev/null @@ -1,122 +0,0 @@ - - - - - - - - - - Emulator Dashboard - - - - - - - - - - - - - - - - - - - - - - - - -
-
-

Emulator Dashboard

-
- - - -
-
- - -
- -
Emulated Datacenters  0Lateness: -
- - -
-
- -
 
- -
- -
Running Containers  0Lateness: -
- - -
-
- -
 
- -
-
Placement Graph  
- - -
-
- - - -
- -
(c) 2017 by SONATA Consortium and Paderborn University and IMEC
- - - - - diff --git a/src/emuvim/dashboard/index_upb.html b/src/emuvim/dashboard/index_upb.html deleted file mode 100755 index 0dff2be..0000000 --- a/src/emuvim/dashboard/index_upb.html +++ /dev/null @@ -1,108 +0,0 @@ - - - - - - - - - - Emulator Dashboard - - - - - - - - - - - - - - - - - - - - - - -
-
-

Emulator Dashboard

-
- -
-
- - -
- -
Emulated Datacenters  0Lateness: -
- - -
-
- -
 
- -
- -
Running Containers  0Lateness: -
- - -
-
- - -
- -
(c) 2017 by SONATA Consortium, Paderborn University and IMEC
- - - - - diff --git a/src/emuvim/dashboard/js/graph.js b/src/emuvim/dashboard/js/graph.js new file mode 100644 index 0000000..fff8872 --- /dev/null +++ b/src/emuvim/dashboard/js/graph.js @@ -0,0 +1,100 @@ +/* + Copyright (c) 2017 SONATA-NFV, IMEC and Paderborn University + ALL RIGHTS RESERVED. + + 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. + + Neither the name of the SONATA-NFV, Paderborn University + nor the names of its contributors may be used to endorse or promote + products derived from this software without specific prior written + permission. + + This work has been performed in the framework of the SONATA project, + funded by the European Commission under Grant number 671517 through + the Horizon 2020 and 5G-PPP programmes. The authors would like to + acknowledge the contributions of their colleagues of the SONATA + partner consortium (www.sonata-nfv.eu). +*/ + + //functions to make the nodes stick after they have been manually maoved + function tick() { + link.attr("x1", function(d) { return d.source.x; }) + .attr("y1", function(d) { return d.source.y; }) + .attr("x2", function(d) { return d.target.x; }) + .attr("y2", function(d) { return d.target.y; }); + + node.attr("cx", function(d) { return d.x; }) + .attr("cy", function(d) { return d.y; }); + } + + function dragstart(d) { + d3.select(this).classed("fixed", d.fixed = true); + } + +var width = 960, + height = 500, + color = d3.scale.category10(); + +var svg = d3.select("#table_graph").append("svg") + .attr("width", width) + .attr("height", height); + +var force = d3.layout.force() + .gravity(0.05) + .distance(100) + .charge(-100) + .size([width, height]) + .on("tick", tick); + +var drag = force.drag() + .on("dragstart", dragstart); + +d3.json("/restapi/network/d3jsgraph", function(error, json) { + if (error) throw error; + + force + .nodes(json.nodes) + .links(json.links) + .start(); + + var link = svg.selectAll(".link") + .data(json.links) + .enter().append("line") + .attr("class", "link"); + + var node = svg.selectAll(".node") + .data(json.nodes) + .enter().append("g") + .attr("class", "node") + .call(drag); + + node.append("circle") + .attr("r", 10) + .style("fill", function(d) { return color(d.group); }); + + node.append("text") + .attr("dx", 12) + .attr("dy", ".35em") + .text(function(d) { return d.name }); + + force.on("tick", function() { + link.attr("x1", function(d) { return d.source.x; }) + .attr("y1", function(d) { return d.source.y; }) + .attr("x2", function(d) { return d.target.x; }) + .attr("y2", function(d) { return d.target.y; }); + + node.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; }); + }); + + +}); diff --git a/src/emuvim/dashboard/js/main.js b/src/emuvim/dashboard/js/main.js index 19169ac..a705840 100755 --- a/src/emuvim/dashboard/js/main.js +++ b/src/emuvim/dashboard/js/main.js @@ -25,21 +25,21 @@ acknowledge the contributions of their colleagues of the SONATA partner consortium (www.sonata-nfv.eu). */ -var API_HOST = "http://127.0.0.1:5001"; +var API_HOST = ""; // set to a remote url if dashboard is not served by REST API server var ERROR_ALERT = false; var TIMESTAMP = 0; var CONNECTED = false; var LATENESS_UPDATE_INTERVAL = 50; -var DATA_UPDATE_INTERVAL = 1000 * 10; +var DATA_UPDATE_INTERVAL = 1000 * 30; // 30 seconds var LAST_UPDATE_TIMESTAMP_CONTAINER = 0; var LAST_UPDATE_TIMESTAMP_DATACENTER = 0; function update_lateness_loop() { lateness_datacenter= (Date.now() - LAST_UPDATE_TIMESTAMP_DATACENTER) / 1000; - $("#lbl_lateness_datacenter").text("Lateness: " + Number(lateness_datacenter).toPrecision(3) + "s"); + $("#lbl_lateness_datacenter").text("Lateness: " + Number(lateness_datacenter).toPrecision(2) + "s"); lateness_container= (Date.now() - LAST_UPDATE_TIMESTAMP_CONTAINER) / 1000; - $("#lbl_lateness_container").text("Lateness: " + Number(lateness_container).toPrecision(3) + "s"); + $("#lbl_lateness_container").text("Lateness: " + Number(lateness_container).toPrecision(2) + "s"); // loop while connected if(CONNECTED) setTimeout(update_lateness_loop, LATENESS_UPDATE_INTERVAL) @@ -53,11 +53,13 @@ function errorAjaxConnection() { ERROR_ALERT = true; // show message - alert("ERROR!\nAPI request failed.\n\n Please check the backend connection.", function() { - // callback - ERROR_ALERT = false; - }); + //alert("API request failed. Is the emulator running?", function() { + // // callback + // ERROR_ALERT = false; + //}); } + CONNECTED = false; + console.error("API request failed. Is the emulator running?") } @@ -72,7 +74,7 @@ function update_table_datacenter(data) $.each(data, function(i, item) { var row_str = ""; row_str += ''; - row_str += '' + item.label + '1'; + row_str += '' + item.label + ''; row_str += '' + item.internalname + ''; row_str += '' + item.switch + ''; row_str += '' + item.n_running_containers + ''; @@ -93,43 +95,27 @@ function update_table_container(data) // clear table $("#table_container").empty(); // header - $("#table_container").append('DatacenterContainerImagedocker0--Networking--
'); + $("#table_container").append('DatacenterContainerImagedocker0Status'); // fill table $.each(data, function(i, item) { var row_str = ""; - row_str += ''; + row_str += ''; row_str += '' + item[1].datacenter + ''; row_str += '' + item[0] + ''; row_str += '' + item[1].image + ''; - row_str += '' + item[1].docker_network + ''; - row_str += ''; - //row_str += build_network_table(item[1].network, item[0]); - row_str += '
'; - row_str += ''; - $("#table_container").append(row_str); - build_network_table(item[1].network, item[0]); + row_str += '' + item[1].docker_network + ''; + if(item[1].state.Status == "running") + row_str += 'running'; + else + row_str += 'stopped'; + row_str += ''; + $("#table_container").append(row_str); }); $("#lbl_container_count").text(data.length); - $("#table_network").append('
datacenter portinterfaceipmac
') // update lateness counter LAST_UPDATE_TIMESTAMP_CONTAINER = Date.now(); } -function build_network_table(network_list, id) -{ - console.debug('network list ' + id) - console.debug(network_list) - var row_str = ""; - network_list.forEach(function(interface) { - row_str += ''; - row_str += '' + interface.dc_portname + ''; - row_str += '' + interface.intf_name + ''; - row_str += '' + interface.ip + ''; - row_str += '' + interface.mac + ''; - row_str += ''; - }); - $("#network_list_" + id).append(row_str) -} function fetch_datacenter() { @@ -149,14 +135,6 @@ function fetch_container() } -function fetch_d3graph() -{ - // do HTTP request and trigger gui update on success - var request_url = API_HOST + "/restapi/network/d3jsgraph"; - console.debug("fetching from: " + request_url); - //$.getJSON(request_url, update_graph); -} - function fetch_loop() { // only fetch if we are connected @@ -177,7 +155,7 @@ function connect() { console.info("connect()"); // get host address - API_HOST = "http://" + $("#text_api_host").val(); + //API_HOST = "http://" + $("#text_api_host").val(); console.debug("API address: " + API_HOST); // reset data LAST_UPDATE_TIMESTAMP_DATACENTER = Date.now(); @@ -187,36 +165,19 @@ function connect() update_lateness_loop(); // restart data fetch loop fetch_loop(); - // gui updates - $("#btn_disconnect").removeClass("disabled"); - $("#btn_connect").addClass("disabled"); -} - -function disconnect() -{ - console.info("disconnect()"); - CONNECTED = false; - // gui updates - $("#btn_connect").removeClass("disabled"); - $("#btn_disconnect").addClass("disabled"); } $(document).ready(function(){ console.info("document ready"); // setup global connection error handling - /* + $.ajaxSetup({ "error": errorAjaxConnection }); - // add listeners - $("#btn_connect").click(connect); - $("#btn_disconnect").click(disconnect); - */ - setTimeout(fetch_datacenter, 500);//fetch_datacenter(); - setTimeout(fetch_container, 1000);//fetch_container(); - + // connect + connect(); // additional refresh on window focus $(window).focus(function () { diff --git a/src/emuvim/dashboard/js/main_upb.js b/src/emuvim/dashboard/js/main_upb.js deleted file mode 100755 index 28cfa83..0000000 --- a/src/emuvim/dashboard/js/main_upb.js +++ /dev/null @@ -1,190 +0,0 @@ -/* - Copyright (c) 2017 SONATA-NFV and Paderborn University - ALL RIGHTS RESERVED. - - 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. - - Neither the name of the SONATA-NFV, Paderborn University - nor the names of its contributors may be used to endorse or promote - products derived from this software without specific prior written - permission. - - This work has been performed in the framework of the SONATA project, - funded by the European Commission under Grant number 671517 through - the Horizon 2020 and 5G-PPP programmes. The authors would like to - acknowledge the contributions of their colleagues of the SONATA - partner consortium (www.sonata-nfv.eu). -*/ -var API_HOST = ""; // set to a remote url if dashboard is not served by REST API server -var ERROR_ALERT = false; -var TIMESTAMP = 0; -var CONNECTED = false; -var LATENESS_UPDATE_INTERVAL = 50; -var DATA_UPDATE_INTERVAL = 1000 * 2; -var LAST_UPDATE_TIMESTAMP_CONTAINER = 0; -var LAST_UPDATE_TIMESTAMP_DATACENTER = 0; - - -function update_lateness_loop() { - lateness_datacenter= (Date.now() - LAST_UPDATE_TIMESTAMP_DATACENTER) / 1000; - $("#lbl_lateness_datacenter").text("Lateness: " + Number(lateness_datacenter).toPrecision(2) + "s"); - lateness_container= (Date.now() - LAST_UPDATE_TIMESTAMP_CONTAINER) / 1000; - $("#lbl_lateness_container").text("Lateness: " + Number(lateness_container).toPrecision(2) + "s"); - // loop while connected - if(CONNECTED) - setTimeout(update_lateness_loop, LATENESS_UPDATE_INTERVAL) -} - - -function errorAjaxConnection() -{ - // only do once - if(!ERROR_ALERT) - { - ERROR_ALERT = true; - // show message - alert("API request failed. Is the emulator running?", function() { - // callback - ERROR_ALERT = false; - }); - } - CONNECTED = false; -} - - -function update_table_datacenter(data) -{ - console.debug(data) - // clear table - $("#table_datacenter").empty(); - // header - $("#table_datacenter").append('LabelInt. NameSwitchNum. ContainersVNFs'); - // fill table - $.each(data, function(i, item) { - var row_str = ""; - row_str += ''; - row_str += '' + item.label + '1'; - row_str += '' + item.internalname + ''; - row_str += '' + item.switch + ''; - row_str += '' + item.n_running_containers + ''; - //row_str += '' + Object.keys(item.metadata).length + ''; - row_str += '' + item.vnf_list + ''; - row_str += ''; - $("#table_datacenter").append(row_str); - }); - $("#lbl_datacenter_count").text(data.length); - // update lateness counter - LAST_UPDATE_TIMESTAMP_DATACENTER = Date.now(); -} - - -function update_table_container(data) -{ - console.debug(data) - // clear table - $("#table_container").empty(); - // header - $("#table_container").append('DatacenterContainerImagedocker0Status'); - // fill table - $.each(data, function(i, item) { - var row_str = ""; - row_str += ''; - row_str += '' + item[1].datacenter + ''; - row_str += '' + item[0] + ''; - row_str += '' + item[1].image + ''; - row_str += '' + item[1].docker_network + ''; - if(item[1].state.Status == "running") - row_str += 'running'; - else - row_str += 'stopped'; - row_str += ''; - $("#table_container").append(row_str); - }); - $("#lbl_container_count").text(data.length); - // update lateness counter - LAST_UPDATE_TIMESTAMP_CONTAINER = Date.now(); -} - - -function fetch_datacenter() -{ - // do HTTP request and trigger gui update on success - var request_url = API_HOST + "/restapi/datacenter"; - console.debug("fetching from: " + request_url); - $.getJSON(request_url, update_table_datacenter); -} - - -function fetch_container() -{ - // do HTTP request and trigger gui update on success - var request_url = API_HOST + "/restapi/compute"; - console.debug("fetching from: " + request_url); - $.getJSON(request_url, update_table_container); -} - - -function fetch_loop() -{ - // only fetch if we are connected - if(!CONNECTED) - return; - - // download data - fetch_datacenter(); - fetch_container(); - - // loop while connected - if(CONNECTED) - setTimeout(fetch_loop, DATA_UPDATE_INTERVAL); -} - - -function connect() -{ - console.info("connect()"); - // get host address - //API_HOST = "http://" + $("#text_api_host").val(); - console.debug("API address: " + API_HOST); - // reset data - LAST_UPDATE_TIMESTAMP_DATACENTER = Date.now(); - LAST_UPDATE_TIMESTAMP_CONTAINER = Date.now(); - CONNECTED = true; - // restart lateness counter - update_lateness_loop(); - // restart data fetch loop - fetch_loop(); -} - - -$(document).ready(function(){ - console.info("document ready"); - // setup global connection error handling - - $.ajaxSetup({ - "error": errorAjaxConnection - }); - - // connect - connect(); - - // additional refresh on window focus - $(window).focus(function () { - if(CONNECTED) - { - fetch_datacenter(); - fetch_container(); - } - }); - -});