From: stevenvanrossem Date: Sat, 8 Apr 2017 11:41:15 +0000 (+0200) Subject: add son-emu dashboard to the python setup file, so it is served from the Flask app... X-Git-Tag: v3.1~33^2~3 X-Git-Url: https://osm.etsi.org/gitweb/?a=commitdiff_plain;h=5b876700bdef500e17c212f5b0bcea5383e44528;p=osm%2Fvim-emu.git add son-emu dashboard to the python setup file, so it is served from the Flask app, at the dummygatekeeper's ip:port --- diff --git a/dashboard/README.md b/dashboard/README.md index db15c9d..70161b7 100755 --- a/dashboard/README.md +++ b/dashboard/README.md @@ -1,4 +1 @@ -# son-emu Dashboard - -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. - +dashboard is moved to src/emuvim/dashboard so it can be part of the python-based installation script diff --git a/dashboard/css/main.css b/dashboard/css/main.css deleted file mode 100755 index 7b10dbc..0000000 --- a/dashboard/css/main.css +++ /dev/null @@ -1,34 +0,0 @@ -body { - margin: 16px; - height: 100%; -} - -#logo { - height: 90px; - text-align: right; -} - -#content { -} - - -.tbl-head { - font-weight: bold; - border-bottom: 1px solid #2c3e50; - background-color: #c9ced3; -} - -.tbl-head > td { - padding-top: 4px; - padding-bottom: 4px; -} - -.tbl-row > td { - padding-top: 4px; - padding-bottom: 4px; - -} - -.spacer { - height: 20px; -} diff --git a/dashboard/dashboard b/dashboard/dashboard new file mode 120000 index 0000000..fa6d186 --- /dev/null +++ b/dashboard/dashboard @@ -0,0 +1 @@ +../src/emuvim/dashboard/ \ No newline at end of file diff --git a/dashboard/img/SONATA_new.png b/dashboard/img/SONATA_new.png deleted file mode 100755 index 8fd99f4..0000000 Binary files a/dashboard/img/SONATA_new.png and /dev/null differ diff --git a/dashboard/index.html b/dashboard/index.html deleted file mode 100755 index 3c31bec..0000000 --- a/dashboard/index.html +++ /dev/null @@ -1,97 +0,0 @@ - - - - - - - - MeDICINE Dashboard - - - - - - - - - - - - - - - - - - - - - -
-
-

MeDICINE Dashboard

- -
- - -
-
- - -
- -
Emulated Datacenters  0Lateness: -
- - -
-
- -
 
- -
- -
Running Containers  0Lateness: -
- - -
-
-
- - - - - - - diff --git a/dashboard/js/main.js b/dashboard/js/main.js deleted file mode 100755 index 71741f2..0000000 --- a/dashboard/js/main.js +++ /dev/null @@ -1,173 +0,0 @@ -var API_HOST = "http://127.0.0.1:5001"; -var ERROR_ALERT = false; -var TIMESTAMP = 0; -var CONNECTED = false; -var LATENESS_UPDATE_INTERVAL = 50; -var DATA_UPDATE_INTERVAL = 1000 * 10; -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"); - lateness_container= (Date.now() - LAST_UPDATE_TIMESTAMP_CONTAINER) / 1000; - $("#lbl_lateness_container").text("Lateness: " + Number(lateness_container).toPrecision(3) + "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("ERROR!\nAPI request failed.\n\n Please check the backend connection.", function() { - // callback - ERROR_ALERT = false; - }); - } -} - - -function update_table_datacenter(data) -{ - console.debug(data) - // clear table - $("#table_datacenter").empty(); - // header - $("#table_datacenter").append('LabelInt. NameSwitchNum. ContainersMetadata Items'); - // 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 += ''; - $("#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(); - // 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); - - // additional refresh on window focus - $(window).focus(function () { - if(CONNECTED) - { - fetch_datacenter(); - fetch_container(); - } - }); - -}); diff --git a/dashboard/son-emu-dashboard-screenshot.png b/dashboard/son-emu-dashboard-screenshot.png deleted file mode 100755 index 8274984..0000000 Binary files a/dashboard/son-emu-dashboard-screenshot.png and /dev/null differ diff --git a/setup.py b/setup.py index ce1c47f..186356c 100755 --- a/setup.py +++ b/setup.py @@ -38,7 +38,8 @@ setup(name='emuvim', packages=find_packages('src'), include_package_data=True, package_data= { - 'emuvim.api.sonata': ['*.yml'] + 'emuvim.api.sonata': ['*.yml'], + 'emuvim.dashboard' : ['*.html', 'css/*.css','img/*','js/*.js'] }, install_requires=[ 'pyaml', diff --git a/src/emuvim/api/rest/rest_api_endpoint.py b/src/emuvim/api/rest/rest_api_endpoint.py index 4767be4..71f87b9 100755 --- a/src/emuvim/api/rest/rest_api_endpoint.py +++ b/src/emuvim/api/rest/rest_api_endpoint.py @@ -25,6 +25,7 @@ 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). """ + import logging import threading from flask import Flask @@ -42,6 +43,9 @@ from network import NetworkAction import monitor from monitor import MonitorInterfaceAction, MonitorFlowAction, MonitorLinkAction, MonitorSkewAction +import pkg_resources +from os import path + logging.basicConfig(level=logging.INFO) @@ -57,7 +61,12 @@ class RestApiEndpoint(object): self.port = port # setup Flask - self.app = Flask(__name__) + # 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.api = Api(self.app) # setup endpoints @@ -96,6 +105,7 @@ class RestApiEndpoint(object): logging.debug("Created API endpoint %s(%s:%d)" % (self.__class__.__name__, self.ip, self.port)) + def connectDatacenter(self, dc): compute.dcs[dc.label] = dc logging.info( diff --git a/src/emuvim/api/sonata/dummygatekeeper.py b/src/emuvim/api/sonata/dummygatekeeper.py index 4e98c6a..76ed3c2 100755 --- a/src/emuvim/api/sonata/dummygatekeeper.py +++ b/src/emuvim/api/sonata/dummygatekeeper.py @@ -1012,10 +1012,6 @@ api.add_resource(Instantiations, '/instantiations', '/api/v2/instantiations', '/ api.add_resource(Exit, '/emulator/exit') -#def initialize_GK(): -# global GK -# GK = Gatekeeper() - def start_rest_api(host, port, datacenters=dict()): GK.dcs = datacenters diff --git a/src/emuvim/dashboard/README.md b/src/emuvim/dashboard/README.md new file mode 100755 index 0000000..db15c9d --- /dev/null +++ b/src/emuvim/dashboard/README.md @@ -0,0 +1,4 @@ +# son-emu Dashboard + +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. + diff --git a/src/emuvim/dashboard/__init__.py b/src/emuvim/dashboard/__init__.py new file mode 100644 index 0000000..45ad698 --- /dev/null +++ b/src/emuvim/dashboard/__init__.py @@ -0,0 +1,27 @@ +""" +Copyright (c) 2017 SONATA-NFV +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 [, ANY ADDITIONAL AFFILIATION] +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). +""" \ No newline at end of file diff --git a/src/emuvim/dashboard/css/main.css b/src/emuvim/dashboard/css/main.css new file mode 100755 index 0000000..7b10dbc --- /dev/null +++ b/src/emuvim/dashboard/css/main.css @@ -0,0 +1,34 @@ +body { + margin: 16px; + height: 100%; +} + +#logo { + height: 90px; + text-align: right; +} + +#content { +} + + +.tbl-head { + font-weight: bold; + border-bottom: 1px solid #2c3e50; + background-color: #c9ced3; +} + +.tbl-head > td { + padding-top: 4px; + padding-bottom: 4px; +} + +.tbl-row > td { + padding-top: 4px; + padding-bottom: 4px; + +} + +.spacer { + height: 20px; +} diff --git a/src/emuvim/dashboard/img/SONATA_new.png b/src/emuvim/dashboard/img/SONATA_new.png new file mode 100755 index 0000000..8fd99f4 Binary files /dev/null and b/src/emuvim/dashboard/img/SONATA_new.png differ diff --git a/src/emuvim/dashboard/index.html b/src/emuvim/dashboard/index.html new file mode 100755 index 0000000..6f77d08 --- /dev/null +++ b/src/emuvim/dashboard/index.html @@ -0,0 +1,97 @@ + + + + + + + + MeDICINE Dashboard + + + + + + + + + + + + + + + + + + + + + +
+
+

MeDICINE Dashboard

+ +
+ + +
+
+ + +
+ +
Emulated Datacenters  0Lateness: -
+ + +
+
+ +
 
+ +
+ +
Running Containers  0Lateness: -
+ + +
+
+
+ +
(c) 2017 by SONATA Consortium and Paderborn University
+ + + + + diff --git a/src/emuvim/dashboard/js/main.js b/src/emuvim/dashboard/js/main.js new file mode 100755 index 0000000..71741f2 --- /dev/null +++ b/src/emuvim/dashboard/js/main.js @@ -0,0 +1,173 @@ +var API_HOST = "http://127.0.0.1:5001"; +var ERROR_ALERT = false; +var TIMESTAMP = 0; +var CONNECTED = false; +var LATENESS_UPDATE_INTERVAL = 50; +var DATA_UPDATE_INTERVAL = 1000 * 10; +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"); + lateness_container= (Date.now() - LAST_UPDATE_TIMESTAMP_CONTAINER) / 1000; + $("#lbl_lateness_container").text("Lateness: " + Number(lateness_container).toPrecision(3) + "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("ERROR!\nAPI request failed.\n\n Please check the backend connection.", function() { + // callback + ERROR_ALERT = false; + }); + } +} + + +function update_table_datacenter(data) +{ + console.debug(data) + // clear table + $("#table_datacenter").empty(); + // header + $("#table_datacenter").append('LabelInt. NameSwitchNum. ContainersMetadata Items'); + // 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 += ''; + $("#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(); + // 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); + + // additional refresh on window focus + $(window).focus(function () { + if(CONNECTED) + { + fetch_datacenter(); + fetch_container(); + } + }); + +}); diff --git a/src/emuvim/dashboard/son-emu-dashboard-screenshot.png b/src/emuvim/dashboard/son-emu-dashboard-screenshot.png new file mode 100755 index 0000000..8274984 Binary files /dev/null and b/src/emuvim/dashboard/son-emu-dashboard-screenshot.png differ