├── .gitignore ├── Dockerfile ├── LICENSE ├── README.md ├── app.js ├── config.json ├── lib ├── client-manager.js ├── extend.js ├── lock.js └── utils.js ├── open_vmonitor.njsproj ├── package.json ├── public ├── favicon.ico ├── fonts │ ├── fontawesome-webfont.eot │ ├── fontawesome-webfont.svg │ ├── fontawesome-webfont.ttf │ ├── fontawesome-webfont.woff │ ├── glyphicons-halflings-regular.eot │ ├── glyphicons-halflings-regular.svg │ ├── glyphicons-halflings-regular.ttf │ └── glyphicons-halflings-regular.woff ├── images │ ├── bg_login.jpg │ ├── icons_16x16px.png │ ├── loading.gif │ ├── spinner.gif │ └── ul.png ├── js │ ├── assets.js │ ├── bootstrap-select.min.js │ ├── bootstrap.min.js │ ├── d3.tip.v0.6.3.js │ ├── d3.v3.min.js │ ├── event_emitter.js │ ├── jquery-1.11.1.min.js │ ├── loading.js │ ├── navigation.js │ ├── plugins │ │ ├── jquery.cookie.js │ │ ├── metisMenu.min.js │ │ └── scroller.js │ ├── sb-admin-2.js │ └── sweet-alert.min.js └── stylesheets │ ├── bootstrap-select.min.css │ ├── bootstrap.min.css │ ├── default.css │ ├── font-awesome.min.css │ ├── plugins │ ├── metisMenu.min.css │ └── scroller.css │ ├── sb-admin-2.css │ ├── style.css │ └── sweet-alert.css ├── routes ├── bridge_stats_route.js ├── custom_data_loaders.js ├── index.js ├── table_routes.js └── table_routes_info.js └── views ├── 404.jade ├── 500.jade ├── alerts.jade ├── custom ├── object_bridge.jade ├── table_bridge.jade ├── table_controller.jade ├── table_interface.jade ├── table_manager.jade ├── table_openvswitch.jade ├── table_port.jade └── table_ssl.jade ├── dashboard.jade ├── enter.jade ├── error.jade ├── general_table.jade ├── htable.jade ├── include ├── htable_inc.jade ├── mixins.jade └── vtable_inc.jade ├── index.jade ├── layout.jade ├── raw_json.jade └── vtable.jade /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # nyc test coverage 18 | .nyc_output 19 | 20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 21 | .grunt 22 | 23 | # node-waf configuration 24 | .lock-wscript 25 | 26 | # Compiled binary addons (http://nodejs.org/api/addons.html) 27 | build/Release 28 | 29 | # Dependency directories 30 | node_modules 31 | jspm_packages 32 | 33 | # Optional npm cache directory 34 | .npm 35 | 36 | # Optional REPL history 37 | .node_repl_history 38 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:6.9.1-alpine 2 | COPY . /opt/open_vmonitor 3 | RUN cd /opt/open_vmonitor && npm install 4 | EXPOSE 3000 5 | WORKDIR /opt/open_vmonitor 6 | CMD [ "npm", "start" ] 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![PLVision Logo](https://plvision.eu/wp-content/themes/THEME/img/logo.svg)](http://plvision.eu/) 2 | 3 | Fast and minimalist OVSDB UI for [node](http://nodejs.org) - https://plvision.eu/open-vmonitor/ 4 | 5 | ![Open_vMonitor screenshot](https://plvision.eu/wp-content/uploads/2017/04/Artboard.png) 6 | 7 | ## Prerequirements 8 | Open_vMonitor requires an opened OVS database port. In most cases it is enough to perform this action on a server side. This will open OVSDB port in passive mode on port 6640 (OVSDB default). 9 | ```bash 10 | $ sudo ovs-vsctl set-manager ptcp:6640 11 | ``` 12 | 13 | ## Installation from Github 14 | ```bash 15 | $ git clone https://github.com/PLVision/open_vmonitor.git 16 | $ cd open_vmonitor 17 | $ npm install 18 | ``` 19 | 20 | ## Features 21 | * HTTP/HTTPS web UI support 22 | * SSL connection to an OVS database 23 | * lightweight and fast UI 24 | 25 | ## HTTPs support 26 | ```bash 27 | $ mkdir -p ./certs 28 | $ openssl genrsa -out ./certs/key.pem 1024 29 | $ openssl req -new -key ./certs/key.pem -out ./certs/certrequest.csr 30 | $ openssl x509 -req -in ./certs/certrequest.csr -signkey ./certs/key.pem -out ./certs/certificate.pem 31 | ``` 32 | And start add env variable 33 | ```bash 34 | $ env MODE=https PORT=3001 npm start 35 | ``` 36 | 37 | ## Docker container 38 | ```bash 39 | $ docker pull plvisiondevs/open_vmonitor 40 | $ docker run -d -p 3000:3000 plvisiondevs/open_vmonitor 41 | ``` 42 | 43 | ## Start application 44 | ```bash 45 | $ npm start 46 | ``` 47 | 48 | ## Default credentials 49 | Initial user/password are admin/admin 50 | 51 | ## People 52 | Copyright (c) 2014-2016 PLVision 53 | Authors of ovsdb-client are Ihor Chumak and [Roman Gotsiy](https://github.com/romangotsiy) (developers@plvision.eu). 54 | Maintainer: Ihor Chumak (developers@plvision.eu) 55 | 56 | ## License 57 | [AGPL-3.0](LICENSE) 58 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Open vMonitor is a WEB-based tool for monitoring and troubleshooting Open vSwitch 3 | * Copyright (C) 2014-2016 PLVision 4 | * Ihor Chumak, Roman Gotsiy 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | 16 | * You should have received a copy of the GNU Affero General Public License 17 | * along with this program. If not, see . 18 | 19 | * PLVision, developers@plvision.eu 20 | */ 21 | 22 | Array.prototype.clear = function () { 23 | this.length = 0; 24 | }; 25 | 26 | Array.prototype.includes = function (object) { 27 | return this.indexOf(object) != -1; 28 | }; 29 | 30 | String.prototype.startsWith = function (object) { 31 | return this.indexOf(object) == 0; 32 | } 33 | 34 | var express = require('express'); 35 | var http = require('http'); 36 | var https = require('https'); 37 | var fs = require('fs'); 38 | var errorhandler = require('errorhandler'); 39 | var cookie_session = require('cookie-session'); 40 | var body_parser = require('body-parser'); 41 | var debug = require('debug')('app'); 42 | var extend = require("./lib/extend.js"); 43 | var uuid = require("uuid"); 44 | var url = require('url'); 45 | 46 | var ovsdb = require('ovsdb-client'); 47 | 48 | var app = express(); 49 | var client_manager = require("./lib/client-manager.js")(app); 50 | 51 | var utils = require("./lib/utils.js"); 52 | var routes = require("./routes"); 53 | 54 | app.listen = function () { 55 | var options = undefined; 56 | var server; 57 | 58 | switch (app.get("mode")) { 59 | case 'https': 60 | options = { 61 | key: fs.readFileSync(app.get("key")), 62 | cert: fs.readFileSync(app.get("certificate")) 63 | }; 64 | case 'http': 65 | default: 66 | server = options ? https.createServer(options, this) : http.createServer(this); 67 | break; 68 | } 69 | 70 | // Check if we are running as root 71 | if (process.getgid && process.setgid && process.setuid) { 72 | if (process.getgid() === 0) { 73 | // TODO: uncomment on release 74 | // process.setgid('ovm'); 75 | // process.setuid('ovm'); 76 | } 77 | } 78 | 79 | return server.listen.apply(server, arguments); 80 | }; 81 | 82 | app.set('config', {}) 83 | 84 | app.load_config = function (callback) { 85 | var config; 86 | var config_file = __dirname + "/config.json"; 87 | 88 | app.set('configErr', undefined) 89 | fs.exists(config_file, function (exists) { 90 | if (exists) { 91 | fs.readFile(config_file, function (err, data) { 92 | if (!err) { 93 | try { 94 | config = JSON.parse(data); 95 | app.set("port", config.server.port); 96 | app.set("mode", config.server.mode); 97 | app.set("key", config.server.key); 98 | app.set("certificate", config.server.certificate); 99 | } catch (err) { 100 | console.error(err); 101 | err.code = 500; 102 | err.message = "[config.json]: " + err + ".\nPlease check the configuration file and restart an application"; 103 | app.set('configErr', err) 104 | } 105 | } 106 | app.set('config', config || {}); 107 | callback(err); 108 | }); 109 | } else { 110 | callback("Configuration file does not exist"); 111 | } 112 | }); 113 | } 114 | 115 | app.use(errorhandler()); 116 | 117 | app.use(function (req, res, next) { 118 | if (app.get("configErr") !== undefined) { 119 | res.render("error", { 120 | message: "Config file syntax error", 121 | error: app.get("configErr"), 122 | showStack: false 123 | }) 124 | } else { 125 | next(); 126 | } 127 | }); 128 | 129 | app.use(cookie_session({ 130 | genid: function (req) { 131 | return genuuid(); // use UUIDs for session IDs 132 | }, 133 | secret: 'keyboard pretty cat', 134 | cookie: { 135 | secure: false 136 | } 137 | })); 138 | 139 | app.use(express.static(__dirname + '/public')); 140 | 141 | // parse application/x-www-form-urlencoded 142 | app.use(body_parser.urlencoded({ 143 | extended: false 144 | })); 145 | 146 | app.use(function (req, res, next) { 147 | debug('app.use: original url', req.originalUrl); 148 | res.locals.isAjax = req.headers['x-requested-with'] && req.headers['x-requested-with'] === 'XMLHttpRequest'; 149 | next(); 150 | }); 151 | 152 | app.use(function (req, res, next) { 153 | if (req.originalUrl.startsWith("/enter")) { 154 | // need to look at this 'return' later 155 | next(); 156 | return; 157 | } 158 | if (!req.session.connection) { 159 | res.redirect('/enter'); 160 | } else { 161 | next(); 162 | } 163 | }); 164 | 165 | app.use(function (req, res, next) { 166 | res.renderView = function (viewName, opts) { 167 | var defaults = { 168 | title: app.get('title'), 169 | connection: req.session.connection, 170 | }; 171 | 172 | // if loaded not over ajax but using GET, table view need info for constructing menu 173 | if (!res.locals.isAjax) { 174 | var addr = req.session.connection; 175 | 176 | var userid = req.session.userid; 177 | client_manager.client(userid, addr, function (err, client) { 178 | if (err) 179 | throw err; 180 | var db_name = req.session.db_name; 181 | if (!db_name) { 182 | db_name = client.databases()[0]; 183 | } 184 | client.database(db_name).bridges().list(["name"], function (err, bridges) { 185 | if (err) 186 | throw err; 187 | 188 | var extra_opts = { 189 | databases: client.databases(), 190 | selected_db: db_name, 191 | bridges: bridges, 192 | }; 193 | if (client.authorized === true) { 194 | extra_opts.authorized = true; 195 | } else if (client.authorized === false) { 196 | extra_opts.authorized = false; 197 | extra_opts.auth_error = client.auth_error; 198 | } 199 | // if client.authorized is not specified then we have simple (nonsecure) connection 200 | 201 | opts = extend(defaults, opts, extra_opts); 202 | res.render(viewName, opts); 203 | }); 204 | }); 205 | } else { 206 | opts = extend(defaults, opts); 207 | res.render(viewName, opts); 208 | } 209 | }; 210 | next(); 211 | }); 212 | 213 | app.set('views', __dirname + '/views'); 214 | app.set('view engine', 'jade'); 215 | app.set('title', "Open_vMonitor"); 216 | app.set('default_connection_address', '127.0.0.1'); 217 | app.set('username', 'admin'); 218 | 219 | // app.all('/', function(req, res, next) { 220 | // debug('/: body: ' + JSON.stringify(req.body)); 221 | // next(); 222 | // }); 223 | 224 | app.all('/enter', function (req, res, next) { 225 | // get all possible arguments 226 | var ip = req.query.database_ip_address || req.body.database_ip_address; 227 | var port; 228 | var secure = req.body.secure == 'true' || req.query.secure == 'true'; 229 | 230 | // authenticate user 231 | var username = req.query.username || req.body.username; 232 | var password = req.query.password || req.body.password; 233 | var config = app.get('config'); 234 | 235 | // retrieve a port number 236 | if (ip && ip.indexOf(":") != -1) { 237 | var parts = ip.split(":"); 238 | ip = parts[0]; 239 | port = parts[1]; 240 | } 241 | port = req.query.port || port || 6640; 242 | 243 | // if we have all necessary info, proceed to required page 244 | var redirect = true; 245 | if (username && password && ip && port) { 246 | redirect = false; 247 | } 248 | 249 | if (req.method == 'GET' && redirect) { 250 | res.render('enter', { 251 | title: app.get('title'), 252 | username: app.get('username'), 253 | connection: app.get('default_connection_address') 254 | }); 255 | } else if (req.method == 'POST' || (req.method == 'GET' && !redirect)) { 256 | if ((config.credentials.username != username) || (config.credentials.password != password)) { 257 | debug("user/password do not match") 258 | res.status(403).end(); 259 | } else { 260 | var conn_id = (secure ? "ssl:" : "") + ip + ":" + port; 261 | 262 | debug("Add " + conn_id + " to connections"); 263 | req.session.connection = conn_id; 264 | req.session.userid = uuid.v4(); 265 | 266 | var userid = req.session.userid; 267 | client_manager.client(userid, ip, port, secure, function (err) { 268 | if (!err) { 269 | // if client specifies a page through a POST request, redirect 270 | if (req.query.page) { 271 | var item = req.query.item || ''; 272 | debug('redirect to selected page'); 273 | res.redirect(req.query.page + '/' + item); 274 | } else { 275 | debug('redirect to /'); 276 | res.redirect('/'); 277 | } 278 | } else { 279 | debug('error: ', err); 280 | next(err); 281 | } 282 | }); 283 | } 284 | } 285 | }); 286 | 287 | //app.post('/enter', function(req, res, next) { 288 | // 289 | //}); 290 | 291 | app.get('/alerts/:action?', function (req, res, next) { 292 | var db_name = req.session.db_name; 293 | var addr = req.session.connection; 294 | var count = -1; 295 | var action = req.params.action || 'show'; 296 | debug(action); 297 | 298 | var userid = req.session.userid; 299 | client_manager.client(userid, addr, function (err, client) { 300 | switch (action) { 301 | case 'clear': 302 | client.clear_updates(function () { 303 | res.renderView('alerts', { 304 | title: 'Alerts', 305 | table: 'Alerts', // FIXME: index template should be modified 306 | updates: [] 307 | }); 308 | }); 309 | break; 310 | case 'show': 311 | client.retrieve_updates(count, function (updates) { 312 | res.renderView('alerts', { 313 | title: 'Alerts', 314 | table: 'Alerts', // FIXME: index template should be modified 315 | updates: updates 316 | }); 317 | }); 318 | break; 319 | default: 320 | break; 321 | } 322 | }); 323 | }); 324 | 325 | app.get('/updates', function (req, res, next) { 326 | var db_name = req.session.db_name; 327 | var addr = req.session.connection; 328 | var count = -1; 329 | 330 | var userid = req.session.userid; 331 | client_manager.client(userid, addr, function (err, client) { 332 | if (err) { 333 | return next(err); 334 | } 335 | client.retrieve_updates(count, function (updates) { 336 | res.send(updates); 337 | }); 338 | }); 339 | }); 340 | 341 | app.get('/logout', function (req, res, next) { 342 | client_manager.closeAll(); 343 | req.session.connection = null; 344 | res.redirect('/enter'); 345 | }); 346 | 347 | app.post('/select-db/:db_name', function (req, res, next) { 348 | var db_name = req.params.db_name; 349 | req.session.db_name = db_name; 350 | res.status(200).end(); 351 | }); 352 | 353 | app.get('/', function (req, res, next) { 354 | res.redirect('/Open_vSwitch'); 355 | }); 356 | 357 | app.get('/raw_json', function (req, res, next) { 358 | res.renderView('raw_json'); 359 | }); 360 | 361 | app.post('/:db_name/raw_json', function (req, res, next) { 362 | var db_name = req.session.db_name; 363 | var addr = req.session.connection; 364 | 365 | debug(req.body); 366 | var request = JSON.parse(req.body.request); 367 | 368 | var userid = req.session.userid; 369 | client_manager.client(userid, addr, function (err, client) { 370 | client.raw_request(request, function (result) { 371 | res.status(200).send(JSON.stringify(result)); 372 | }); 373 | }); 374 | }); 375 | 376 | app.get('/_switch_view', function (req, res, next) { 377 | var db_name = req.session.db_name; 378 | var addr = req.session.connection; 379 | 380 | var userid = req.session.userid; 381 | client_manager.client(userid, addr, function (err, client) { 382 | var brs = []; 383 | if (err) { 384 | return next(err); 385 | } 386 | client.database(db_name).bridges().each(function (bridge, next_bridge) { 387 | var ports = []; 388 | 389 | bridge.data(function (err, data) { 390 | var name = data.name; 391 | debug(err, data, name); 392 | 393 | bridge.ports().each(function (port, next_port) { 394 | port.data(function (err, data) { 395 | var name = data.name; 396 | debug(err, data, name); 397 | 398 | port.interfaces().data(function (err, interfaces) { 399 | ports.push({ 400 | name: name, 401 | interfaces: interfaces 402 | }); 403 | next_port(); 404 | }); 405 | }); 406 | }, function () { 407 | // done for ports 408 | brs.push({ 409 | name: name, 410 | ports: ports 411 | }); 412 | next_bridge(); 413 | }); 414 | }); 415 | }, function (err) { 416 | // done for bridges 417 | debug(brs.length); 418 | res.send(JSON.stringify(brs)); 419 | }); 420 | }); 421 | }); 422 | 423 | app.get('/:table', routes.table_route); 424 | 425 | app.get('/:table/:row_name', routes.table_object_route); 426 | 427 | app.get('/Bridge/:bridge_name/port-stats', routes.stats_route); 428 | app.get('/Port/:port_name/port-stats', routes.stats_route); 429 | 430 | app.get('/:parent_table/:parent_obj_name/:table', routes.table_object_subtable_route); 431 | 432 | // catch 404 and forwarding to error handler 433 | app.use(function (m_err, req, res, next) { 434 | if (m_err) { 435 | return next(m_err); 436 | } 437 | var err = new Error('Not Found'); 438 | err.status = 404; 439 | next(err); 440 | }); 441 | 442 | 443 | app.use(function (err, req, res, next) { 444 | if (err.status === 404) { 445 | res.status(404).renderView("404", { route: err.table }); 446 | } else if (err.code === 495) { //ssl error 447 | res.status(495).send(err) 448 | } else { 449 | res.status(500).render("500"); 450 | } 451 | //console.log(err); 452 | }); 453 | 454 | //module.exports = app; 455 | 456 | // we need to generate certificates first 457 | // mkdir -p ./certs 458 | // openssl genrsa -out ./certs/key.pem 1024 459 | // openssl req -new -key ./certs/key.pem -out ./certs/certrequest.csr # lots of questions here 460 | // openssl x509 -req -in ./certs/certrequest.csr -signkey ./certs/key.pem -out ./certs/certificate.pem 461 | 462 | var app_name = 'Open_vMonitor'; 463 | 464 | //var app = require('../app'); 465 | //var debug = require('debug')(app_name); 466 | 467 | app.set('port', process.env.PORT || 3000); 468 | app.set('mode', process.env.MODE || 'http'); 469 | app.set('key', process.env.KEY || './cert/key.pem'); 470 | app.set('certificate', process.env.CERTIFICATE || './cert/certificate.pem'); 471 | 472 | app.load_config(function (err) { 473 | if (err) { 474 | console.error("Can't load configuration file 'config.json': ", err); 475 | console.error("Try to continue without it..."); 476 | } 477 | debug("listening port: " + app.get("port")); 478 | var server = app.listen(app.get("port"), function () { 479 | var log_string = app_name.concat(' ', app.get("mode"), ' server listening on port ', server.address().port); 480 | debug(log_string); 481 | }); 482 | }); 483 | 484 | 485 | 486 | -------------------------------------------------------------------------------- /config.json: -------------------------------------------------------------------------------- 1 | { 2 | "secure_connection": { 3 | "private_key": "/var/ovm/vtep-privkey.pem", 4 | "certificate": "/var/ovm/vtep-cert.pem", 5 | "ca_certificate": "" 6 | }, 7 | "credentials": { 8 | "username" : "admin", 9 | "password" : "admin" 10 | }, 11 | "server" : { 12 | "mode" : "http", 13 | "port" : 3000, 14 | "key" : "", 15 | "certificate" : "" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /lib/client-manager.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Open vMonitor is a WEB-based tool for monitoring and troubleshooting Open vSwitch 3 | * Copyright (C) 2014 PLVision 4 | * Ihor Chumak, Roman Gotsiy 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | 16 | * You should have received a copy of the GNU Affero General Public License 17 | * along with this program. If not, see . 18 | 19 | * PLVision, developers@plvision.eu 20 | */ 21 | 22 | var ovsdb = require('ovsdb-client'); 23 | var debug = require('debug')('client-manager'); 24 | var assert = require("assert"); 25 | var async = require("async"); 26 | var fs = require("fs"); 27 | 28 | var lock = require("./lock.js") 29 | 30 | var _instance; 31 | 32 | function getInstance(app) { 33 | if (!_instance) { 34 | debug("Creating instance of ClientManager"); 35 | _instance = new ClientManager(app); 36 | return _instance; 37 | } 38 | return _instance; 39 | } 40 | 41 | function ClientManager(app) { 42 | this.app = app; 43 | this.init(); 44 | } 45 | 46 | ClientManager.prototype.init = function () { 47 | this.clients = {}; 48 | }; 49 | 50 | ClientManager.prototype.client = function (/* ip [, port, secure], callback */) { 51 | var self = this; 52 | var userid = arguments[0]; 53 | var ip = arguments[1]; 54 | var port, callback, secure; 55 | 56 | if (ip.indexOf(":") != -1) { 57 | var parts = ip.split(":"); 58 | if (parts[0] === "ssl") { 59 | secure = true; 60 | ip = parts[1]; 61 | port = parts[2]; 62 | } else { 63 | secure = false; 64 | ip = parts[0]; 65 | port = parts[1]; 66 | } 67 | callback = arguments[2]; 68 | } else { 69 | port = arguments[2]; 70 | secure = arguments[3]; 71 | if (secure instanceof Function) { 72 | callback = secure; 73 | secure = false; 74 | } else { 75 | callback = arguments[4]; 76 | } 77 | } 78 | 79 | assert(!(port instanceof Function), "Expected port but function given"); 80 | 81 | assert((callback === undefined || (callback instanceof Function)), 82 | "Expected callback, but third parameter is not a function"); 83 | 84 | client_id = userid + "@" + (secure ? "ssl:" : "") + ip + ":" + port; 85 | debug("Requesting client " + client_id); 86 | 87 | var connect_client = function (client_id, attempts) { 88 | client = self.clients[client_id]; 89 | if (lock.get(callback)) { 90 | debug("locked"); 91 | client.connect(function (err, remote) { 92 | if (err) { 93 | delete self.clients[client_id]; 94 | if (callback) { 95 | callback(err, null); 96 | } 97 | lock.free(this, [err, null]) 98 | } else { 99 | if (callback) { 100 | callback(null, client); 101 | } 102 | lock.free(this, [null, client]) 103 | } 104 | }); 105 | } else { 106 | debug("wait for free"); 107 | } 108 | }; 109 | 110 | if (!self.clients[client_id]) { 111 | debug("Client doesn't exist: creating new client"); 112 | var new_client 113 | if (secure) { 114 | self.loadCertificates(function (err, certs) { 115 | if (err) return callback(err); 116 | new_client = ovsdb.createClient(port, ip, certs); 117 | self.clients[client_id] = new_client; 118 | connect_client(client_id); 119 | }); 120 | } 121 | else { 122 | new_client = ovsdb.createClient(port, ip); 123 | self.clients[client_id] = new_client; 124 | connect_client(client_id); 125 | } 126 | } else { 127 | debug("Client exist"); 128 | var client = self.clients[client_id]; 129 | if (!client.connected()) { 130 | debug("Client is not connected: reconnecting"); 131 | connect_client(client_id); 132 | } else { 133 | if (callback) { 134 | callback(null, self.clients[client_id]); 135 | } 136 | } 137 | } 138 | }; 139 | 140 | ClientManager.prototype.closeAll = function () { 141 | var self = this; 142 | 143 | for (var client_id in self.clients) { 144 | var client = self.clients[client_id].close(); 145 | delete self.clients[client_id]; 146 | } 147 | }; 148 | 149 | ClientManager.prototype.loadCertificates = function (callback) { 150 | var self = this; 151 | 152 | var certs = {}; 153 | 154 | var e = new Error(); 155 | e.code = 495; //cert error 156 | var cfg = self.app.get('config').secure_connection; 157 | if (!cfg) { 158 | e.message = "Secure connection options are not present in config file" 159 | return callback(e); 160 | } 161 | 162 | var options = ["private_key", "certificate", "ca_certificate"]; 163 | var optional = { "ca_certificate": true } 164 | 165 | async.each(options, function (opt, next) { 166 | var cert_file = cfg[opt]; 167 | if (!cert_file) { 168 | if (!optional[opt]) { 169 | e.message = "Option " + opt + " is not specified in config file"; 170 | return next(e); 171 | } else { 172 | return next(); 173 | } 174 | } 175 | 176 | fs.readFile(cert_file, function (err, data) { 177 | if (!err) { 178 | certs[opt] = data; 179 | next(); 180 | } else { 181 | e.message = "Error during reading " + opt + " (" + cert_file + ")"; 182 | return next(e); 183 | } 184 | }); 185 | }, function (err) { 186 | callback(err, certs); 187 | }); 188 | } 189 | 190 | module.exports = getInstance; 191 | -------------------------------------------------------------------------------- /lib/extend.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Open vMonitor is a WEB-based tool for monitoring and troubleshooting Open vSwitch 3 | * Copyright (C) 2015 PLVision 4 | * Ihor Chumak 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | 16 | * You should have received a copy of the GNU Affero General Public License 17 | * along with this program. If not, see . 18 | 19 | * PLVision, developers@plvision.eu 20 | */ 21 | 22 | function ExtendException(message) { 23 | this.message = message; 24 | this.name = 'ExtendException'; 25 | } 26 | 27 | module.exports = function extend() { 28 | var target = arguments[0]; 29 | var length = arguments.length; 30 | 31 | if (length > 1) { 32 | switch (typeof target) { 33 | case 'function': 34 | case 'boolean': 35 | case 'number': 36 | case 'string': 37 | case 'symbol': 38 | throw 'Incompatible type. Must be \'object\', but ' + typeof target + ' found.'; 39 | break; 40 | case 'undefined': 41 | target = {}; 42 | default: 43 | // do nothing 44 | break; 45 | } 46 | 47 | // need to extend an array 48 | for (var i = 1; i <= length; i++) { 49 | var element = arguments[i]; 50 | for (name in element) { 51 | var tmp = element[name]; 52 | if (!(tmp in target)) { 53 | target[name] = tmp; 54 | } 55 | } 56 | } 57 | } 58 | return target; 59 | } 60 | -------------------------------------------------------------------------------- /lib/lock.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Open vMonitor is a WEB-based tool for monitoring and troubleshooting Open vSwitch 3 | * Copyright (C) 2015 PLVision 4 | * Ihor Chumak 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | 16 | * You should have received a copy of the GNU Affero General Public License 17 | * along with this program. If not, see . 18 | 19 | * PLVision, developers@plvision.eu 20 | */ 21 | var locked = false; 22 | var requesters = []; 23 | 24 | var getLock = function (callback) { 25 | if (locked) { 26 | requesters.push(callback); 27 | return false; 28 | } 29 | else { 30 | locked = true; 31 | return true; 32 | } 33 | }; 34 | exports.get = getLock; 35 | 36 | var freeLock = function (arg, params) { 37 | locked = false; 38 | 39 | for (var r = 0; r < requesters.length; r++) { 40 | if (requesters[r]) { 41 | requesters[r].apply(arg, params); 42 | delete requesters[r]; 43 | } 44 | } 45 | }; 46 | exports.free = freeLock; 47 | -------------------------------------------------------------------------------- /lib/utils.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Open vMonitor is a WEB-based tool for monitoring and troubleshooting Open vSwitch 3 | * Copyright (C) 2014 PLVision 4 | * Ihor Chumak, Roman Gotsiy 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | 16 | * You should have received a copy of the GNU Affero General Public License 17 | * along with this program. If not, see . 18 | 19 | * PLVision, developers@plvision.eu 20 | */ 21 | 22 | var ip2url = function (url) { 23 | //TODO 24 | }; 25 | 26 | var url2ip = function (url) { 27 | return url.replace('__', ':').split('_').join('.'); 28 | }; 29 | 30 | var prepare_breadcrumbs = function (url) { 31 | var parts = url.split("/"); 32 | 33 | var breadcrumbs = []; 34 | var current_url = ""; 35 | var first = true; 36 | for (var i = 0; i < parts.length; i++) { 37 | if (parts[i] === "") continue; 38 | current_url += ("/" + parts[i]); 39 | if (first) { 40 | parts[i] = url2ip(parts[i]); 41 | first = false; 42 | } 43 | var crumb = {}; 44 | crumb.title = parts[i]; 45 | crumb.url = current_url; 46 | breadcrumbs.push(crumb); 47 | } 48 | return breadcrumbs; 49 | }; 50 | 51 | var map_array2map = function (map_array) { 52 | res = {}; 53 | if (map_array && map_array[0] === "map") { 54 | var map_array_elems = map_array[1]; 55 | for (var i = 0; i < map_array_elems.length; i++) { 56 | res[map_array_elems[i][0]] = map_array_elems[i][1]; 57 | } 58 | } 59 | return res; 60 | }; 61 | 62 | module.exports = { 63 | prepare_breadcrumbs: prepare_breadcrumbs, 64 | url2ip: url2ip, 65 | ip2url: ip2url, 66 | map_array2map: map_array2map 67 | }; 68 | -------------------------------------------------------------------------------- /open_vmonitor.njsproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 14.0 5 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 6 | open_vmonitor 7 | open_vmonitor 8 | 9 | 10 | 11 | Debug 12 | 2.0 13 | 2a2313e1-1359-4417-82d2-2e09b74da01d 14 | . 15 | app.js 16 | 17 | 18 | . 19 | . 20 | v4.0 21 | {3AF33F2E-1136-4D97-BBB7-1795711AC8B8};{349c5851-65df-11da-9384-00065b846f21};{9092AA53-FB77-4645-B42D-1CCCA6BD08BD} 22 | ShowAllFiles 23 | 3000 24 | True 25 | 26 | 27 | true 28 | 29 | 30 | true 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | False 116 | True 117 | 0 118 | / 119 | http://localhost:48022/ 120 | False 121 | True 122 | http://localhost:1337 123 | False 124 | 125 | 126 | 127 | 128 | 129 | 130 | CurrentPage 131 | True 132 | False 133 | False 134 | False 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | False 144 | False 145 | 146 | 147 | 148 | 149 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": { 3 | "name": "Ihor Chumak", 4 | "email": "developers@plvision.eu" 5 | }, 6 | "dependencies": { 7 | "async": "0.9.0", 8 | "body-parser": "1.6.7", 9 | "cookie-session": "1.0.2", 10 | "errorhandler": "1.2.0", 11 | "express": "4.8.7", 12 | "jade": "1.7.0", 13 | "ovsdb-client": "1.0.1", 14 | "uuid": "^3.0.0" 15 | }, 16 | "devDependencies": { 17 | "debug": "1.0.4" 18 | }, 19 | "license": "AGPL-3.0", 20 | "name": "Open_vMonitor", 21 | "private": false, 22 | "repository": { 23 | "type": "git", 24 | "url": "https://github.com/PLVision/open_vmonitor.git" 25 | }, 26 | "scripts": { 27 | "start": "node app.js", 28 | "package": "jx package app.js && rm app.jxp", 29 | "test": "echo \"Error: no test specified\" && exit 1" 30 | }, 31 | "version": "1.0.1" 32 | } 33 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PLVision/open_vmonitor/6655a65add0fd78911d5e227749dec341799c24e/public/favicon.ico -------------------------------------------------------------------------------- /public/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PLVision/open_vmonitor/6655a65add0fd78911d5e227749dec341799c24e/public/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /public/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PLVision/open_vmonitor/6655a65add0fd78911d5e227749dec341799c24e/public/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /public/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PLVision/open_vmonitor/6655a65add0fd78911d5e227749dec341799c24e/public/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /public/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PLVision/open_vmonitor/6655a65add0fd78911d5e227749dec341799c24e/public/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /public/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PLVision/open_vmonitor/6655a65add0fd78911d5e227749dec341799c24e/public/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /public/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PLVision/open_vmonitor/6655a65add0fd78911d5e227749dec341799c24e/public/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /public/images/bg_login.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PLVision/open_vmonitor/6655a65add0fd78911d5e227749dec341799c24e/public/images/bg_login.jpg -------------------------------------------------------------------------------- /public/images/icons_16x16px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PLVision/open_vmonitor/6655a65add0fd78911d5e227749dec341799c24e/public/images/icons_16x16px.png -------------------------------------------------------------------------------- /public/images/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PLVision/open_vmonitor/6655a65add0fd78911d5e227749dec341799c24e/public/images/loading.gif -------------------------------------------------------------------------------- /public/images/spinner.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PLVision/open_vmonitor/6655a65add0fd78911d5e227749dec341799c24e/public/images/spinner.gif -------------------------------------------------------------------------------- /public/images/ul.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PLVision/open_vmonitor/6655a65add0fd78911d5e227749dec341799c24e/public/images/ul.png -------------------------------------------------------------------------------- /public/js/assets.js: -------------------------------------------------------------------------------- 1 | function display_port_stats(table_container_selector, bridge_name) { 2 | 3 | var header_cell_template = ' {} '; 4 | var url = "/Bridge/" + bridge_name + "/port-stats" 5 | if (!bridge_name) 6 | url = (location.pathname.substr(-1) === "/") ? "../port-stats" : "port-stats"; 7 | $.getJSON(url, {/* no data */}, function(ports_stats) { 8 | var $container = $(table_container_selector); 9 | /* table header */ 10 | 11 | var port_names = Object.keys(ports_stats); 12 | var $header_row = $(""); 13 | $(header_cell_template.replace("{}", "")).appendTo($header_row); 14 | for (var i = 0; i < port_names.length; i++) { 15 | $(header_cell_template.replace("{}", port_names[i])).appendTo($header_row); 16 | } 17 | 18 | $("").append($header_row).appendTo($container); 19 | 20 | var stat_fields = {}; 21 | for ( var port_name in ports_stats) { 22 | $.extend(stat_fields, ports_stats[port_name]); 23 | } 24 | 25 | stat_fields = Object.keys(stat_fields); 26 | 27 | for (i = 0; i < stat_fields.length; i++) { 28 | var field = stat_fields[i]; 29 | var $row = $(""); 30 | $stat_name_cell = $("").text(field); 31 | $row.append($stat_name_cell); 32 | for (port_name in ports_stats) { 33 | stats = ports_stats[port_name]; 34 | var $stat_cell = $("").text(stats[field]); 35 | $row.append($stat_cell); 36 | $container.append($row); 37 | } 38 | } 39 | init_custom_scroller($("#port_stats").parent(), true); 40 | }); 41 | } 42 | 43 | function init_custom_scroller(selector, horizontal) { 44 | $(selector).scroller('reset').scroller({ 45 | horizontal : horizontal 46 | }); 47 | } 48 | 49 | update_custom_scroller = init_custom_scroller; 50 | 51 | function draw_switch_view(selector) { 52 | 53 | var position = 0; 54 | var led = { 55 | width : 30, 56 | height : 20 57 | }; 58 | 59 | // clear tips if any 60 | $(".d3-tip").remove(); 61 | 62 | var tip = d3.tip().attr('class', 'd3-tip').offset([ -10, 0 ]).html( 63 | function(intf) { 64 | var tip_html = "Interface: " + intf.name + "
Port: " 65 | + intf.port + ""; 66 | 67 | 68 | var statistics = (intf.statistics && intf.statistics[0] == "map") ? intf.statistics[1] : []; 69 | var is_err = statistics.filter(function(element) { 70 | return (((element[0].indexOf('err') != -1) || (element[0].indexOf('drop') != -1)) && (element[1] > 0)); 71 | }); 72 | 73 | if (is_err.length) 74 | tip_html += "
Error detected on this interface:
see statistics for details
" 75 | return tip_html 76 | }); 77 | 78 | var redirect = function(intf, index) { 79 | // TODO: change a redirect principle 80 | window.location.replace('/Interface/' + intf.name); 81 | }; 82 | 83 | $.getJSON('/_switch_view', {/* no data */}, function(bridges) { 84 | // clear data 85 | var start_x = 3; 86 | var start_y = 3; 87 | 88 | for (var bridge_idx = 0; bridge_idx < bridges.length; bridge_idx++) { 89 | 90 | var bridge = bridges[bridge_idx]; 91 | var bridge_selector = ".switch_view#br_" + bridge.name; 92 | if (!$(bridge_selector).length) 93 | continue; 94 | 95 | d3.select(bridge_selector).selectAll("svg").remove(); 96 | 97 | for (var port_idx = 0; port_idx < bridge.ports.length; port_idx++) { 98 | var port = bridge.ports[port_idx]; 99 | // add port name to each interface in a group 100 | for (var intf_idx = 0; intf_idx < port.interfaces.length; intf_idx++) { 101 | port.interfaces[intf_idx].port = port.name; 102 | } 103 | 104 | var max_width = $(bridge_selector).prop('clientWidth'); 105 | var num_of_rows = 1; 106 | 107 | var svg_container = d3.select(bridge_selector).append("svg").attr({ 108 | "width" : "100%", 109 | "height" : "1px" // increased after drawing whole switch_view 110 | }); 111 | 112 | svg_container.call(tip); 113 | 114 | var max_width = $(bridge_selector).prop('clientWidth'); 115 | var row = 0; 116 | var prev_x = start_x; 117 | var real_width = start_x; 118 | 119 | var ports = svg_container.selectAll("rect").data(port.interfaces).enter().append("rect").attr({ 120 | "x" : function() { 121 | var x = start_x; 122 | if (start_x > (max_width - (led.width + 3))) { 123 | x = start_x = 3; 124 | row++; 125 | } else { 126 | start_x += (led.width + 3); 127 | } 128 | if (x > real_width) { 129 | real_width = x + led.width + 1; 130 | } 131 | return x; 132 | }, 133 | "y" : function() { 134 | var x = this.x.animVal.value; 135 | if (x < prev_x) { 136 | start_y += row * led.height + 3; 137 | } 138 | prev_x = x; 139 | return start_y; 140 | }, /* + led.width */ 141 | "width" : led.width, 142 | "height" : led.height, 143 | "class" : function(intf) { 144 | var c = "iface "; 145 | if (intf.admin_state !== "up") { 146 | c += "down "; 147 | } else { 148 | c += (intf.link_state === "up") ? "up" : "fail"; 149 | }; 150 | 151 | var statistics = (intf.statistics && intf.statistics[0] == "map") ? intf.statistics[1] : []; 152 | var is_err = statistics.filter(function(element) { 153 | return (((element[0].indexOf('err') != -1) || (element[0].indexOf('drop') != -1)) && (element[1] > 0)); 154 | }); 155 | 156 | if (is_err.length) { 157 | c += " error"; 158 | } 159 | 160 | return c; 161 | }, 162 | }).on({ 163 | "mouseover" : tip.show, 164 | "mouseout" : tip.hide, 165 | "click" : redirect 166 | }); 167 | 168 | start_x = 3; 169 | var real_hight = start_y + led.height; 170 | if (real_width == start_x) { 171 | real_width = start_x + led.width + 1; 172 | } 173 | 174 | svg_container.attr({ 175 | 'height' : real_hight + 3 + "px", 176 | 'width' : real_width + 3 + "px" 177 | }); 178 | 179 | svg_container.append("rect").attr({ 180 | "x" : start_x - 2, 181 | "y" : 1, 182 | "width" : real_width, 183 | "height" : 1 + real_hight, 184 | "style" : "fill:none; stroke:black;" 185 | }); 186 | start_y = 3; 187 | } 188 | } 189 | }); 190 | } 191 | 192 | function retrieve_updates() { 193 | var $alerts = $('.dropdown-alerts'); 194 | var $updates = $('#num-of-updates'); 195 | 196 | $.getJSON('/updates', function(updates) { 197 | $('li', $alerts).remove(); 198 | 199 | var len = updates.length < 10 ? updates.length : 10; 200 | 201 | for (var int = 0; int < len; int++) { 202 | var update = updates[int]; 203 | var key = Object.keys(update); 204 | var uuid = Object.keys(update[key]); 205 | var msg; 206 | var subkey; 207 | var action; 208 | var icon; 209 | 210 | if ((uuid instanceof Array) && (uuid.length > 1)) { 211 | msg = 'Multiple changes'; 212 | action = 'changes'; 213 | icon = 'fa-warning'; 214 | } else { 215 | if (update[key][uuid].old) { 216 | // removed 217 | msg = update[key][uuid]['old']; 218 | action = 'removed'; 219 | icon = 'fa-warning'; 220 | } else { 221 | // added 222 | msg = update[key][uuid]['new']; 223 | action = 'created'; 224 | icon = 'fa-comment'; 225 | } 226 | // TODO: foreach key in subkeys should be here 227 | subkey = Object.keys(msg)[0]; 228 | msg = msg[subkey]; 229 | } 230 | 231 | $alerts.append($('
  • ').append($('', { 232 | href : "#" 233 | }).append($('
    ').append($('', { 234 | class : 'fa ' + icon + ' fa-fw' 235 | })).append(key + ': "' + msg + '"').append($('', { 236 | 'class' : 'pull-right text-muted small' 237 | }).append(action)))).append($('
  • ', { 238 | class : 'divider' 239 | }))); 240 | } 241 | 242 | $alerts.append($('
  • ').append($('', { 243 | href : "/alerts", 244 | class : 'text-center' 245 | }).append($('').text('See All Alerts').append($('', { 246 | class : 'fa fa-angle-right fa-fw' 247 | }))))); 248 | 249 | //
  • See All Alerts
  • 250 | $updates.text(updates.length || ''); 251 | }); 252 | } 253 | 254 | function select_menu_item(selector) { 255 | $item = $(selector); 256 | if (!$item.length) { 257 | return; 258 | } 259 | 260 | $(".nav.collapse.in").removeClass("in"); 261 | $item.addClass("active"); 262 | $parents = $item.parents(".nav.collapse"); 263 | $parents.addClass("in"); 264 | } 265 | 266 | function submit_raw_json() { 267 | var data = $("#raw_content > textarea").val(); 268 | var bridge = window.location.pathname.substring(1); 269 | var index = bridge.indexOf('/'); 270 | if (index != -1) { 271 | bridge = bridge.substring(0, bridge.lastIndexOf('/')); 272 | } 273 | 274 | $.post('/' + bridge + '/raw_json', { 275 | request : data 276 | }, function(data) { 277 | console.log(data); 278 | $("#raw_reply").val(data); 279 | }); 280 | } 281 | -------------------------------------------------------------------------------- /public/js/d3.tip.v0.6.3.js: -------------------------------------------------------------------------------- 1 | // d3.tip 2 | // Copyright (c) 2013 Justin Palmer 3 | // 4 | // Tooltips for d3.js SVG visualizations 5 | 6 | // Public - contructs a new tooltip 7 | // 8 | // Returns a tip 9 | d3.tip = function() { 10 | var direction = d3_tip_direction, 11 | offset = d3_tip_offset, 12 | html = d3_tip_html, 13 | node = initNode(), 14 | svg = null, 15 | point = null, 16 | target = null 17 | 18 | function tip(vis) { 19 | svg = getSVGNode(vis) 20 | point = svg.createSVGPoint() 21 | document.body.appendChild(node) 22 | } 23 | 24 | // Public - show the tooltip on the screen 25 | // 26 | // Returns a tip 27 | tip.show = function() { 28 | var args = Array.prototype.slice.call(arguments) 29 | if(args[args.length - 1] instanceof SVGElement) target = args.pop() 30 | 31 | var content = html.apply(this, args), 32 | poffset = offset.apply(this, args), 33 | dir = direction.apply(this, args), 34 | nodel = d3.select(node), i = 0, 35 | coords 36 | 37 | nodel.html(content) 38 | .style({ opacity: 1, 'pointer-events': 'all' }) 39 | 40 | while(i--) nodel.classed(directions[i], false) 41 | coords = direction_callbacks.get(dir).apply(this) 42 | nodel.classed(dir, true).style({ 43 | top: (coords.top + poffset[0]) + 'px', 44 | left: (coords.left + poffset[1]) + 'px' 45 | }) 46 | 47 | return tip 48 | } 49 | 50 | // Public - hide the tooltip 51 | // 52 | // Returns a tip 53 | tip.hide = function() { 54 | nodel = d3.select(node) 55 | nodel.style({ opacity: 0, 'pointer-events': 'none' }) 56 | return tip 57 | } 58 | 59 | // Public: Proxy attr calls to the d3 tip container. Sets or gets attribute value. 60 | // 61 | // n - name of the attribute 62 | // v - value of the attribute 63 | // 64 | // Returns tip or attribute value 65 | tip.attr = function(n, v) { 66 | if (arguments.length < 2 && typeof n === 'string') { 67 | return d3.select(node).attr(n) 68 | } else { 69 | var args = Array.prototype.slice.call(arguments) 70 | d3.selection.prototype.attr.apply(d3.select(node), args) 71 | } 72 | 73 | return tip 74 | } 75 | 76 | // Public: Proxy style calls to the d3 tip container. Sets or gets a style value. 77 | // 78 | // n - name of the property 79 | // v - value of the property 80 | // 81 | // Returns tip or style property value 82 | tip.style = function(n, v) { 83 | if (arguments.length < 2 && typeof n === 'string') { 84 | return d3.select(node).style(n) 85 | } else { 86 | var args = Array.prototype.slice.call(arguments) 87 | d3.selection.prototype.style.apply(d3.select(node), args) 88 | } 89 | 90 | return tip 91 | } 92 | 93 | // Public: Set or get the direction of the tooltip 94 | // 95 | // v - One of n(north), s(south), e(east), or w(west), nw(northwest), 96 | // sw(southwest), ne(northeast) or se(southeast) 97 | // 98 | // Returns tip or direction 99 | tip.direction = function(v) { 100 | if (!arguments.length) return direction 101 | direction = v == null ? v : d3.functor(v) 102 | 103 | return tip 104 | } 105 | 106 | // Public: Sets or gets the offset of the tip 107 | // 108 | // v - Array of [x, y] offset 109 | // 110 | // Returns offset or 111 | tip.offset = function(v) { 112 | if (!arguments.length) return offset 113 | offset = v == null ? v : d3.functor(v) 114 | 115 | return tip 116 | } 117 | 118 | // Public: sets or gets the html value of the tooltip 119 | // 120 | // v - String value of the tip 121 | // 122 | // Returns html value or tip 123 | tip.html = function(v) { 124 | if (!arguments.length) return html 125 | html = v == null ? v : d3.functor(v) 126 | 127 | return tip 128 | } 129 | 130 | function d3_tip_direction() { return 'n' } 131 | function d3_tip_offset() { return [0, 0] } 132 | function d3_tip_html() { return ' ' } 133 | 134 | var direction_callbacks = d3.map({ 135 | n: direction_n, 136 | s: direction_s, 137 | e: direction_e, 138 | w: direction_w, 139 | nw: direction_nw, 140 | ne: direction_ne, 141 | sw: direction_sw, 142 | se: direction_se 143 | }), 144 | 145 | directions = direction_callbacks.keys() 146 | 147 | function direction_n() { 148 | var bbox = getScreenBBox() 149 | return { 150 | top: bbox.n.y - node.offsetHeight, 151 | left: bbox.n.x - node.offsetWidth / 2 152 | } 153 | } 154 | 155 | function direction_s() { 156 | var bbox = getScreenBBox() 157 | return { 158 | top: bbox.s.y, 159 | left: bbox.s.x - node.offsetWidth / 2 160 | } 161 | } 162 | 163 | function direction_e() { 164 | var bbox = getScreenBBox() 165 | return { 166 | top: bbox.e.y - node.offsetHeight / 2, 167 | left: bbox.e.x 168 | } 169 | } 170 | 171 | function direction_w() { 172 | var bbox = getScreenBBox() 173 | return { 174 | top: bbox.w.y - node.offsetHeight / 2, 175 | left: bbox.w.x - node.offsetWidth 176 | } 177 | } 178 | 179 | function direction_nw() { 180 | var bbox = getScreenBBox() 181 | return { 182 | top: bbox.nw.y - node.offsetHeight, 183 | left: bbox.nw.x - node.offsetWidth 184 | } 185 | } 186 | 187 | function direction_ne() { 188 | var bbox = getScreenBBox() 189 | return { 190 | top: bbox.ne.y - node.offsetHeight, 191 | left: bbox.ne.x 192 | } 193 | } 194 | 195 | function direction_sw() { 196 | var bbox = getScreenBBox() 197 | return { 198 | top: bbox.sw.y, 199 | left: bbox.sw.x - node.offsetWidth 200 | } 201 | } 202 | 203 | function direction_se() { 204 | var bbox = getScreenBBox() 205 | return { 206 | top: bbox.se.y, 207 | left: bbox.e.x 208 | } 209 | } 210 | 211 | function initNode() { 212 | var node = d3.select(document.createElement('div')) 213 | node.style({ 214 | position: 'absolute', 215 | opacity: 0, 216 | pointerEvents: 'none', 217 | boxSizing: 'border-box' 218 | }) 219 | 220 | return node.node() 221 | } 222 | 223 | function getSVGNode(el) { 224 | el = el.node() 225 | if(el.tagName.toLowerCase() == 'svg') 226 | return el 227 | 228 | return el.ownerSVGElement 229 | } 230 | 231 | // Private - gets the screen coordinates of a shape 232 | // 233 | // Given a shape on the screen, will return an SVGPoint for the directions 234 | // n(north), s(south), e(east), w(west), ne(northeast), se(southeast), nw(northwest), 235 | // sw(southwest). 236 | // 237 | // +-+-+ 238 | // | | 239 | // + + 240 | // | | 241 | // +-+-+ 242 | // 243 | // Returns an Object {n, s, e, w, nw, sw, ne, se} 244 | function getScreenBBox() { 245 | var targetel = target || d3.event.target, 246 | bbox = {}, 247 | matrix = targetel.getScreenCTM(), 248 | tbbox = targetel.getBBox(), 249 | width = tbbox.width, 250 | height = tbbox.height, 251 | x = tbbox.x, 252 | y = tbbox.y, 253 | scrollTop = document.documentElement.scrollTop || document.body.scrollTop, 254 | scrollLeft = document.documentElement.scrollLeft || document.body.scrollLeft 255 | 256 | 257 | point.x = x + scrollLeft 258 | point.y = y + scrollTop 259 | bbox.nw = point.matrixTransform(matrix) 260 | point.x += width 261 | bbox.ne = point.matrixTransform(matrix) 262 | point.y += height 263 | bbox.se = point.matrixTransform(matrix) 264 | point.x -= width 265 | bbox.sw = point.matrixTransform(matrix) 266 | point.y -= height / 2 267 | bbox.w = point.matrixTransform(matrix) 268 | point.x += width 269 | bbox.e = point.matrixTransform(matrix) 270 | point.x -= width / 2 271 | point.y -= height / 2 272 | bbox.n = point.matrixTransform(matrix) 273 | point.y += height 274 | bbox.s = point.matrixTransform(matrix) 275 | 276 | return bbox 277 | } 278 | 279 | return tip 280 | }; 281 | -------------------------------------------------------------------------------- /public/js/event_emitter.js: -------------------------------------------------------------------------------- 1 | // Generated by CommonJS Everywhere 0.9.7 2 | (function (global) { 3 | function require(file, parentModule) { 4 | if ({}.hasOwnProperty.call(require.cache, file)) 5 | return require.cache[file]; 6 | var resolved = require.resolve(file); 7 | if (!resolved) 8 | throw new Error('Failed to resolve module ' + file); 9 | var module$ = { 10 | id: file, 11 | require: require, 12 | filename: file, 13 | exports: {}, 14 | loaded: false, 15 | parent: parentModule, 16 | children: [] 17 | }; 18 | if (parentModule) 19 | parentModule.children.push(module$); 20 | var dirname = file.slice(0, file.lastIndexOf('/') + 1); 21 | require.cache[file] = module$.exports; 22 | resolved.call(module$.exports, module$, module$.exports, dirname, file); 23 | module$.loaded = true; 24 | return require.cache[file] = module$.exports; 25 | } 26 | require.modules = {}; 27 | require.cache = {}; 28 | require.resolve = function (file) { 29 | return {}.hasOwnProperty.call(require.modules, file) ? require.modules[file] : void 0; 30 | }; 31 | require.define = function (file, fn) { 32 | require.modules[file] = fn; 33 | }; 34 | require.define('/index.js', function (module, exports, __dirname, __filename) { 35 | var util = {}; 36 | util.isObject = function isObject(arg) { 37 | return typeof arg === 'object' && arg !== null; 38 | }; 39 | util.isNumber = function isNumber(arg) { 40 | return typeof arg === 'number'; 41 | }; 42 | util.isUndefined = function isUndefined(arg) { 43 | return arg === void 0; 44 | }; 45 | util.isFunction = function isFunction(arg) { 46 | return typeof arg === 'function'; 47 | }; 48 | function EventEmitter() { 49 | EventEmitter.init.call(this); 50 | } 51 | module.exports = EventEmitter; 52 | EventEmitter.EventEmitter = EventEmitter; 53 | EventEmitter.prototype._events = undefined; 54 | EventEmitter.prototype._maxListeners = undefined; 55 | EventEmitter.defaultMaxListeners = 10; 56 | EventEmitter.init = function () { 57 | this._events = this._events || {}; 58 | this._maxListeners = this._maxListeners || undefined; 59 | }; 60 | EventEmitter.prototype.setMaxListeners = function (n) { 61 | if (!util.isNumber(n) || n < 0 || isNaN(n)) 62 | throw TypeError('n must be a positive number'); 63 | this._maxListeners = n; 64 | return this; 65 | }; 66 | EventEmitter.prototype.emit = function (type) { 67 | var er, handler, len, args, i, listeners; 68 | if (!this._events) 69 | this._events = {}; 70 | if (type === 'error' && !this._events.error) { 71 | er = arguments[1]; 72 | if (er instanceof Error) { 73 | throw er; 74 | } else { 75 | throw Error('Uncaught, unspecified "error" event.'); 76 | } 77 | return false; 78 | } 79 | handler = this._events[type]; 80 | if (util.isUndefined(handler)) 81 | return false; 82 | if (util.isFunction(handler)) { 83 | switch (arguments.length) { 84 | case 1: 85 | handler.call(this); 86 | break; 87 | case 2: 88 | handler.call(this, arguments[1]); 89 | break; 90 | case 3: 91 | handler.call(this, arguments[1], arguments[2]); 92 | break; 93 | default: 94 | len = arguments.length; 95 | args = new Array(len - 1); 96 | for (i = 1; i < len; i++) 97 | args[i - 1] = arguments[i]; 98 | handler.apply(this, args); 99 | } 100 | } else if (util.isObject(handler)) { 101 | len = arguments.length; 102 | args = new Array(len - 1); 103 | for (i = 1; i < len; i++) 104 | args[i - 1] = arguments[i]; 105 | listeners = handler.slice(); 106 | len = listeners.length; 107 | for (i = 0; i < len; i++) 108 | listeners[i].apply(this, args); 109 | } 110 | return true; 111 | }; 112 | EventEmitter.prototype.addListener = function (type, listener) { 113 | var m; 114 | if (!util.isFunction(listener)) 115 | throw TypeError('listener must be a function'); 116 | if (!this._events) 117 | this._events = {}; 118 | if (this._events.newListener) 119 | this.emit('newListener', type, util.isFunction(listener.listener) ? listener.listener : listener); 120 | if (!this._events[type]) 121 | this._events[type] = listener; 122 | else if (util.isObject(this._events[type])) 123 | this._events[type].push(listener); 124 | else 125 | this._events[type] = [ 126 | this._events[type], 127 | listener 128 | ]; 129 | if (util.isObject(this._events[type]) && !this._events[type].warned) { 130 | var m; 131 | if (!util.isUndefined(this._maxListeners)) { 132 | m = this._maxListeners; 133 | } else { 134 | m = EventEmitter.defaultMaxListeners; 135 | } 136 | if (m && m > 0 && this._events[type].length > m) { 137 | this._events[type].warned = true; 138 | if (util.isFunction(console.error)) { 139 | console.error('(node) warning: possible EventEmitter memory ' + 'leak detected. %d listeners added. ' + 'Use emitter.setMaxListeners() to increase limit.', this._events[type].length); 140 | } 141 | if (util.isFunction(console.trace)) 142 | console.trace(); 143 | } 144 | } 145 | return this; 146 | }; 147 | EventEmitter.prototype.on = EventEmitter.prototype.addListener; 148 | EventEmitter.prototype.once = function (type, listener) { 149 | if (!util.isFunction(listener)) 150 | throw TypeError('listener must be a function'); 151 | var fired = false; 152 | function g() { 153 | this.removeListener(type, g); 154 | if (!fired) { 155 | fired = true; 156 | listener.apply(this, arguments); 157 | } 158 | } 159 | g.listener = listener; 160 | this.on(type, g); 161 | return this; 162 | }; 163 | EventEmitter.prototype.removeListener = function (type, listener) { 164 | var list, position, length, i; 165 | if (!util.isFunction(listener)) 166 | throw TypeError('listener must be a function'); 167 | if (!this._events || !this._events[type]) 168 | return this; 169 | list = this._events[type]; 170 | length = list.length; 171 | position = -1; 172 | if (list === listener || util.isFunction(list.listener) && list.listener === listener) { 173 | delete this._events[type]; 174 | if (this._events.removeListener) 175 | this.emit('removeListener', type, listener); 176 | } else if (util.isObject(list)) { 177 | for (i = length; i-- > 0;) { 178 | if (list[i] === listener || list[i].listener && list[i].listener === listener) { 179 | position = i; 180 | break; 181 | } 182 | } 183 | if (position < 0) 184 | return this; 185 | if (list.length === 1) { 186 | list.length = 0; 187 | delete this._events[type]; 188 | } else { 189 | list.splice(position, 1); 190 | } 191 | if (this._events.removeListener) 192 | this.emit('removeListener', type, listener); 193 | } 194 | return this; 195 | }; 196 | EventEmitter.prototype.removeAllListeners = function (type) { 197 | var key, listeners; 198 | if (!this._events) 199 | return this; 200 | if (!this._events.removeListener) { 201 | if (arguments.length === 0) 202 | this._events = {}; 203 | else if (this._events[type]) 204 | delete this._events[type]; 205 | return this; 206 | } 207 | if (arguments.length === 0) { 208 | for (key in this._events) { 209 | if (key === 'removeListener') 210 | continue; 211 | this.removeAllListeners(key); 212 | } 213 | this.removeAllListeners('removeListener'); 214 | this._events = {}; 215 | return this; 216 | } 217 | listeners = this._events[type]; 218 | if (util.isFunction(listeners)) { 219 | this.removeListener(type, listeners); 220 | } else if (Array.isArray(listeners)) { 221 | while (listeners.length) 222 | this.removeListener(type, listeners[listeners.length - 1]); 223 | } 224 | delete this._events[type]; 225 | return this; 226 | }; 227 | EventEmitter.prototype.listeners = function (type) { 228 | var ret; 229 | if (!this._events || !this._events[type]) 230 | ret = []; 231 | else if (util.isFunction(this._events[type])) 232 | ret = [this._events[type]]; 233 | else 234 | ret = this._events[type].slice(); 235 | return ret; 236 | }; 237 | EventEmitter.listenerCount = function (emitter, type) { 238 | var ret; 239 | if (!emitter._events || !emitter._events[type]) 240 | ret = 0; 241 | else if (util.isFunction(emitter._events[type])) 242 | ret = 1; 243 | else 244 | ret = emitter._events[type].length; 245 | return ret; 246 | }; 247 | }); 248 | global.EventEmitter = require('/index.js'); 249 | }.call(this, this)); 250 | -------------------------------------------------------------------------------- /public/js/loading.js: -------------------------------------------------------------------------------- 1 | Navigation.on("loading", function() { 2 | $('#loading-indicator').show(); 3 | var left_pos = ($(window).width() - $('#loading-indicator').width()) / 2; 4 | $('#loading-indicator').css("left", left_pos); 5 | 6 | /* hide left panel on mobile phones */ 7 | $("#wrapper").removeClass("sidebar-shown"); 8 | $(".navbar-collapse").toggleClass("in", $("#wrapper").hasClass("sidebar-shown")); 9 | }); 10 | 11 | Navigation.on("loaded", function() { 12 | $('#loading-indicator').hide(); 13 | }); 14 | -------------------------------------------------------------------------------- /public/js/navigation.js: -------------------------------------------------------------------------------- 1 | (function (global, $, undefined) { 2 | 3 | function Navigation(options) { 4 | // Singleton 5 | if (arguments.callee.instance) 6 | return arguments.callee.instance; 7 | this.init(options); 8 | arguments.callee.instance = this; 9 | 10 | //event emmiter 11 | EventEmitter.call(this); 12 | } 13 | 14 | Navigation.prototype.__proto__ = EventEmitter.prototype; 15 | 16 | Navigation.prototype.init = function(options) { 17 | var self = this; 18 | 19 | var defaults = this.options || { 20 | container: "body", 21 | landing_page: "/", 22 | params : {xhr: true} 23 | }; 24 | 25 | options = options || {}; 26 | this.options = $.extend(defaults, options); 27 | 28 | this.lastpage = this.options.landing_page; 29 | //history.pushState(this.options.landing_page, null, this.options.landing_page); 30 | this.event_handlers = {}; 31 | 32 | this.$container = $(this.options.container); 33 | 34 | $(window).off("popstate").on("popstate", function(e) { 35 | var url = e.originalEvent.state || global.location.pathname; 36 | self.go(url, null, true); 37 | }); 38 | $(window).on("load", function() { 39 | self.emit("loaded", global.location.pathname); 40 | }); 41 | }; 42 | 43 | Navigation.getInstance = function(options) { 44 | var instance = new Navigation(options); 45 | return instance; 46 | }; 47 | 48 | Navigation.prototype.go = function(url, params, no_history) { 49 | var self = this; 50 | if (url === self.lastpage) { 51 | return; 52 | } 53 | 54 | if (!url) { 55 | 56 | url = self.options.landing_page; 57 | no_history = true; 58 | } 59 | if (url.substr(0,1) !== "/"){ 60 | url = '/' + url.replace('#',''); //strip the #page part of the hash and leave only the page number 61 | } 62 | 63 | self.emit("loading"); 64 | 65 | params = $.extend(this.options.params, params); 66 | 67 | 68 | $.get(url, params, function(data) { 69 | self.$container.html(data); 70 | self.lastpage = url; 71 | self.emit("loaded", url); 72 | }).error(function(xhr, status, e) { 73 | if (xhr.status == 404) { 74 | var data = xhr.responseText; 75 | self.$container.html(data); 76 | }; 77 | self.emit("loaded",xhr); 78 | }); 79 | 80 | //push page change to history 81 | if (! no_history) { 82 | history.pushState(url, null, url); 83 | } 84 | }; 85 | 86 | 87 | $.fn.navigation = function(options) { 88 | var chain = this; 89 | var defaults = {url : "href"}; 90 | options = $.extend(defaults, options); 91 | this.each( function() { 92 | if ($(this).data("navigation")) return; 93 | 94 | $(this).data("navigation", true); 95 | $(this).on("click.navig", function(e) { 96 | var $this = $(this); 97 | e.preventDefault(); 98 | var addr = $this.attr("data-addr"); 99 | var url; 100 | if (options.url instanceof Function) { 101 | url = options.url($this); 102 | } else { 103 | url = $this.attr(options.url); 104 | } 105 | 106 | global.Navigation.go(url, {addr : addr}); 107 | return true; 108 | }); 109 | }); 110 | return chain; 111 | }; 112 | 113 | global.Navigation = Navigation.getInstance({}); 114 | }(this, $, undefined)); 115 | -------------------------------------------------------------------------------- /public/js/plugins/jquery.cookie.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery Cookie Plugin v1.4.1 3 | * https://github.com/carhartl/jquery-cookie 4 | * 5 | * Copyright 2006, 2014 Klaus Hartl 6 | * Released under the MIT license 7 | */ 8 | (function (factory) { 9 | if (typeof define === 'function' && define.amd) { 10 | // AMD 11 | define(['jquery'], factory); 12 | } else if (typeof exports === 'object') { 13 | // CommonJS 14 | factory(require('jquery')); 15 | } else { 16 | // Browser globals 17 | factory(jQuery); 18 | } 19 | }(function ($) { 20 | 21 | var pluses = /\+/g; 22 | 23 | function encode(s) { 24 | return config.raw ? s : encodeURIComponent(s); 25 | } 26 | 27 | function decode(s) { 28 | return config.raw ? s : decodeURIComponent(s); 29 | } 30 | 31 | function stringifyCookieValue(value) { 32 | return encode(config.json ? JSON.stringify(value) : String(value)); 33 | } 34 | 35 | function parseCookieValue(s) { 36 | if (s.indexOf('"') === 0) { 37 | // This is a quoted cookie as according to RFC2068, unescape... 38 | s = s.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\'); 39 | } 40 | 41 | try { 42 | // Replace server-side written pluses with spaces. 43 | // If we can't decode the cookie, ignore it, it's unusable. 44 | // If we can't parse the cookie, ignore it, it's unusable. 45 | s = decodeURIComponent(s.replace(pluses, ' ')); 46 | return config.json ? JSON.parse(s) : s; 47 | } catch(e) {} 48 | } 49 | 50 | function read(s, converter) { 51 | var value = config.raw ? s : parseCookieValue(s); 52 | return $.isFunction(converter) ? converter(value) : value; 53 | } 54 | 55 | var config = $.cookie = function (key, value, options) { 56 | 57 | // Write 58 | 59 | if (arguments.length > 1 && !$.isFunction(value)) { 60 | options = $.extend({}, config.defaults, options); 61 | 62 | if (typeof options.expires === 'number') { 63 | var days = options.expires, t = options.expires = new Date(); 64 | t.setTime(+t + days * 864e+5); 65 | } 66 | 67 | return (document.cookie = [ 68 | encode(key), '=', stringifyCookieValue(value), 69 | options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE 70 | options.path ? '; path=' + options.path : '', 71 | options.domain ? '; domain=' + options.domain : '', 72 | options.secure ? '; secure' : '' 73 | ].join('')); 74 | } 75 | 76 | // Read 77 | 78 | var result = key ? undefined : {}; 79 | 80 | // To prevent the for loop in the first place assign an empty array 81 | // in case there are no cookies at all. Also prevents odd result when 82 | // calling $.cookie(). 83 | var cookies = document.cookie ? document.cookie.split('; ') : []; 84 | 85 | for (var i = 0, l = cookies.length; i < l; i++) { 86 | var parts = cookies[i].split('='); 87 | var name = decode(parts.shift()); 88 | var cookie = parts.join('='); 89 | 90 | if (key && key === name) { 91 | // If second argument (value) is a function it's a converter... 92 | result = read(cookie, value); 93 | break; 94 | } 95 | 96 | // Prevent storing a cookie that we couldn't decode. 97 | if (!key && (cookie = read(cookie)) !== undefined) { 98 | result[name] = cookie; 99 | } 100 | } 101 | 102 | return result; 103 | }; 104 | 105 | config.defaults = {}; 106 | 107 | $.removeCookie = function (key, options) { 108 | if ($.cookie(key) === undefined) { 109 | return false; 110 | } 111 | 112 | // Must not alter options, thus extending a fresh object... 113 | $.cookie(key, '', $.extend({}, options, { expires: -1 })); 114 | return !$.cookie(key); 115 | }; 116 | 117 | })); 118 | -------------------------------------------------------------------------------- /public/js/plugins/metisMenu.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | * metismenu - v1.0.3 3 | * Easy menu jQuery plugin for Twitter Bootstrap 3 4 | * https://github.com/onokumus/metisMenu 5 | * 6 | * Made by Osman Nuri Okumuş 7 | * Under MIT License 8 | */ 9 | !function(a,b,c){function d(b,c){this.element=b,this.settings=a.extend({},f,c),this._defaults=f,this._name=e,this.init()}var e="metisMenu",f={toggle:!0};d.prototype={init:function(){var b=a(this.element),c=this.settings.toggle;this.isIE()<=9?(b.find("li.active").has("ul").children("ul").collapse("show"),b.find("li").not(".active").has("ul").children("ul").collapse("hide")):(b.find("li.active").has("ul").children("ul").addClass("collapse in"),b.find("li").not(".active").has("ul").children("ul").addClass("collapse")),b.find("li").has("ul").children("a").on("click",function(b){b.preventDefault(),a(this).parent("li").toggleClass("active").children("ul").collapse("toggle"),c&&a(this).parent("li").siblings().removeClass("active").children("ul.in").collapse("hide")})},isIE:function(){for(var a,b=3,d=c.createElement("div"),e=d.getElementsByTagName("i");d.innerHTML="",e[0];)return b>4?b:a}},a.fn[e]=function(b){return this.each(function(){a.data(this,"plugin_"+e)||a.data(this,"plugin_"+e,new d(this,b))})}}(jQuery,window,document); -------------------------------------------------------------------------------- /public/js/plugins/scroller.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Scroller v3.0.4 - 2014-04-08 3 | * A jQuery plugin for replacing default browser scrollbars. Part of the Formstone Library. 4 | * http://formstone.it/scroller/ 5 | * 6 | * Copyright 2014 Ben Plum; MIT Licensed 7 | */ 8 | 9 | !function(a,b){"use strict";function c(b){b=a.extend({},l,b||{}),null===k&&(k=a("body"));for(var c=a(this),e=0,f=c.length;f>e;e++)d(c.eq(e),b);return c}function d(c,d){if(!c.hasClass("scroller")){d=a.extend({},d,c.data("scroller-options"));var h="";h+='
    ',h+='
    ',h+='
    ',h+="
    ",d.paddingRight=parseInt(c.css("padding-right"),10),d.paddingBottom=parseInt(c.css("padding-bottom"),10),c.addClass(d.customClass+" scroller").wrapInner('
    ').prepend(h),d.horizontal&&c.addClass("scroller-horizontal");var i=a.extend({$scroller:c,$content:c.find(".scroller-content"),$bar:c.find(".scroller-bar"),$track:c.find(".scroller-track"),$handle:c.find(".scroller-handle")},d);i.trackMargin=parseInt(i.trackMargin,10),i.$content.on("scroll.scroller",i,e),i.$scroller.on("touchstart.scroller mousedown.scroller",".scroller-track",i,f).on("touchstart.scroller mousedown.scroller",".scroller-handle",i,g).data("scroller",i),m.reset.apply(c),a(b).one("load",function(){m.reset.apply(c)})}}function e(a){a.preventDefault(),a.stopPropagation();var b=a.data;if(b.horizontal){var c=b.$content.scrollLeft();0>c&&(c=0);var d=c/b.scrollRatio;d>b.handleBounds.right&&(d=b.handleBounds.right),b.$handle.css({left:d})}else{var e=b.$content.scrollTop();0>e&&(e=0);var f=e/b.scrollRatio;f>b.handleBounds.bottom&&(f=b.handleBounds.bottom),b.$handle.css({top:f})}}function f(a){a.preventDefault(),a.stopPropagation();var b=a.data,c=a.originalEvent,d=b.$track.offset(),e="undefined"!=typeof c.targetTouches?c.targetTouches[0]:null,f=e?e.pageX:a.clientX,g=e?e.pageY:a.clientY;b.horizontal?(b.mouseStart=f,b.handleLeft=f-d.left-b.handleWidth/2,j.apply(b.$scroller,[b,b.handleLeft])):(b.mouseStart=g,b.handleTop=g-d.top-b.handleHeight/2,j.apply(b.$scroller,[b,b.handleTop])),b.$content.off(".scroller"),k.on("touchmove.scroller mousemove.scroller",b,h).on("touchend.scroller mouseup.scroller",b,i)}function g(a){a.preventDefault(),a.stopPropagation();var b=a.data,c=a.originalEvent,d="undefined"!=typeof c.targetTouches?c.targetTouches[0]:null,e=d?d.pageX:a.clientX,f=d?d.pageY:a.clientY;b.horizontal?(b.mouseStart=e,b.handleLeft=parseInt(b.$handle.css("left"),10)):(b.mouseStart=f,b.handleTop=parseInt(b.$handle.css("top"),10)),b.$content.off(".scroller"),k.on("touchmove.scroller mousemove.scroller",b,h).on("touchend.scroller mouseup.scroller",b,i)}function h(a){a.preventDefault(),a.stopPropagation();var b=a.data,c=a.originalEvent,d=0,e=0,f="undefined"!=typeof c.targetTouches?c.targetTouches[0]:null,g=f?f.pageX:a.clientX,h=f?f.pageY:a.clientY;b.horizontal?(e=b.mouseStart-g,d=b.handleLeft-e):(e=b.mouseStart-h,d=b.handleTop-e),j.apply(b.$scroller,[b,d])}function i(a){a.preventDefault(),a.stopPropagation();var b=a.data;b.$content.on("scroll.scroller",b,e),k.off(".scroller")}function j(a,b){if(a.horizontal){ba.handleBounds.right&&(b=a.handleBounds.right);var c=Math.round(b*a.scrollRatio);a.$handle.css({left:b}),a.$content.scrollLeft(c)}else{ba.handleBounds.bottom&&(b=a.handleBounds.bottom);var d=Math.round(b*a.scrollRatio);a.$handle.css({top:b}),a.$content.scrollTop(d)}}var k=null,l={customClass:"",duration:0,handleSize:0,horizontal:!1,trackMargin:0},m={defaults:function(b){return l=a.extend(l,b||{}),a(this)},destroy:function(){return a(this).each(function(b,c){var d=a(c).data("scroller");d&&(d.$scroller.removeClass(d.customClass).removeClass("scroller").removeClass("scroller-active"),d.$bar.remove(),d.$content.contents().unwrap(),d.$content.off(".scroller"),d.$scroller.off(".scroller").removeData("scroller"))})},scroll:function(b,c){return a(this).each(function(){var d=a(this).data("scroller"),e=c||l.duration;if("number"!=typeof b){var f=a(b);if(f.length>0){var g=f.position();b=d.horizontal?g.left+d.$content.scrollLeft():g.top+d.$content.scrollTop()}else b=d.$content.scrollTop()}d.horizontal?d.$content.stop().animate({scrollLeft:b},e):d.$content.stop().animate({scrollTop:b},e)})},reset:function(){return a(this).each(function(){var b=a(this).data("scroller");if(b){if(b.$scroller.addClass("scroller-setup"),b.horizontal){b.barHeight=b.$content[0].offsetHeight-b.$content[0].clientHeight,b.frameWidth=b.$content.outerWidth(),b.trackWidth=b.frameWidth-2*b.trackMargin,b.scrollWidth=b.$content[0].scrollWidth,b.ratio=b.trackWidth/b.scrollWidth,b.trackRatio=b.trackWidth/b.scrollWidth,b.handleWidth=b.handleSize>0?b.handleSize:b.trackWidth*b.trackRatio,b.scrollRatio=(b.scrollWidth-b.frameWidth)/(b.trackWidth-b.handleWidth),b.handleBounds={left:0,right:b.trackWidth-b.handleWidth},b.$content.css({paddingBottom:b.barHeight+b.paddingBottom});var c=b.$content.scrollLeft(),d=c*b.ratio;b.scrollWidth<=b.frameWidth?b.$scroller.removeClass("scroller-active"):b.$scroller.addClass("scroller-active"),b.$bar.css({width:b.frameWidth}),b.$track.css({width:b.trackWidth,marginLeft:b.trackMargin,marginRight:b.trackMargin}),b.$handle.css({width:b.handleWidth}),j.apply(b.$scroller,[b,d])}else{b.barWidth=b.$content[0].offsetWidth-b.$content[0].clientWidth,b.frameHeight=b.$content.outerHeight(),b.trackHeight=b.frameHeight-2*b.trackMargin,b.scrollHeight=b.$content[0].scrollHeight,b.ratio=b.trackHeight/b.scrollHeight,b.trackRatio=b.trackHeight/b.scrollHeight,b.handleHeight=b.handleSize>0?b.handleSize:b.trackHeight*b.trackRatio,b.scrollRatio=(b.scrollHeight-b.frameHeight)/(b.trackHeight-b.handleHeight),b.handleBounds={top:0,bottom:b.trackHeight-b.handleHeight};var e=b.$content.scrollTop(),f=e*b.ratio;b.scrollHeight<=b.frameHeight?b.$scroller.removeClass("scroller-active"):b.$scroller.addClass("scroller-active"),b.$bar.css({height:b.frameHeight}),b.$track.css({height:b.trackHeight,marginBottom:b.trackMargin,marginTop:b.trackMargin}),b.$handle.css({height:b.handleHeight}),j.apply(b.$scroller,[b,f])}b.$scroller.removeClass("scroller-setup")}})}};a.fn.scroller=function(a){return m[a]?m[a].apply(this,Array.prototype.slice.call(arguments,1)):"object"!=typeof a&&a?this:c.apply(this,arguments)},a.scroller=function(a){"defaults"===a&&m.defaults.apply(this,Array.prototype.slice.call(arguments,1))}}(jQuery); -------------------------------------------------------------------------------- /public/js/sb-admin-2.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Start Bootstrap - SB Admin 2 Bootstrap Admin Theme (http://startbootstrap.com) 3 | * Code licensed under the Apache License v2.0. 4 | * For details, see http://www.apache.org/licenses/LICENSE-2.0. 5 | */ 6 | 7 | $(function() { 8 | 9 | $('#side-menu').metisMenu(); 10 | 11 | }); 12 | 13 | // Loads the correct sidebar on window load, 14 | // collapses the sidebar on window resize. 15 | // Sets the min-height of #page-wrapper to window size 16 | $(function() { 17 | $(window).bind("load resize", function() { 18 | topOffset = 50; 19 | width = (this.window.innerWidth > 0) ? this.window.innerWidth : this.screen.width; 20 | if (width < 768) { 21 | $('div.navbar-collapse').addClass('collapse') 22 | topOffset = 100; // 2-row-menu 23 | } else { 24 | $('div.navbar-collapse').removeClass('collapse') 25 | } 26 | 27 | height = (this.window.innerHeight > 0) ? this.window.innerHeight : this.screen.height; 28 | height = height - topOffset; 29 | if (height < 1) 30 | height = 1; 31 | if (height > topOffset) { 32 | $("#page-wrapper").css("min-height", (height) + "px"); 33 | } 34 | }) 35 | }) 36 | -------------------------------------------------------------------------------- /public/js/sweet-alert.min.js: -------------------------------------------------------------------------------- 1 | !function(e,t){function n(){var e='

    Title

    Text

    ',n=t.createElement("div");n.innerHTML=e,t.body.appendChild(n)}function o(t){var n=m(),o=n.querySelector("h2"),r=n.querySelector("p"),a=n.querySelector("button.cancel"),c=n.querySelector("button.confirm");if(o.innerHTML=b(t.title).split("\n").join("
    "),r.innerHTML=b(t.text||"").split("\n").join("
    "),t.text&&w(r),x(n.querySelectorAll(".icon")),t.type){for(var l=!1,s=0;sr;r++)o=parseInt(e.substr(2*r,2),16),o=Math.round(Math.min(Math.max(0,o+o*t),255)).toString(16),n+=("00"+o).substr(o.length);return n}function a(e){var t=/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(e);return t?parseInt(t[1],16)+", "+parseInt(t[2],16)+", "+parseInt(t[3],16):null}function i(e,t){var n=a(t);e.style.boxShadow="0 0 2px rgba("+n+", 0.8), inset 0 0 0 1px rgba(0, 0, 0, 0.05)"}function c(){var e=m();B(g(),10),w(e),p(e,"showSweetAlert"),v(e,"hideSweetAlert"),O=t.activeElement;var n=e.querySelector("button.confirm");n.focus(),setTimeout(function(){p(e,"visible")},500)}function l(){var n=m();T(g(),5),T(n,5),v(n,"showSweetAlert"),p(n,"hideSweetAlert"),v(n,"visible");var o=n.querySelector(".icon.success");v(o,"animate"),v(o.querySelector(".tip"),"animateSuccessTip"),v(o.querySelector(".long"),"animateSuccessLong");var r=n.querySelector(".icon.error");v(r,"animateErrorIcon"),v(r.querySelector(".x-mark"),"animateXMark");var a=n.querySelector(".icon.warning");v(a,"pulseWarning"),v(a.querySelector(".body"),"pulseWarningIns"),v(a.querySelector(".dot"),"pulseWarningIns"),e.onkeydown=A,t.onclick=I,O&&O.focus(),M=void 0}function s(){var e=m();e.style.marginTop=k(m())}var u=".sweet-alert",f=".sweet-overlay",d=["error","warning","info","success"],m=function(){return t.querySelector(u)},g=function(){return t.querySelector(f)},y=function(e,t){return new RegExp(" "+t+" ").test(" "+e.className+" ")},p=function(e,t){y(e,t)||(e.className+=" "+t)},v=function(e,t){var n=" "+e.className.replace(/[\t\r\n]/g," ")+" ";if(y(e,t)){for(;n.indexOf(" "+t+" ")>=0;)n=n.replace(" "+t+" "," ");e.className=n.replace(/^\s+|\s+$/g,"")}},b=function(e){var n=t.createElement("div");return n.appendChild(t.createTextNode(e)),n.innerHTML},h=function(e){e.style.opacity="",e.style.display="block"},w=function(e){if(e&&!e.length)return h(e);for(var t=0;t0?setTimeout(o,t):e.style.display="none"};o()},E=function(n){if(MouseEvent){var o=new MouseEvent("click",{view:e,bubbles:!1,cancelable:!0});n.dispatchEvent(o)}else if(t.createEvent){var r=t.createEvent("MouseEvents");r.initEvent("click",!1,!1),n.dispatchEvent(r)}else t.createEventObject?n.fireEvent("onclick"):"function"==typeof n.onclick&&n.onclick()},q=function(t){"function"==typeof t.stopPropagation?(t.stopPropagation(),t.preventDefault()):e.event&&e.event.hasOwnProperty("cancelBubble")&&(e.event.cancelBubble=!0)},O,I,A,M;e.sweetAlert=e.swal=function(){function n(e){var t=e.keyCode||e.which;if(-1!==[9,13,32,27].indexOf(t)){for(var n=e.target||e.srcElement,o=-1,r=0;r.btn{width:100%;padding-right:25px}.error .bootstrap-select .btn{border:1px solid #b94a48}.control-group.error .bootstrap-select .dropdown-toggle{border-color:#b94a48}.bootstrap-select.fit-width{width:auto!important}.bootstrap-select:not([class*=col-]):not([class*=form-control]):not(.input-group-btn){width:220px}.bootstrap-select .btn:focus{outline:thin dotted #333!important;outline:5px auto -webkit-focus-ring-color!important;outline-offset:-2px}.bootstrap-select.form-control{margin-bottom:0;padding:0;border:none}.bootstrap-select.form-control:not([class*=col-]){width:100%}.bootstrap-select.btn-group:not(.input-group-btn),.bootstrap-select.btn-group[class*=col-]{float:none;display:inline-block;margin-left:0}.bootstrap-select.btn-group.dropdown-menu-right,.bootstrap-select.btn-group[class*=col-].dropdown-menu-right,.row-fluid .bootstrap-select.btn-group[class*=col-].dropdown-menu-right{float:right}.form-search .bootstrap-select.btn-group,.form-inline .bootstrap-select.btn-group,.form-horizontal .bootstrap-select.btn-group,.form-group .bootstrap-select.btn-group{margin-bottom:0}.form-group-lg .bootstrap-select.btn-group.form-control,.form-group-sm .bootstrap-select.btn-group.form-control{padding:0}.form-inline .bootstrap-select.btn-group .form-control{width:100%}.input-append .bootstrap-select.btn-group{margin-left:-1px}.input-prepend .bootstrap-select.btn-group{margin-right:-1px}.bootstrap-select.btn-group>.disabled{cursor:not-allowed}.bootstrap-select.btn-group>.disabled:focus{outline:0!important}.bootstrap-select.btn-group .btn .filter-option{display:inline-block;overflow:hidden;width:100%;text-align:left}.bootstrap-select.btn-group .btn .caret{position:absolute;top:50%;right:12px;margin-top:-2px;vertical-align:middle}.bootstrap-select.btn-group[class*=col-] .btn{width:100%}.bootstrap-select.btn-group .dropdown-menu{min-width:100%;z-index:1035;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.bootstrap-select.btn-group .dropdown-menu.inner{position:static;border:0;padding:0;margin:0;border-radius:0;-webkit-box-shadow:none;box-shadow:none}.bootstrap-select.btn-group .dropdown-menu li{position:relative}.bootstrap-select.btn-group .dropdown-menu li:not(.disabled) a:hover small,.bootstrap-select.btn-group .dropdown-menu li:not(.disabled) a:focus small,.bootstrap-select.btn-group .dropdown-menu li.active:not(.disabled) a small{color:#64b1d8;color:rgba(100,177,216,.4)}.bootstrap-select.btn-group .dropdown-menu li.disabled a{cursor:not-allowed}.bootstrap-select.btn-group .dropdown-menu li a{cursor:pointer}.bootstrap-select.btn-group .dropdown-menu li a.opt{position:relative;padding-left:2.25em}.bootstrap-select.btn-group .dropdown-menu li a span.check-mark{display:none}.bootstrap-select.btn-group .dropdown-menu li a span.text{display:inline-block}.bootstrap-select.btn-group .dropdown-menu li small{padding-left:.5em}.bootstrap-select.btn-group .dropdown-menu .notify{position:absolute;bottom:5px;width:96%;margin:0 2%;min-height:26px;padding:3px 5px;background:#f5f5f5;border:1px solid #e3e3e3;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05);pointer-events:none;opacity:.9;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.bootstrap-select.btn-group .no-results{padding:3px;background:#f5f5f5;margin:0 5px}.bootstrap-select.btn-group.fit-width .btn .filter-option{position:static}.bootstrap-select.btn-group.fit-width .btn .caret{position:static;top:auto;margin-top:-1px}.bootstrap-select.btn-group.show-tick .dropdown-menu li.selected a span.check-mark{position:absolute;display:inline-block;right:15px;margin-top:5px}.bootstrap-select.btn-group.show-tick .dropdown-menu li a span.text{margin-right:34px}.bootstrap-select.show-menu-arrow.open>.btn{z-index:1035+1}.bootstrap-select.show-menu-arrow .dropdown-toggle:before{content:'';border-left:7px solid transparent;border-right:7px solid transparent;border-bottom-width:7px;border-bottom-style:solid;border-bottom-color:#ccc;border-bottom-color:rgba(204,204,204,.2);position:absolute;bottom:-4px;left:9px;display:none}.bootstrap-select.show-menu-arrow .dropdown-toggle:after{content:'';border-left:6px solid transparent;border-right:6px solid transparent;border-bottom:6px solid #fff;position:absolute;bottom:-4px;left:10px;display:none}.bootstrap-select.show-menu-arrow.dropup .dropdown-toggle:before{bottom:auto;top:-3px;border-bottom:0;border-top-width:7px;border-top-style:solid;border-top-color:#ccc;border-top-color:rgba(204,204,204,.2)}.bootstrap-select.show-menu-arrow.dropup .dropdown-toggle:after{bottom:auto;top:-3px;border-top:6px solid #fff;border-bottom:0}.bootstrap-select.show-menu-arrow.pull-right .dropdown-toggle:before{right:12px;left:auto}.bootstrap-select.show-menu-arrow.pull-right .dropdown-toggle:after{right:13px;left:auto}.bootstrap-select.show-menu-arrow.open>.dropdown-toggle:before,.bootstrap-select.show-menu-arrow.open>.dropdown-toggle:after{display:block}.bs-searchbox,.bs-actionsbox{padding:4px 8px}.bs-actionsbox{float:left;width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.bs-actionsbox .btn-group button{width:50%}.bs-searchbox+.bs-actionsbox{padding:0 8px 4px}.bs-searchbox input.form-control{margin-bottom:0;width:100%}.mobile-device{position:absolute;top:0;left:0;display:block!important;width:100%;height:100%!important;opacity:0} -------------------------------------------------------------------------------- /public/stylesheets/default.css: -------------------------------------------------------------------------------- 1 | #wrapper { 2 | padding-left: 0; 3 | } 4 | 5 | #wrapper.toggled { 6 | padding-left: 250px; 7 | } 8 | 9 | #sidebar_wrapper { 10 | font-size: 18px; 11 | z-index: 1000; 12 | position: fixed; 13 | /*left: 460px;*/ 14 | width: 0; 15 | height: 100%; 16 | margin-left: -460px; 17 | overflow-y: none; 18 | background-color: #29272c; 19 | background: url('/images/bg_login.jpg') bottom; 20 | } 21 | 22 | #sidebar_wrapper { 23 | color: #555; 24 | padding-left: 0px; 25 | padding-right: 0px; 26 | height: 100%; 27 | } 28 | 29 | #sidebar_wrapper .well { 30 | background-color: transparent; 31 | text-align: right; 32 | border: none; 33 | box-shadow: none; 34 | } 35 | 36 | #sidebar_wrapper .well.well-sm { 37 | line-height: 30px; 38 | } 39 | 40 | #wrapper.toggled #sidebar_wrapper { 41 | width: 460px; 42 | } 43 | 44 | #page_content_wrapper { 45 | width: 100%; 46 | } 47 | 48 | #sidebar_wrapper > div { 49 | position: absolute; 50 | width: 100%; 51 | padding-right: 40px; 52 | text-align: right; 53 | } 54 | 55 | .sidebar-welcome { 56 | position: absolute; 57 | bottom: 70%; 58 | color: #007da5; 59 | font-size: 24px; 60 | font-weight: bold; 61 | } 62 | 63 | .sidebar-welcome.mobile-version { 64 | visibility: visible; 65 | } 66 | 67 | #sidebar_choose_ip { 68 | top: 30%; 69 | } 70 | 71 | .sidebar-welcome div { 72 | width: 100%; 73 | } 74 | 75 | .sidebar-welcome .project-name { 76 | font-size: 42px; 77 | font-weight: normal; 78 | } 79 | 80 | .bottom-line { 81 | font-size: 14px; 82 | color: #999792; 83 | position: absolute; 84 | bottom: 3%; 85 | } 86 | 87 | .links-navbar { 88 | list-style-type: none; 89 | margin: 0; 90 | } 91 | 92 | .links-navbar li { 93 | display: inline-block; 94 | } 95 | 96 | .links-navbar li:not(:first-child):before { 97 | content: "|"; 98 | margin: 0 15px; 99 | } 100 | 101 | .links-navbar a { 102 | color: #99979c; 103 | } 104 | 105 | .links-navbar a:hover { 106 | text-decoration: none; 107 | } 108 | 109 | .btn { 110 | color: #ffffff; 111 | /*background-color: #3372f2;*/ 112 | background-color: #00a3d8; 113 | font-weight: normal; 114 | font-family: sans; 115 | } 116 | 117 | .btn:hover, .btn:active, .btn:focus { 118 | color: #ffffff; 119 | /*background-color: #1352e2;*/ 120 | background-color: #00a3d8; 121 | } 122 | 123 | .btn.disabled, .btn[disabled] { 124 | background-color: #c9c7cc !important; 125 | } 126 | 127 | .btn.loading { 128 | /* temporarily hack: change to lada-spinner in future */ 129 | background-image: url("/images/spinner.gif"); 130 | background-position: center; 131 | background-repeat: no-repeat; 132 | text-indent: -1000px; 133 | } 134 | 135 | #connect_btn { 136 | width: 120px; 137 | margin-left: 14px; 138 | } 139 | 140 | .nice { 141 | margin-bottom: 0px; 142 | border-radius: 0px; 143 | height: 50px; 144 | } 145 | 146 | .font18 { 147 | font-size: 18px; 148 | } 149 | 150 | .radius-fixed { 151 | border-radius: 0px; 152 | } 153 | 154 | #row { 155 | position: absolute; 156 | top: 30%; 157 | } 158 | 159 | body { 160 | background-color: #e7e9ec; 161 | background: url('/images/bg_login.jpg') bottom right; 162 | } 163 | 164 | .arrow-right { 165 | margin-top: 16px; 166 | position: absolute; 167 | top: 30%; 168 | width: 0; 169 | height: 0; 170 | border-top: 8px solid transparent; 171 | border-bottom: 8px solid transparent; 172 | border-left: 8px solid #99979c; 173 | } 174 | 175 | #add_connection { 176 | text-align: center; 177 | padding-top: 70px; 178 | 179 | } 180 | 181 | .btn-enter { 182 | padding-top: 6px; 183 | vertical-align: bottom; 184 | padding-bottom: 32px; 185 | } 186 | 187 | .icon { 188 | background-image: url("/images/icons_16x16px.png"); 189 | width: 16px; 190 | height: 16px; 191 | display: inline-block; 192 | } 193 | 194 | .icon.icon-add, .icon.icon-add { 195 | background-position: 0px 112px; 196 | } 197 | 198 | .icon.icon-add.disabled, .icon.icon-add[disabled] { 199 | background-position: 0px 32px; 200 | } 201 | 202 | label.mobile-version { 203 | color: #D3E4FF; 204 | } 205 | 206 | @media(min-width:768px) { 207 | body { 208 | /*background-image: none;*/ 209 | background-color: #e7e9ec; 210 | } 211 | 212 | #wrapper { 213 | padding-left: 460px; 214 | } 215 | 216 | #wrapper.toggled { 217 | padding-left: 0; 218 | } 219 | 220 | #sidebar_wrapper { 221 | width: 460px; 222 | } 223 | 224 | #wrapper.toggled #sidebar_wrapper { 225 | width: 0; 226 | } 227 | 228 | #page_content_wrapper { 229 | /* padding: 20px; */ 230 | } 231 | 232 | #wrapper.toggled #page_content_wrapper { 233 | position: relative; 234 | margin-right: 0; 235 | } 236 | 237 | .sidebar-welcome.mobile-version { 238 | visibility:hidden; 239 | } 240 | 241 | label.mobile-version { 242 | color: #99979C; 243 | } 244 | 245 | .arrow-right { 246 | border-left-color: black; 247 | } 248 | 249 | #row { 250 | position: absolute; 251 | top: 30%; 252 | width: 100%; 253 | left: 0px; 254 | padding-left: 475px; 255 | } 256 | } 257 | -------------------------------------------------------------------------------- /public/stylesheets/plugins/metisMenu.min.css: -------------------------------------------------------------------------------- 1 | .arrow{float:right}.glyphicon.arrow:before{content:"\e079"}.active>a>.glyphicon.arrow:before{content:"\e114"}.fa.arrow:before{content:"\f104"}.active>a>.fa.arrow:before{content:"\f107"}.plus-times{float:right}.fa.plus-times:before{content:"\f067"}.active>a>.fa.plus-times{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1);-webkit-transform:rotate(45deg);-moz-transform:rotate(45deg);-ms-transform:rotate(45deg);-o-transform:rotate(45deg);transform:rotate(45deg)}.plus-minus{float:right}.fa.plus-minus:before{content:"\f067"}.active>a>.fa.plus-minus:before{content:"\f068"} -------------------------------------------------------------------------------- /public/stylesheets/plugins/scroller.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Scroller v3.0.4 - 2014-04-08 3 | * A jQuery plugin for replacing default browser scrollbars. Part of the Formstone Library. 4 | * http://formstone.it/scroller/ 5 | * 6 | * Copyright 2014 Ben Plum; MIT Licensed 7 | */ 8 | 9 | .scroller { overflow: hidden; overflow-x: hidden; overflow-y: hidden; position: relative; } 10 | .scroller-content { height: 100%; overflow: auto; overflow-x: hidden; overflow-y: auto; position: relative; z-index: 1; 11 | -webkit-overflow-scrolling: touch; 12 | } 13 | .scroller-bar { background: #FBFBFB; border-left: 1px solid #EEEEEE; display: none; height: 100%; position: absolute; right: 0; top: 0; width: 20px; z-index: 2; } 14 | .scroller-track { background: #FBFBFB; height: 100%; overflow: hidden; position: relative; width: 100%; } 15 | .scroller-handle { background: #EEEEEE; border: 1px solid #D5D5D5; border-radius: 5px; cursor: pointer; height: 20px; overflow: hidden; position: absolute; right: 5px; top: 0; width: 10px; z-index: 2; 16 | -webkit-transition: right 0.1s linear, width 0.1s linear; 17 | -moz-transition: right 0.1s linear, width 0.1s linear; 18 | -ms-transition: right 0.1s linear, width 0.1s linear; 19 | -o-transition: right 0.1s linear, width 0.1s linear; 20 | transition: right 0.1s linear, width 0.1s linear; 21 | } 22 | 23 | /* Webkit Fix */ 24 | .scroller-content::-webkit-scrollbar, 25 | .scroller-content::-webkit-scrollbar-button, 26 | .scroller-content::-webkit-scrollbar-track, 27 | .scroller-content::-webkit-scrollbar-track-piece, 28 | .scroller-content::-webkit-scrollbar-thumb, 29 | .scroller-content::-webkit-scrollbar-corner, 30 | .scroller-content::-webkit-resizer { background: transparent; opacity: 0; } 31 | 32 | /* Active Scrollbar */ 33 | .scroller-active .scroller-content { padding: 20px; } 34 | .scroller-active .scroller-bar { display: block; } 35 | 36 | /* Setup Scrollbar - Should match 'active' styles for proper sizing */ 37 | .scroller-active .scroller-content { padding: 20px; } 38 | .scroller-active .scroller-bar { display: block; } 39 | 40 | /* Horizontal */ 41 | .scroller-horizontal .scroller-content { overflow: auto; overflow-x: auto; overflow-y: hidden; padding: 0 0 10px 0; } 42 | .scroller-horizontal .scroller-bar { border-left: none; border-top: 1px solid #EEEEEE; bottom: 0; height: 20px; top: auto; width: 100%; } 43 | .scroller-horizontal .scroller-handle { bottom: 5px; height: 10px; right: auto; top: auto; width: 20px; } -------------------------------------------------------------------------------- /public/stylesheets/sb-admin-2.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Start Bootstrap - SB Admin 2 Bootstrap Admin Theme (http://startbootstrap.com) 3 | * Code licensed under the Apache License v2.0. 4 | * For details, see http://www.apache.org/licenses/LICENSE-2.0. 5 | */ 6 | 7 | body { 8 | background-color: #f8f8f8; 9 | } 10 | 11 | #wrapper { 12 | width: 100%; 13 | } 14 | 15 | #page-wrapper { 16 | padding: 0 15px; 17 | min-height: 568px; 18 | background-color: #fff; 19 | } 20 | 21 | @media(min-width:768px) { 22 | #page-wrapper { 23 | position: inherit; 24 | margin: 0 0 0 250px; 25 | /*padding: 0 30px;*/ 26 | border-left: 1px solid #e7e7e7; 27 | } 28 | } 29 | 30 | .navbar-top-links li { 31 | display: inline-block; 32 | } 33 | 34 | .navbar-top-links li:last-child { 35 | margin-right: 15px; 36 | } 37 | 38 | .navbar-top-links li a { 39 | padding: 15px; 40 | min-height: 50px; 41 | } 42 | 43 | .navbar-top-links .dropdown-menu li { 44 | display: block; 45 | } 46 | 47 | .navbar-top-links .dropdown-menu li:last-child { 48 | margin-right: 0; 49 | } 50 | 51 | .navbar-top-links .dropdown-menu li a { 52 | padding: 3px 20px; 53 | min-height: 0; 54 | } 55 | 56 | .navbar-top-links .dropdown-menu li a div { 57 | white-space: normal; 58 | } 59 | 60 | .navbar-top-links .dropdown-messages, 61 | .navbar-top-links .dropdown-tasks, 62 | .navbar-top-links .dropdown-alerts { 63 | width: 310px; 64 | min-width: 0; 65 | } 66 | 67 | .navbar-top-links .dropdown-messages { 68 | margin-left: 5px; 69 | } 70 | 71 | .navbar-top-links .dropdown-tasks { 72 | margin-left: -59px; 73 | } 74 | 75 | .navbar-top-links .dropdown-alerts { 76 | margin-left: -123px; 77 | } 78 | 79 | .navbar-top-links .dropdown-user { 80 | right: 0; 81 | /* left: auto; */ 82 | margin-left: 5px; 83 | } 84 | 85 | .sidebar .sidebar-nav.navbar-collapse { 86 | padding-right: 0; 87 | padding-left: 0; 88 | } 89 | 90 | .sidebar .sidebar-search { 91 | padding: 15px; 92 | } 93 | 94 | .sidebar ul li { 95 | border-bottom: 1px solid #e7e7e7; 96 | } 97 | 98 | .sidebar ul li a.active { 99 | background-color: #eee; 100 | } 101 | 102 | .sidebar .arrow { 103 | float: right; 104 | } 105 | 106 | .sidebar .fa.arrow:before { 107 | content: "\f104"; 108 | } 109 | 110 | .sidebar .active>a>.fa.arrow:before { 111 | content: "\f107"; 112 | } 113 | 114 | .sidebar .nav-second-level li, 115 | .sidebar .nav-third-level li { 116 | border-bottom: 0!important; 117 | } 118 | 119 | .sidebar .nav-second-level li a { 120 | padding-left: 37px; 121 | } 122 | 123 | .sidebar .nav-third-level li a { 124 | padding-left: 52px; 125 | } 126 | 127 | @media(min-width:768px) { 128 | .sidebar { 129 | z-index: 1; 130 | position: absolute; 131 | width: 250px; 132 | margin-top: 51px; 133 | } 134 | 135 | .navbar-top-links .dropdown-messages, 136 | .navbar-top-links .dropdown-tasks, 137 | .navbar-top-links .dropdown-alerts { 138 | margin-left: auto; 139 | } 140 | } 141 | 142 | .btn-outline { 143 | color: inherit; 144 | background-color: transparent; 145 | transition: all .5s; 146 | } 147 | 148 | .btn-primary.btn-outline { 149 | color: #428bca; 150 | } 151 | 152 | .btn-success.btn-outline { 153 | color: #5cb85c; 154 | } 155 | 156 | .btn-info.btn-outline { 157 | color: #5bc0de; 158 | } 159 | 160 | .btn-warning.btn-outline { 161 | color: #f0ad4e; 162 | } 163 | 164 | .btn-danger.btn-outline { 165 | color: #d9534f; 166 | } 167 | 168 | .btn-primary.btn-outline:hover, 169 | .btn-success.btn-outline:hover, 170 | .btn-info.btn-outline:hover, 171 | .btn-warning.btn-outline:hover, 172 | .btn-danger.btn-outline:hover { 173 | color: #fff; 174 | } 175 | 176 | .chat { 177 | margin: 0; 178 | padding: 0; 179 | list-style: none; 180 | } 181 | 182 | .chat li { 183 | margin-bottom: 10px; 184 | padding-bottom: 5px; 185 | border-bottom: 1px dotted #999; 186 | } 187 | 188 | .chat li.left .chat-body { 189 | margin-left: 60px; 190 | } 191 | 192 | .chat li.right .chat-body { 193 | margin-right: 60px; 194 | } 195 | 196 | .chat li .chat-body p { 197 | margin: 0; 198 | } 199 | 200 | .panel .slidedown .glyphicon, 201 | .chat .glyphicon { 202 | margin-right: 5px; 203 | } 204 | 205 | .chat-panel .panel-body { 206 | height: 350px; 207 | overflow-y: scroll; 208 | } 209 | 210 | .login-panel { 211 | margin-top: 25%; 212 | } 213 | 214 | .flot-chart { 215 | display: block; 216 | height: 400px; 217 | } 218 | 219 | .flot-chart-content { 220 | width: 100%; 221 | height: 100%; 222 | } 223 | 224 | table.dataTable thead .sorting, 225 | table.dataTable thead .sorting_asc, 226 | table.dataTable thead .sorting_desc, 227 | table.dataTable thead .sorting_asc_disabled, 228 | table.dataTable thead .sorting_desc_disabled { 229 | background: 0 0; 230 | } 231 | 232 | table.dataTable thead .sorting_asc:after { 233 | content: "\f0de"; 234 | float: right; 235 | font-family: fontawesome; 236 | } 237 | 238 | table.dataTable thead .sorting_desc:after { 239 | content: "\f0dd"; 240 | float: right; 241 | font-family: fontawesome; 242 | } 243 | 244 | table.dataTable thead .sorting:after { 245 | content: "\f0dc"; 246 | float: right; 247 | font-family: fontawesome; 248 | color: rgba(50,50,50,.5); 249 | } 250 | 251 | .btn-circle { 252 | width: 30px; 253 | height: 30px; 254 | padding: 6px 0; 255 | border-radius: 15px; 256 | text-align: center; 257 | font-size: 12px; 258 | line-height: 1.428571429; 259 | } 260 | 261 | .btn-circle.btn-lg { 262 | width: 50px; 263 | height: 50px; 264 | padding: 10px 16px; 265 | border-radius: 25px; 266 | font-size: 18px; 267 | line-height: 1.33; 268 | } 269 | 270 | .btn-circle.btn-xl { 271 | width: 70px; 272 | height: 70px; 273 | padding: 10px 16px; 274 | border-radius: 35px; 275 | font-size: 24px; 276 | line-height: 1.33; 277 | } 278 | 279 | .show-grid [class^=col-] { 280 | padding-top: 10px; 281 | padding-bottom: 10px; 282 | border: 1px solid #ddd; 283 | background-color: #eee!important; 284 | } 285 | 286 | .show-grid { 287 | margin: 15px 0; 288 | } 289 | 290 | .huge { 291 | font-size: 40px; 292 | } 293 | 294 | .panel-green { 295 | border-color: #5cb85c; 296 | } 297 | 298 | .panel-green .panel-heading { 299 | border-color: #5cb85c; 300 | color: #fff; 301 | background-color: #5cb85c; 302 | } 303 | 304 | .panel-green a { 305 | color: #5cb85c; 306 | } 307 | 308 | .panel-green a:hover { 309 | color: #3d8b3d; 310 | } 311 | 312 | .panel-red { 313 | border-color: #d9534f; 314 | } 315 | 316 | .panel-red .panel-heading { 317 | border-color: #d9534f; 318 | color: #fff; 319 | background-color: #d9534f; 320 | } 321 | 322 | .panel-red a { 323 | color: #d9534f; 324 | } 325 | 326 | .panel-red a:hover { 327 | color: #b52b27; 328 | } 329 | 330 | .panel-yellow { 331 | border-color: #f0ad4e; 332 | } 333 | 334 | .panel-yellow .panel-heading { 335 | border-color: #f0ad4e; 336 | color: #fff; 337 | background-color: #f0ad4e; 338 | } 339 | 340 | .panel-yellow a { 341 | color: #f0ad4e; 342 | } 343 | 344 | .panel-yellow a:hover { 345 | color: #df8a13; 346 | } 347 | -------------------------------------------------------------------------------- /public/stylesheets/style.css: -------------------------------------------------------------------------------- 1 | .glyphicon { 2 | color: #99979c; 3 | } 4 | 5 | #loading-indicator { 6 | position: fixed; 7 | left: 50%; 8 | top: 50%; 9 | z-index: 100; 10 | } 11 | 12 | #wrapper.sidebar-shown { 13 | overflow-x : hidden; 14 | } 15 | .sidebar-shown .sidebar { 16 | z-index: 1; 17 | position: absolute; 18 | width: 250px; 19 | display: block; 20 | } 21 | 22 | .sidebar-shown #page-wrapper { 23 | margin-left: 250px; 24 | width: 100%; 25 | } 26 | 27 | .sidebar-shown .sidebar { 28 | width: 250px; 29 | } 30 | 31 | .modal-body .table { 32 | word-break: break-all; 33 | } 34 | 35 | #page_overlay { 36 | width: 100%; 37 | height: 150%; 38 | background: #000; 39 | position: fixed; 40 | top: 0; 41 | left: 0; 42 | z-index: 50; 43 | opacity: 0.6; 44 | display: none; 45 | } 46 | 47 | .data-table td.Empty:before, .data-table td:Empty:before { 48 | content: "Not specified"; 49 | color: #999; 50 | } 51 | 52 | .table-wrapper { 53 | border-left: 1px solid #ddd; 54 | border-right: 1px solid #ddd; 55 | overflow-x: auto; 56 | } 57 | 58 | .table-wrapper .table { 59 | margin-bottom: 0px; 60 | border-left: 0px; 61 | border-right: 0px; 62 | } 63 | 64 | .table-wrapper td:first-child, .table-wrapper th:first-child { 65 | border-left: 0px; 66 | } 67 | 68 | .table-wrapper td:last-child, .table-wrapper th:last-child { 69 | border-right: 0px; 70 | } 71 | 72 | .stats-table { 73 | font-size: 11px; 74 | } 75 | 76 | .stats-table td:empty:before { 77 | content: "N/A"; 78 | color: #999; 79 | } 80 | 81 | .no-border { 82 | border: none; 83 | } 84 | 85 | /* device view */ 86 | .panel-default>.panel-heading { 87 | background-color: inherit; 88 | } 89 | 90 | .panel { 91 | margin-bottom: 15px; 92 | } 93 | 94 | .panel-general { 95 | border: none; 96 | } 97 | 98 | .panel-body { 99 | /* padding-top: 10px; 100 | padding-bottom: 10px; */ 101 | overflow-x: auto; 102 | } 103 | 104 | .sidebar-nav a { 105 | cursor: pointer; 106 | } 107 | 108 | /* for d3 */ 109 | 110 | .switch_view { 111 | border: 1px solid #dddddd; 112 | padding: 7px; 113 | } 114 | 115 | .switch_view svg { 116 | margin-right: 7px; 117 | } 118 | 119 | .d3-tip.n { 120 | background-color: black; 121 | padding: 6px; 122 | border-radius: 3px; 123 | } 124 | 125 | .d3-tip { 126 | line-height: 1; 127 | font-weight: bold; 128 | padding: 12px; 129 | background: rgba(0, 0, 0, 0.5); 130 | color: #fff; 131 | border-radius: 2px; 132 | } 133 | 134 | /* Creates a small triangle extender for the tooltip */ 135 | .d3-tip:after { 136 | box-sizing: border-box; 137 | display: inline; 138 | font-size: 10px; 139 | width: 100%; 140 | line-height: 1; 141 | color: rgba(0, 0, 0, 0.8); 142 | content: "\25BC"; 143 | position: absolute; 144 | text-align: center; 145 | } 146 | 147 | /* Style northward tooltips differently */ 148 | .d3-tip.n:after { 149 | margin: -1px 0 0 0; 150 | top: 100%; 151 | left: 0; 152 | } 153 | 154 | 155 | .uuid-links-list .uuid-link:not(:last-child):after { 156 | content: ", "; 157 | } 158 | 159 | .uuid-links-list:not(:empty) + .uuid-list-details:before { 160 | content: ", "; 161 | } 162 | 163 | .uuid-list-details a { 164 | font-weight: bold; 165 | } 166 | 167 | .iface { 168 | cursor : pointer; 169 | } 170 | 171 | .iface.up { 172 | fill : green; 173 | } 174 | 175 | .iface.down { 176 | fill : grey; 177 | } 178 | 179 | .iface.fail { 180 | fill : red; 181 | } 182 | 183 | .iface.error { 184 | stroke: orange; 185 | stroke-width: 2px; 186 | } 187 | 188 | /* axilera style */ 189 | .nav>li { 190 | background: #3c3c3c !important; 191 | } 192 | 193 | .nav>li>a { 194 | color: #d9d9d9 !important; 195 | } 196 | 197 | .nav>li :hover { 198 | background: #303030 !important; 199 | } 200 | 201 | .nav.nav-tabs>li>a:active, 202 | .nav>li>a.active { 203 | background: #00a3d8 !important; 204 | } 205 | 206 | .nav-tabs>li.active>a, .nav-tabs>li.active>a:hover, .nav-tabs>li.active>a:focus { 207 | background: #00a3d8 !important; 208 | } 209 | 210 | .nav>li:active, 211 | .nav>li>a:focus { 212 | background: #00a3d8 !important; 213 | } 214 | 215 | .panel-info { 216 | border-color: #007da5; 217 | } 218 | .panel-info>.panel-heading { 219 | background-color: #00a3d8 !important; 220 | color: #d9d9d9 !important; 221 | } 222 | 223 | a:focus, 224 | li.selected:focus { 225 | outline-color: #007da5 !important; 226 | } 227 | -------------------------------------------------------------------------------- /routes/bridge_stats_route.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Open vMonitor is a WEB-based tool for monitoring and troubleshooting Open vSwitch 3 | * Copyright (C) 2014-2016 PLVision 4 | * Ihor Chumak, Roman Gotsiy 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | 16 | * You should have received a copy of the GNU Affero General Public License 17 | * along with this program. If not, see . 18 | 19 | * PLVision, developers@plvision.eu 20 | */ 21 | 22 | var client_manager = require("../lib/client-manager.js")(); 23 | var utils = require("../lib/utils.js"); 24 | var extend = require("../lib/extend.js"); 25 | //var extend = require("extend") 26 | 27 | module.exports.stats_route = function (req, res, next) { 28 | var db_name = req.session.db_name; 29 | var addr = req.session.connection; 30 | var bridge_name = req.params.bridge_name; 31 | 32 | var userid = "test"; 33 | client_manager.client(userid, addr, function (err, client) { 34 | var stats = {}; 35 | client.database(db_name).tableByName("Bridge").find_by_name(bridge_name).ports().each(function (port, next_port) { 36 | port.data(["name", "statistics", "interfaces"], function (err, port_data) { 37 | if (err) { 38 | return next_port(err); 39 | } 40 | if (port_data.statistics[1].length) { 41 | stats[port_data.name] = utils.map_array2map(port_data.statistics); 42 | }; 43 | // TODO: bound port is temporary skipped 44 | if (port_data.interfaces[0] !== "uuid") { 45 | return next_port(); 46 | } 47 | 48 | var uuid = port_data.interfaces[1]; 49 | port.interfaces().get(uuid).data(["statistics"], function (err, iface_data) { 50 | if (err) 51 | return next_port(err); 52 | stats[port_data.name] = extend(stats[port_data.name], 53 | utils.map_array2map(iface_data.statistics)); 54 | next_port(); 55 | }); 56 | } 57 | ); 58 | }, function (err) { 59 | res.send(JSON.stringify(stats)).end(); 60 | }); 61 | }); 62 | }; 63 | -------------------------------------------------------------------------------- /routes/custom_data_loaders.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Open vMonitor is a WEB-based tool for monitoring and troubleshooting Open vSwitch 3 | * Copyright (C) 2014-2016 PLVision 4 | * Ihor Chumak, Roman Gotsiy 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | 16 | * You should have received a copy of the GNU Affero General Public License 17 | * along with this program. If not, see . 18 | 19 | * PLVision, developers@plvision.eu 20 | */ 21 | 22 | 23 | /* 24 | * file with extra-data loaders for some tables 25 | * USED BY: table_routes_info.js 26 | */ 27 | var utils = require("../lib/utils.js"); 28 | var asyncForEach = require("async").forEach; 29 | 30 | /* extra-data loader for bridge table. Loads bridge controllers info */ 31 | var object_bridge_data_loader = function (bridge_data, db, callback) { 32 | var additiona_data = {}; 33 | if (bridge_data instanceof Array) { 34 | bridge_data = bridge_data[0]; 35 | } 36 | db.bridges().get(bridge_data._uuid).controllers().data(["is_connected", "target", "status"], function (err, controllers) { 37 | if (err) return callback(err); 38 | for (var i = 0; i < controllers.length; i++) { 39 | controllers[i].status = utils.map_array2map(controllers[i].status); 40 | } 41 | additiona_data.controllers = controllers; 42 | callback(null, additiona_data); 43 | }); 44 | }; 45 | 46 | /* extra-data loader for port/ports table. Loads interfaces and qoses info */ 47 | var ports_data_loader = function (ports_data, db, callback) { 48 | var additional_data = { interfaces: {} }; 49 | if (!(ports_data instanceof Array)) { 50 | ports_data = [ports_data]; 51 | } 52 | 53 | asyncForEach(ports_data, function (port, next_port) { 54 | db.ports().get(port._uuid).interfaces().data(["name", "statistics"], function (err, interface_data) { 55 | if (err) return next_port(err); 56 | additional_data.interfaces[port.name] = interface_data; 57 | next_port(); 58 | }); 59 | }, function (err) { 60 | callback(err, additional_data); 61 | }); 62 | 63 | }; 64 | 65 | var bridges_data_loader = function (bridges_data, db, callback) { 66 | var additional_data = { controllers: {} }; 67 | if (!(bridges_data instanceof Array)) { 68 | bridges_data = [bridges_data]; 69 | } 70 | 71 | asyncForEach(bridges_data, function (bridge, next_bridge) { 72 | db.bridges().get(bridge._uuid).controllers().data(["is_connected", "target", "status"], function (err, controller_data) { 73 | if (err) return next_bridge(err); 74 | additional_data.controllers[bridge.name] = controller_data; 75 | next_bridge(); 76 | }); 77 | }, function (err) { 78 | callback(err, additional_data); 79 | }); 80 | 81 | }; 82 | 83 | 84 | module.exports = { 85 | object_bridge_data_loader: object_bridge_data_loader, 86 | ports_data_loader: ports_data_loader, 87 | bridges_data_loader: bridges_data_loader 88 | }; 89 | -------------------------------------------------------------------------------- /routes/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Open vMonitor is a WEB-based tool for monitoring and troubleshooting Open vSwitch 3 | * Copyright (C) 2014-2016 PLVision 4 | * Ihor Chumak, Roman Gotsiy 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | 16 | * You should have received a copy of the GNU Affero General Public License 17 | * along with this program. If not, see . 18 | 19 | * PLVision, developers@plvision.eu 20 | */ 21 | 22 | //var extend = require("extend"); 23 | var extend = require("../lib/extend.js"); 24 | var table_routes = require("./table_routes.js"); 25 | var bridge_stats_route = require("./bridge_stats_route.js"); 26 | 27 | extend(module.exports, table_routes, bridge_stats_route); 28 | -------------------------------------------------------------------------------- /routes/table_routes.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Open vMonitor is a WEB-based tool for monitoring and troubleshooting Open vSwitch 3 | * Copyright (C) 2014-2016 PLVision 4 | * Ihor Chumak, Roman Gotsiy 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | 16 | * You should have received a copy of the GNU Affero General Public License 17 | * along with this program. If not, see . 18 | 19 | * PLVision, developers@plvision.eu 20 | */ 21 | 22 | var client_manager = require("../lib/client-manager.js")(); 23 | var utils = require("../lib/utils.js"); 24 | var custom_data_loaders = require("./custom_data_loaders.js"); 25 | var routes_info = require("./table_routes_info.js"); 26 | 27 | 28 | /* Abstract table route. 29 | * basic routine for rendering entities from db 30 | * is customisated by parameters: 31 | * - load_data: async function that loads data 32 | * - get_table_title: function that build table title 33 | * - is_table: bool responsible for search table details if routes_info 34 | */ 35 | var AbstractTableRoute = function (load_data, get_table_title, is_table) { 36 | this.load_data = load_data; 37 | this.get_table_title = get_table_title; 38 | this.is_table = is_table; 39 | }; 40 | 41 | AbstractTableRoute.prototype.route = function (req, res, next) { 42 | var self = this; 43 | var addr = req.session.connection; 44 | var db_name = req.session.db_name; 45 | var table = req.params.table; 46 | var userid = req.session.userid; 47 | 48 | client_manager.client(userid, addr, function (err, client) { 49 | if (err) { 50 | return next(err); 51 | } 52 | var db = client.database(db_name); 53 | try { 54 | self.load_data(db, req, function (err, data) { 55 | if (err) 56 | return next(err); 57 | 58 | var table_schema = client.db_schema(db_name).tables[table]; 59 | var custom_tmpl; 60 | if (self.is_table) 61 | custom_tmpl = routes_info.custom_templates.tables[table]; 62 | else 63 | custom_tmpl = routes_info.custom_templates.objects[table]; 64 | 65 | var additional_data_loader; 66 | var template; 67 | if (custom_tmpl) { 68 | template = custom_tmpl.template; 69 | additional_data_loader = custom_tmpl.additional_data_loader; 70 | } 71 | 72 | if (!template) 73 | template = ((!self.is_table) || routes_info.vtables.includes(table)) ? "vtable" : "htable"; 74 | 75 | var title = self.get_table_title(req); 76 | var additional_data; 77 | 78 | var render = function () { 79 | res.renderView(template, { 80 | title: title, 81 | data: data, 82 | table_schema: table_schema, 83 | table: table, 84 | additional_data: additional_data 85 | }); 86 | }; 87 | 88 | if (additional_data_loader) { 89 | additional_data_loader(data, client.database(db_name), function (err, extra_data) { 90 | if (err) { 91 | return next(err); 92 | } 93 | additional_data = extra_data; 94 | render(); 95 | }); 96 | } else { 97 | render(); 98 | } 99 | }); 100 | } catch (e) { 101 | next(e); 102 | } 103 | }); 104 | }; 105 | 106 | /* route for some table */ 107 | var table_route = function (req, res, next) { 108 | var table = req.params.table; 109 | 110 | var load_data_strategy = function (db, req, callback) { 111 | db.tableByName(table).data(callback); 112 | }; 113 | 114 | var get_table_title_strategy = function (req) { 115 | return table + "s list"; 116 | }; 117 | 118 | (new AbstractTableRoute(load_data_strategy, get_table_title_strategy, true)).route(req, res, next); 119 | 120 | }; 121 | 122 | /* route for special object from some table */ 123 | var table_object_route = function (req, res, next) { 124 | var obj_name = req.params.row_name; 125 | var table = req.params.table; 126 | 127 | var load_data_strategy = function (db, req, callback) { 128 | db.tableByName(table).find_by_name(obj_name).data(callback); 129 | }; 130 | 131 | var get_table_title_strategy = function (req) { 132 | return table + " " + obj_name; 133 | }; 134 | 135 | (new AbstractTableRoute(load_data_strategy, get_table_title_strategy, false)).route(req, res, next); 136 | 137 | }; 138 | 139 | /* route for children of special object from some parent table */ 140 | var table_object_subtable_route = function (req, res, next) { 141 | var parent_obj_name = req.params.parent_obj_name; 142 | var parent_table = req.params.parent_table; 143 | var table = req.params.table; 144 | 145 | var load_data_strategy = function (db, req, callback) { 146 | db.tableByName(parent_table).find_by_name(parent_obj_name).tableByName(table).data(callback); 147 | }; 148 | 149 | var get_table_title_strategy = function (req) { 150 | return table + "s of " + parent_table + " " + parent_obj_name; 151 | }; 152 | 153 | (new AbstractTableRoute(load_data_strategy, get_table_title_strategy, true)).route(req, res, next); 154 | 155 | }; 156 | 157 | module.exports = { 158 | table_route: table_route, 159 | table_object_route: table_object_route, 160 | table_object_subtable_route: table_object_subtable_route 161 | }; 162 | -------------------------------------------------------------------------------- /routes/table_routes_info.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Open vMonitor is a WEB-based tool for monitoring and troubleshooting Open vSwitch 3 | * Copyright (C) 2014-2016 PLVision 4 | * Ihor Chumak, Roman Gotsiy 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | 16 | * You should have received a copy of the GNU Affero General Public License 17 | * along with this program. If not, see . 18 | 19 | * PLVision, developers@plvision.eu 20 | */ 21 | 22 | /* 23 | * Contains informations that specify some features of concrete table route 24 | */ 25 | 26 | var custom_data_loaders = require("./custom_data_loaders.js"); 27 | 28 | /* list of tables that should be vertical */ 29 | var vtables = ["Open_vSwitch", "SSL", "IPFIX", "NetFlow", "sFlow"]; 30 | 31 | /* specify for table custom template and if need loader of additional date needed by that template */ 32 | var custom_templates = { 33 | tables: { 34 | "Open_vSwitch": { 35 | template: "custom/table_openvswitch" 36 | }, 37 | "Bridge": { 38 | template: "custom/table_bridge", 39 | additional_data_loader: custom_data_loaders.bridges_data_loader 40 | }, 41 | "Manager": { 42 | template: "custom/table_manager" 43 | }, 44 | "Port": { 45 | template: "custom/table_port", 46 | additional_data_loader: custom_data_loaders.ports_data_loader 47 | }, 48 | /* bug #89 49 | "SSL" : { 50 | template : "custom/table_ssl" 51 | }, */ 52 | "Controller": { 53 | template: "custom/table_controller" 54 | }, 55 | "Interface": { 56 | template: "custom/table_interface" 57 | } 58 | }, 59 | objects: { 60 | "Bridge": { 61 | template: "custom/object_bridge", 62 | additional_data_loader: custom_data_loaders.object_bridge_data_loader 63 | }, 64 | } 65 | }; 66 | 67 | module.exports = { 68 | vtables: vtables, 69 | custom_templates: custom_templates 70 | }; 71 | -------------------------------------------------------------------------------- /views/404.jade: -------------------------------------------------------------------------------- 1 | // 2 | Open vMonitor is a WEB-based tool for monitoring and troubleshooting Open vSwitch 3 | Copyright (C) 2014-2016 PLVision 4 | Ihor Chumak, Roman Gotsiy 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Affero General Public License as 8 | published by the Free Software Foundation, either version 3 of the 9 | License, or (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Affero General Public License for more details. 15 | 16 | You should have received a copy of the GNU Affero General Public License 17 | along with this program. If not, see . 18 | 19 | PLVision, developers@plvision.eu 20 | 21 | extends general_table 22 | 23 | block tablecontent 24 | .row 25 | .col-lg-12 26 | h1.text-warning.page-header 404 Not found 27 | h4 Route 28 | strong #{route} 29 | | was not found 30 | -------------------------------------------------------------------------------- /views/500.jade: -------------------------------------------------------------------------------- 1 | // 2 | Open vMonitor is a WEB-based tool for monitoring and troubleshooting Open vSwitch 3 | Copyright (C) 2014-2016 PLVision 4 | Ihor Chumak, Roman Gotsiy 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Affero General Public License as 8 | published by the Free Software Foundation, either version 3 of the 9 | License, or (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Affero General Public License for more details. 15 | 16 | You should have received a copy of the GNU Affero General Public License 17 | along with this program. If not, see . 18 | 19 | PLVision, developers@plvision.eu 20 | 21 | h1 500 Internal Server Error 22 | 23 | h4 Reload button may correct the error. If the error persists, connection to ovsdb-server may be lost: try to 24 | a(href="/logout") disconnect 25 | | and connect again. 26 | -------------------------------------------------------------------------------- /views/alerts.jade: -------------------------------------------------------------------------------- 1 | //- 2 | Open vMonitor is a WEB-based tool for monitoring and troubleshooting Open vSwitch 3 | Copyright (C) 2014-2016 PLVision 4 | Ihor Chumak, Roman Gotsiy 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Affero General Public License as 8 | published by the Free Software Foundation, either version 3 of the 9 | License, or (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Affero General Public License for more details. 15 | 16 | You should have received a copy of the GNU Affero General Public License 17 | along with this program. If not, see . 18 | 19 | PLVision, developers@plvision.eu 20 | 21 | extends general_table 22 | 23 | block tablecontent 24 | .row 25 | .col-lg-12 26 | h1.page-header Alerts 27 | ul.nav.nav-tabs(role='tablist') 28 | li.active 29 | a(href='#plates', role='tab', data-toggle='tab') Tiles view 30 | //li 31 | a(href='#table', role='tab', data-toggle='tab') Table view 32 | div.tab-content 33 | div#plates.tab-pane.active(style="padding-top:10px;") 34 | .panel.panel-info 35 | .panel-heading 36 | strong Alerts 37 | .panel-body 38 | .row 39 | .col-lg-12 40 | h4 Available only in Full Version 41 | table.table.data-table.table-bordered 42 | tbody 43 | - var idx = 0 44 | each update in updates 45 | - var key = Object.keys(update)[0]; 46 | - var record = update[key] 47 | - var actions = ''; 48 | - var uuids = Object.keys(record); 49 | tr 50 | td #{idx} 51 | td 52 | each uuid in uuids 53 | - var name = record[uuid].old ? record[uuid].old.name : record[uuid].new.name; 54 | - var method = record[uuid].old ? 'removed' : 'created'; 55 | - actions += "\"" + name + "\" " + method + " "; 56 | | #{key}: #{actions} 57 | - idx++; 58 | //.panel-footer 59 | //a(href='/alerts/clear') Clear 60 | div#table.tab-pane 61 | .table-wrapper 62 | -------------------------------------------------------------------------------- /views/custom/object_bridge.jade: -------------------------------------------------------------------------------- 1 | //- 2 | Open vMonitor is a WEB-based tool for monitoring and troubleshooting Open vSwitch 3 | Copyright (C) 2014-2016 PLVision 4 | Ihor Chumak, Roman Gotsiy 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Affero General Public License as 8 | published by the Free Software Foundation, either version 3 of the 9 | License, or (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Affero General Public License for more details. 15 | 16 | You should have received a copy of the GNU Affero General Public License 17 | along with this program. If not, see . 18 | 19 | PLVision, developers@plvision.eu 20 | 21 | extends ../general_table 22 | include ../include/mixins 23 | 24 | block tablecontent 25 | - bridge = data 26 | if (bridge instanceof Array) 27 | - bridge = bridge[0] 28 | 29 | .row 30 | .col-lg-12 31 | h1.page-header Bridge: #{bridge.name} 32 | ul.nav.nav-tabs(role='tablist') 33 | li.active 34 | a(href='#plates', role='tab', data-toggle='tab') Tiles view 35 | li 36 | a(href='#table', role='tab', data-toggle='tab') Table view 37 | 38 | div.tab-content 39 | div#plates.tab-pane.active(style="padding-top:10px;") 40 | .row 41 | .col-lg-5 42 | .row 43 | .col-lg-12 44 | .panel.panel-info 45 | .panel-heading 46 | strong Info 47 | .panel-body 48 | .row 49 | .col-lg-12 50 | table.table.data-table 51 | tbody 52 | tr 53 | td Datapath ID 54 | td #{bridge['datapath_id']} 55 | tr 56 | td Fail mode 57 | +comma_separated_set_td(bridge['fail_mode']) 58 | tr 59 | td OpenFlow Versions 60 | +comma_separated_set_td(bridge['protocols']) 61 | tr 62 | td Flood VLANs 63 | +comma_separated_set_td(bridge['flood_vlans']) 64 | tr 65 | td STP Enable 66 | td #{bridge['stp_enable']} 67 | .row 68 | .col-lg-12 69 | .panel.panel-info 70 | - controllers = additional_data.controllers 71 | .panel-heading 72 | strong Bridge Controllers 73 | .panel-body 74 | .row 75 | .col-lg-12 76 | table.table.data-table 77 | thead 78 | th Target 79 | th Connected 80 | th State 81 | th 82 | |Connected time 83 | h6 (in seconds) 84 | th Last error 85 | tbody 86 | each controller in controllers 87 | tr 88 | td #{controller.target} 89 | - var cls = (controller.is_connected) ? "text-success" : "text-danger" 90 | td(class="#{cls}") #{controller.is_connected} 91 | td #{controller.status.state} 92 | td #{controller.status.sec_since_connect} 93 | td.text-danger #{controller.status.last_error} 94 | .col-lg-7 95 | .panel.panel-info 96 | .panel-heading 97 | strong Ports 98 | .panel-body 99 | .row 100 | .col-lg-6 101 | .row 102 | .col-lg-6 103 | +children_list("Ports", bridge.ports, "Bridge", "Port", bridge.name) 104 | .row 105 | .col-lg-12 106 | h4 Switch view 107 | .switch_view(id="br_#{bridge.name}") 108 | .col-lg-6 109 | .row 110 | .col-lg-12 111 | h4 Ports statistics 112 | div.scrollbar 113 | table.table#port_stats.stats-table 114 | div#table.tab-pane 115 | .table-wrapper 116 | include ../include/vtable_inc 117 | 118 | script. 119 | $(document).ready(function() { 120 | draw_switch_view(".switch_view", function(){}); 121 | display_port_stats("#port_stats", "#{bridge.name}"); 122 | }); 123 | -------------------------------------------------------------------------------- /views/custom/table_bridge.jade: -------------------------------------------------------------------------------- 1 | //- 2 | Open vMonitor is a WEB-based tool for monitoring and troubleshooting Open vSwitch 3 | Copyright (C) 2014-2016 PLVision 4 | Ihor Chumak, Roman Gotsiy 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Affero General Public License as 8 | published by the Free Software Foundation, either version 3 of the 9 | License, or (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Affero General Public License for more details. 15 | 16 | You should have received a copy of the GNU Affero General Public License 17 | along with this program. If not, see . 18 | 19 | PLVision, developers@plvision.eu 20 | 21 | extends ../general_table 22 | include ../include/mixins 23 | 24 | block tablecontent 25 | .row 26 | .col-lg-12 27 | h1.page-header Bridges list 28 | ul.nav.nav-tabs(role='tablist') 29 | li.active 30 | a(href='#plates', role='tab', data-toggle='tab') Tiles view 31 | li 32 | a(href='#table', role='tab', data-toggle='tab') Table view 33 | div.tab-content 34 | div#plates.tab-pane.active(style="padding-top:10px;") 35 | - for (var row = 0; row < data.length / 3; row++) 36 | .row 37 | - for (var index = row * 3; index < row*3+3 && index < data.length; index++) 38 | - bridge = data[index]; 39 | .col-lg-4 40 | .panel.panel-info 41 | .panel-heading 42 | strong #{bridge.name} 43 | .panel-body 44 | .row 45 | .col-lg-12 46 | h4 Basic info 47 | table.table.data-table 48 | tbody 49 | tr 50 | td Datapath ID 51 | td #{bridge['datapath_id']} 52 | tr 53 | td Fail mode 54 | +comma_separated_set_td(bridge['fail_mode']) 55 | tr 56 | td OpenFlow Versions 57 | +comma_separated_set_td(bridge['protocols']) 58 | tr 59 | td Ports 60 | td 61 | +comma_separated_uuids_links(bridge['ports']) 62 | tr 63 | td Mirrors 64 | td 65 | +comma_separated_uuids_links(bridge['mirrors']) 66 | .row 67 | .col-lg-12 68 | h4 Switch view 69 | .switch_view(id="br_#{bridge.name}") 70 | .row 71 | .col-lg-12 72 | h4 Controllers 73 | table.table.data-table 74 | - controllers = additional_data.controllers[bridge.name] 75 | each controller in controllers 76 | tr 77 | td #{controller.target} 78 | - var cls = (controller.is_connected) ? "text-success" : "text-danger" 79 | td(class="#{cls}") #{controller.is_connected ? "Connected" : "Disconected"} 80 | -//.row 81 | -//.col-lg-6 82 | -//+children_list("Ports", bridge.ports, "Bridge", "Port", bridge.name) 83 | -//.col-lg-6 84 | -//+children_list("Mirrors", bridge.mirrors, "Bridge", "Mirror", bridge.name) 85 | 86 | .panel-footer 87 | a(href="/Bridge/#{bridge.name}") Details 88 | div#table.tab-pane 89 | .table-wrapper 90 | include ../include/htable_inc 91 | 92 | script. 93 | $(document).ready(function() { 94 | draw_switch_view(".switch_view"); 95 | }); 96 | -------------------------------------------------------------------------------- /views/custom/table_controller.jade: -------------------------------------------------------------------------------- 1 | //- 2 | Open vMonitor is a WEB-based tool for monitoring and troubleshooting Open vSwitch 3 | Copyright (C) 2014-2016 PLVision 4 | Ihor Chumak, Roman Gotsiy 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Affero General Public License as 8 | published by the Free Software Foundation, either version 3 of the 9 | License, or (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Affero General Public License for more details. 15 | 16 | You should have received a copy of the GNU Affero General Public License 17 | along with this program. If not, see . 18 | 19 | PLVision, developers@plvision.eu 20 | 21 | extends ../general_table 22 | include ../include/mixins 23 | 24 | block tablecontent 25 | .row 26 | .col-lg-12 27 | h1.page-header #{title} 28 | ul.nav.nav-tabs(role='tablist') 29 | li.active 30 | a(href='#plates', role='tab', data-toggle='tab') Tiles view 31 | li 32 | a(href='#table', role='tab', data-toggle='tab') Table view 33 | div.tab-content 34 | div#plates.tab-pane.active(style="padding-top:10px;") 35 | .row 36 | each controller in data 37 | .col-lg-4 38 | .panel.panel-info 39 | .panel-heading 40 | strong Controller 41 | .panel-body 42 | .row 43 | .col-lg-12 44 | h4 Basic info 45 | table.table.data-table 46 | tbody 47 | tr 48 | td Target 49 | td #{controller['target']} 50 | tr 51 | td Connected 52 | td #{controller['is_connected']} 53 | tr 54 | td Local Network Mask 55 | +comma_separated_set_td(controller['local_netmask']) 56 | tr 57 | td Local Gateway 58 | +comma_separated_set_td(controller['local_gateway']) 59 | tr 60 | td Maximum Back-off 61 | +comma_separated_set_td(controller['max_backoff']) 62 | tr 63 | td Local IP 64 | +comma_separated_set_td(controller['local_ip']) 65 | .row 66 | .col-lg-12 67 | h4 Status ( 68 | +set_length(controller['status']) 69 | |) 70 | +comma_separated_list_map(controller['status']) 71 | .row 72 | .col-lg-12 73 | h4 External IDs( 74 | +set_length(controller['external_ids']) 75 | |) 76 | +comma_separated_list_map(controller['external_ids']) 77 | .row 78 | .col-lg-12 79 | h4 Other Configuration( 80 | +set_length(controller['other_config']) 81 | |) 82 | +comma_separated_list_map(controller['other_config']) 83 | .panel-footer 84 | a(href="/Controller#table") Details 85 | div#table.tab-pane 86 | .table-wrapper 87 | include ../include/htable_inc 88 | 89 | script 90 | -------------------------------------------------------------------------------- /views/custom/table_interface.jade: -------------------------------------------------------------------------------- 1 | //- 2 | Open vMonitor is a WEB-based tool for monitoring and troubleshooting Open vSwitch 3 | Copyright (C) 2014-2016 PLVision 4 | Ihor Chumak, Roman Gotsiy 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Affero General Public License as 8 | published by the Free Software Foundation, either version 3 of the 9 | License, or (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Affero General Public License for more details. 15 | 16 | You should have received a copy of the GNU Affero General Public License 17 | along with this program. If not, see . 18 | 19 | PLVision, developers@plvision.eu 20 | 21 | extends ../general_table 22 | include ../include/mixins 23 | 24 | block tablecontent 25 | .row 26 | .col-lg-12 27 | h1.page-header All Interfaces 28 | ul.nav.nav-tabs(role='tablist') 29 | li.active 30 | a(href='#plates', role='tab', data-toggle='tab') Tiles view 31 | li 32 | a(href='#table', role='tab', data-toggle='tab') Table view 33 | div.tab-content 34 | div#plates.tab-pane.active(style="padding-top:10px;") 35 | .row 36 | each interface in data 37 | .col-lg-4 38 | .panel.panel-info 39 | .panel-heading 40 | strong Interface 41 | .panel-body 42 | .row 43 | .col-lg-12 44 | h4 Basic info 45 | table.table.data-table 46 | tbody 47 | tr 48 | td Name 49 | td #{interface['name']} 50 | tr 51 | td MAC 52 | td #{interface['mac_in_use']} 53 | tr 54 | td Link State 55 | td #{interface['link_state']} 56 | tr 57 | td Link Speed 58 | if (interface['link_speed'] instanceof Array) 59 | +comma_separated_set_td(interface['link_speed']) 60 | else 61 | td #{interface['link_speed']} 62 | tr 63 | td Duplex 64 | if (interface['duplex'] instanceof Array) 65 | +comma_separated_set_td(interface['duplex']) 66 | else 67 | td #{interface['duplex']} 68 | tr 69 | td MTU 70 | td #{interface['mtu']} 71 | -//+comma_separated_set_td(interface['connection_mode']) 72 | -//tr 73 | -//td Status 74 | -//td 75 | -//+comma_separated_list_map(interface['statistics']) 76 | .row 77 | .col-lg-12 78 | h4 Status( 79 | +set_length(interface['status']) 80 | |) 81 | +comma_separated_list_map(interface['status']) 82 | .row 83 | .col-lg-12 84 | h4 Statistics( 85 | +set_length(interface['statistics']) 86 | |) 87 | +comma_separated_list_map(interface['statistics']) 88 | 89 | .panel-footer 90 | a(href="/Interface/#{interface['name']}") Details 91 | div#table.tab-pane 92 | .table-wrapper 93 | include ../include/htable_inc 94 | -------------------------------------------------------------------------------- /views/custom/table_manager.jade: -------------------------------------------------------------------------------- 1 | //- 2 | Open vMonitor is a WEB-based tool for monitoring and troubleshooting Open vSwitch 3 | Copyright (C) 2014-2016 PLVision 4 | Ihor Chumak, Roman Gotsiy 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Affero General Public License as 8 | published by the Free Software Foundation, either version 3 of the 9 | License, or (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Affero General Public License for more details. 15 | 16 | You should have received a copy of the GNU Affero General Public License 17 | along with this program. If not, see . 18 | 19 | PLVision, developers@plvision.eu 20 | 21 | extends ../general_table 22 | include ../include/mixins 23 | 24 | block tablecontent 25 | .row 26 | .col-lg-12 27 | h1.page-header Managers 28 | ul.nav.nav-tabs(role='tablist') 29 | li.active 30 | a(href='#plates', role='tab', data-toggle='tab') Tiles view 31 | li 32 | a(href='#table', role='tab', data-toggle='tab') Table view 33 | div.tab-content 34 | div#plates.tab-pane.active(style="padding-top:10px;") 35 | .row 36 | each manager in data 37 | .col-lg-4 38 | .panel.panel-info 39 | .panel-heading 40 | - var state = manager['status'][1].filter(function(element, index) { if (element[0] == 'state') return element[1] })[0]; 41 | - state = state ? state[1] : 'INACTIVE' 42 | - // should be in lowercase 43 | strong Manager (#{state.toLowerCase()}) 44 | .panel-body 45 | .row 46 | .col-lg-12 47 | h4 Basic info 48 | table.table.data-table 49 | tbody 50 | tr 51 | td Target 52 | td #{manager['target']} 53 | tr 54 | td Connection Status 55 | - var connection_status = (manager['is_connected'] == false ? 'Disconnected' : 'Connected'); 56 | td #{connection_status} 57 | 58 | tr 59 | td Maximum Back-off 60 | +comma_separated_set_td(manager['max_backoff']) 61 | tr 62 | td Connection Mode 63 | +comma_separated_set_td(manager['connection_mode']) 64 | tr 65 | td Status 66 | td 67 | +comma_separated_list_map(manager['status']) 68 | .row 69 | .col-lg-12 70 | h4 External IDs( 71 | +set_length(manager['external_ids']) 72 | |) 73 | +comma_separated_list_map(manager['external_ids']) 74 | .row 75 | .col-lg-12 76 | h4 Other Configurations( 77 | +set_length(manager['other_config']) 78 | |) 79 | +comma_separated_list_map(manager['other_config']) 80 | div#table.tab-pane 81 | .table-wrapper 82 | include ../include/htable_inc 83 | -------------------------------------------------------------------------------- /views/custom/table_openvswitch.jade: -------------------------------------------------------------------------------- 1 | //- 2 | Open vMonitor is a WEB-based tool for monitoring and troubleshooting Open vSwitch 3 | Copyright (C) 2014-2016 PLVision 4 | Ihor Chumak, Roman Gotsiy 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Affero General Public License as 8 | published by the Free Software Foundation, either version 3 of the 9 | License, or (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Affero General Public License for more details. 15 | 16 | You should have received a copy of the GNU Affero General Public License 17 | along with this program. If not, see . 18 | 19 | PLVision, developers@plvision.eu 20 | 21 | extends ../general_table 22 | include ../include/mixins 23 | 24 | block tablecontent 25 | .row 26 | .col-lg-12 27 | h1.page-header Open_vSwitch 28 | ul.nav.nav-tabs(role='tablist') 29 | li.active 30 | a(href='#plates', role='tab', data-toggle='tab') Tiles view 31 | li 32 | a(href='#table', role='tab', data-toggle='tab') Table view 33 | div.tab-content 34 | div#plates.tab-pane.active(style="padding-top:10px;") 35 | .row 36 | - openvswitch = data[0]; 37 | .col-lg-6 38 | .panel.panel-info 39 | .panel-heading 40 | strong Open_vSwitch 41 | .panel-body 42 | .row 43 | .col-lg-12 44 | h4 Basic info 45 | table.table.data-table 46 | tbody 47 | tr 48 | td System Version 49 | +comma_separated_set_td(openvswitch['system_version']) 50 | tr 51 | td System Type 52 | +comma_separated_set_td(openvswitch['system_type']) 53 | tr 54 | td Bridges 55 | td 56 | +comma_separated_uuids_links(openvswitch['bridges']) 57 | tr 58 | td Manager Options 59 | td 60 | +comma_separated_uuids_links(openvswitch['manager_options']) 61 | tr 62 | td OVS Version 63 | +comma_separated_set_td(openvswitch['ovs_version']) 64 | tr 65 | td SSL 66 | td 67 | +comma_separated_uuids_links(openvswitch['ssl'], true) 68 | tr 69 | td Database Version 70 | +comma_separated_set_td(openvswitch['db_version']) 71 | tr 72 | td Current Configuration 73 | td #{openvswitch['cur_cfg']} 74 | 75 | -//+children_list("Bridges", openvswitch.bridges, "Open_vSwitch", "Bridge") 76 | -//+children_list("Managers", openvswitch.manager_options, "Manager", "Manager") 77 | 78 | .row 79 | .col-lg-6 80 | -//+children_list("Mirrors", undefined, "Bridge", "Mirror", openvswitch.name) 81 | .row 82 | .col-lg-6 83 | -//+children_list("Controllers", openvswitch.controller, "Bridge", "Controller", openvswitch.name) 84 | //.panel-footer 85 | //a(href="/Bridge") 86 | div#table.tab-pane 87 | .table-wrapper 88 | include ../include/vtable_inc 89 | -------------------------------------------------------------------------------- /views/custom/table_port.jade: -------------------------------------------------------------------------------- 1 | //- 2 | Open vMonitor is a WEB-based tool for monitoring and troubleshooting Open vSwitch 3 | Copyright (C) 2014-2016 PLVision 4 | Ihor Chumak, Roman Gotsiy 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Affero General Public License as 8 | published by the Free Software Foundation, either version 3 of the 9 | License, or (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Affero General Public License for more details. 15 | 16 | You should have received a copy of the GNU Affero General Public License 17 | along with this program. If not, see . 18 | 19 | PLVision, developers@plvision.eu 20 | 21 | extends ../general_table 22 | include ../include/mixins 23 | 24 | block tablecontent 25 | .row 26 | .col-lg-12 27 | h1.page-header #{title} 28 | ul.nav.nav-tabs(role='tablist') 29 | li.active 30 | a(href='#plates', role='tab', data-toggle='tab') Tiles view 31 | li 32 | a(href='#table', role='tab', data-toggle='tab') Table view 33 | div.tab-content 34 | div#plates.tab-pane.active(style="padding-top:10px;") 35 | .row 36 | .col-lg-6 37 | - ports_general_info_cols = { "name" : {"caption" : "Port name"}, "vlan_mode" : {"caption" : "VLAN mode"}, "trunks": { "caption": "Trunks"}, "tag" : {"caption": "Tag"}, "lacp" : {"caption" : "LACP"}, "mac" : {"caption" : "MAC"} } 38 | .panel.panel-info 39 | .panel-heading 40 | strong General info 41 | .panel-body 42 | div.table-wrapper 43 | table.table.table-bordered.data-table 44 | thead 45 | tr 46 | each col_detail, col in ports_general_info_cols 47 | th #{col_detail.caption} 48 | tbody 49 | each port, idx in data 50 | tr 51 | each col_detail, col in ports_general_info_cols 52 | td 53 | +display_complex_data(port[col], col_detail.caption) 54 | .col-lg-6 55 | .panel.panel-info 56 | - port_interfaces = additional_data.interfaces 57 | .panel-heading 58 | strong Interfaces of ports 59 | .panel-body 60 | div.table-wrapper 61 | table.table.table-bordered.data-table 62 | thead 63 | tr 64 | th Port 65 | th Interfaces 66 | tbody 67 | each port, idx in data 68 | tr 69 | td #{port.name} 70 | td 71 | +comma_separated_uuids_links(port.interfaces) 72 | .row 73 | .col-lg-6 74 | .panel.panel-info 75 | .panel-heading 76 | strong Ports statistics 77 | .panel-body 78 | div.scrollbar 79 | table.table#port_stats.stats-table 80 | 81 | div#table.tab-pane 82 | .table-wrapper 83 | include ../include/htable_inc 84 | 85 | script. 86 | $(document).ready(function() { 87 | display_port_stats("#port_stats"); 88 | }); 89 | -------------------------------------------------------------------------------- /views/custom/table_ssl.jade: -------------------------------------------------------------------------------- 1 | //- 2 | Open vMonitor is a WEB-based tool for monitoring and troubleshooting Open vSwitch 3 | Copyright (C) 2014-2016 PLVision 4 | Ihor Chumak, Roman Gotsiy 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Affero General Public License as 8 | published by the Free Software Foundation, either version 3 of the 9 | License, or (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Affero General Public License for more details. 15 | 16 | You should have received a copy of the GNU Affero General Public License 17 | along with this program. If not, see . 18 | 19 | PLVision, developers@plvision.eu 20 | 21 | extends ../general_table 22 | include ../include/mixins 23 | 24 | block tablecontent 25 | .row 26 | .col-lg-12 27 | h1.page-header SSL 28 | ul.nav.nav-tabs(role='tablist') 29 | li.active 30 | a(href='#plates', role='tab', data-toggle='tab') Tiles view 31 | li 32 | a(href='#table', role='tab', data-toggle='tab') Table view 33 | div.tab-content 34 | div#plates.tab-pane.active(style="padding-top:10px;") 35 | .row 36 | each ssl in data 37 | .col-lg-6 38 | .panel.panel-info 39 | .panel-heading 40 | strong SSL 41 | .panel-body 42 | .row 43 | .col-lg-12 44 | h4 Basic info 45 | table.table.data-table 46 | tbody 47 | tr 48 | td CA Cert 49 | td #{ssl['ca_cert']} 50 | tr 51 | td Bootstrap CA Cert 52 | - var is_empty = ssl['bootstrap_ca_cert'] === false 53 | td 54 | - if (is_empty) 55 | | false 56 | - else 57 | | #{ssl['bootstrap_ca_cert']} 58 | tr 59 | td Private Key 60 | td #{ssl['private_key']} 61 | tr 62 | td Certificate 63 | td #{ssl['certificate']} 64 | 65 | .row 66 | .col-lg-12 67 | h4 External IDs( 68 | +set_length(ssl['external_ids']) 69 | |) 70 | +comma_separated_list_map(ssl['external_ids']) 71 | //.panel-footer 72 | //a(href="/SSL#table") Details 73 | div#table.tab-pane 74 | .table-wrapper 75 | include ../include/vtable_inc 76 | 77 | script 78 | -------------------------------------------------------------------------------- /views/dashboard.jade: -------------------------------------------------------------------------------- 1 | //- 2 | Open vMonitor is a WEB-based tool for monitoring and troubleshooting Open vSwitch 3 | Copyright (C) 2014-2016 PLVision 4 | Ihor Chumak, Roman Gotsiy 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Affero General Public License as 8 | published by the Free Software Foundation, either version 3 of the 9 | License, or (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Affero General Public License for more details. 15 | 16 | You should have received a copy of the GNU Affero General Public License 17 | along with this program. If not, see . 18 | 19 | PLVision, developers@plvision.eu 20 | 21 | extends index 22 | block maincontent 23 | .devices 24 | each br in bridges 25 | - //var led_dimensions = { width : 16, height : 8 }; 26 | - //var len = br.ports.length < 10 ? 10 : br.ports.length % 2 ? br.ports.length + 1 : br.ports.length; 27 | - //var panel_dimensions = { width : ((len / 2 >> 0) + 1 )*3 + (len / 2 >> 0)*led_dimensions.width + 2, height : 2*3 + 2*led_dimensions.height + 8 + 2 }; 28 | - //var view_dimensions = { width : panel_dimensions.width + 2*8, height : panel_dimensions.height + 2*12 }; 29 | 30 | .panel.panel-default 31 | .panel-heading 32 | |Bridge: #{br.name} 33 | .panel-body 34 | .switch_view(id="#{br.name}") 35 | - //svg(height="#{view_dimensions.height}", width="#{view_dimensions.width}") 36 | - //rect(x=0, y=0, width=view_dimensions.width, height=view_dimensions.height, style="fill:rgb(247,244,251); stroke: black;") 37 | - //rect(x=8, y=12, width=panel_dimensions.width, height=panel_dimensions.height, style="fill:rgb(247,244,251); stroke: black;") 38 | - //var x = 12 39 | - //var y = 16 40 | - //num = 0 41 | - //var line = (len / 2 >> 0); 42 | - //each port in br.ports 43 | - //if (num == line) 44 | - //num = 0; 45 | - //y += 2*led_dimensions.height; 46 | - //x = 12; 47 | - //else 48 | - //var color = port.link_state == "up" ? "rgb(118,181,18)" : "rgb(228,77,58)"; 49 | - //rect.led(x=x, y=y, width=led_dimensions.width, height=led_dimensions.height, style="fill: #{color}; stroke: black;") 50 | - //title 51 | - //|#{port} 52 | - //x += led_dimensions.width + 3; 53 | - //num++; 54 | script. 55 | $(document).ready(function() { 56 | draw_switch_view(".switch_view"); 57 | }); 58 | -------------------------------------------------------------------------------- /views/enter.jade: -------------------------------------------------------------------------------- 1 | //- 2 | Open vMonitor is a WEB-based tool for monitoring and troubleshooting Open vSwitch 3 | Copyright (C) 2014-2016 PLVision 4 | Ihor Chumak, Roman Gotsiy 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Affero General Public License as 8 | published by the Free Software Foundation, either version 3 of the 9 | License, or (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Affero General Public License for more details. 15 | 16 | You should have received a copy of the GNU Affero General Public License 17 | along with this program. If not, see . 18 | 19 | PLVision, developers@plvision.eu 20 | 21 | doctype html 22 | html(lang="en") 23 | head 24 | meta(charset="utf-8") 25 | meta(http-equiv="X-UA-Compatible", content="IE=edge") 26 | meta(name="viewport", content="width=device-width, initial-scale=1") 27 | meta(name="description", content="") 28 | meta(name="author", content="") 29 | title= title 30 | link(rel='stylesheet', href='/stylesheets/bootstrap.min.css') 31 | link(rel='stylesheet', href='/stylesheets/default.css') 32 | link(rel='stylesheet', href='/stylesheets/sweet-alert.css') 33 | script(src="/js/jquery-1.11.1.min.js") 34 | script(src="/js/bootstrap.min.js") 35 | script(src="/js/sweet-alert.min.js") 36 | 37 | body.cable 38 | #wrapper 39 | #sidebar_wrapper 40 | .sidebar-welcome 41 | div PLVision 42 | .project-name Open_vMonitor 43 | #sidebar_choose_ip.well.well-sm.nice Enter Credentials 44 | .well.well-sm.nice(style="margin-top: 72px; padding-right: 0px;") Enter OVSDB IP 45 | .bottom-line 46 | -// copyright here 47 | 48 | #page_content_wrapper 49 | .arrow-right 50 | .container-fluid 51 | .sidebar-welcome.mobile-version.col-lg-6 52 | div PLVision 53 | .project-name OVS Open_vMonitor 54 | .row-fluid#row 55 | .col-lg-6 56 | .row 57 | .col-lg-12 58 | .input-group 59 | input.form-control.nice.font18#username(type="text", placeholder="login", value="#{username}") 60 | input.form-control.nice.font18#password(type="password", placeholder="password", style="margin-top: 2px;") 61 | input.form-control.nice.font18#database_ip_address(type="text", value="#{connection}", placeholder="Database IP Address", style="margin-top: 6px;") 62 | label.mobile-version 63 | //input#secure(type="checkbox") 64 | //| Use secured connection 65 | button.btn.btn-default.font18.radius-fixed.pull-right#connect_btn(type="submit", style="margin-top: 6px;") Connect 66 | 67 | ul.bottom-line.links-navbar 68 | li 69 | a(href="http://plvision.eu", target="_blank") About 70 | li 71 | a(href="http://plvision.eu/contact-us/", target="_blank") Contacts 72 | script. 73 | $(document).ready(function() { 74 | $("#database_ip_address, #username, #password, #secure").bind("keypress", {}, function(event) { 75 | var code = (event.keyCode ? event.keyCode : event.which); 76 | 77 | if (code == 13) { /* Enter keycode */ 78 | event.preventDefault(); 79 | 80 | $("#connect_btn").click(); 81 | } 82 | }); 83 | 84 | $("#connect_btn").click(function() { 85 | var db_ip = $("#database_ip_address").val(); 86 | var username = $("#username").val(); 87 | var password = $("#password").val(); 88 | var secure = $("#secure").prop("checked"); 89 | 90 | $("#connect_btn").addClass("loading").attr("disabled", true); 91 | 92 | $.ajax( { 93 | type : "POST", 94 | url: "/enter", 95 | data: { 96 | "database_ip_address" : db_ip, 97 | "username" : username, 98 | "password" : password, 99 | "secure" : secure 100 | }, 101 | timeout : 15000 102 | }) 103 | .done(function() { 104 | window.location.href = "/"; 105 | }) 106 | .fail(function(e, t, m) { 107 | var error_text; 108 | 109 | switch (e.status) { 110 | case 403: 111 | error_text = "Authentication error."; 112 | break; 113 | default: 114 | error_text = "Can not connect to server."; 115 | if (t === "timeout") { 116 | error_text += " Timeout error."; 117 | } 118 | break; 119 | } 120 | 121 | if (e.responseJSON && e.responseJSON.message) { 122 | error_text += " " + e.responseJSON.message; 123 | } 124 | 125 | swal({ 126 | title: "Error!", 127 | text: error_text, 128 | // type: "error" 129 | }); 130 | }) 131 | .always(function() { 132 | $("#connect_btn").removeClass("loading").removeAttr("disabled"); 133 | }) 134 | }); 135 | }) 136 | -------------------------------------------------------------------------------- /views/error.jade: -------------------------------------------------------------------------------- 1 | //- 2 | Open vMonitor is a WEB-based tool for monitoring and troubleshooting Open vSwitch 3 | Copyright (C) 2014-2016 PLVision 4 | Ihor Chumak, Roman Gotsiy 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Affero General Public License as 8 | published by the Free Software Foundation, either version 3 of the 9 | License, or (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Affero General Public License for more details. 15 | 16 | You should have received a copy of the GNU Affero General Public License 17 | along with this program. If not, see . 18 | 19 | PLVision, developers@plvision.eu 20 | 21 | doctype html 22 | html(lang="en") 23 | head 24 | meta(charset="utf-8") 25 | meta(http-equiv="X-UA-Compatible", content="IE=edge") 26 | meta(name="viewport", content="width=device-width, initial-scale=1") 27 | meta(name="description", content="") 28 | meta(name="author", content="") 29 | title= title 30 | link(rel='stylesheet', href='/stylesheets/bootstrap.min.css') 31 | script(src="/js/bootstrap.min.js") 32 | 33 | body 34 | #page_content_wrapper 35 | .container-fluid 36 | h1= message 37 | h2= error.status 38 | h4= error.message 39 | if showStack 40 | pre #{error.stack} 41 | -------------------------------------------------------------------------------- /views/general_table.jade: -------------------------------------------------------------------------------- 1 | //- 2 | Open vMonitor is a WEB-based tool for monitoring and troubleshooting Open vSwitch 3 | Copyright (C) 2014-2016 PLVision 4 | Ihor Chumak, Roman Gotsiy 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Affero General Public License as 8 | published by the Free Software Foundation, either version 3 of the 9 | License, or (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Affero General Public License for more details. 15 | 16 | You should have received a copy of the GNU Affero General Public License 17 | along with this program. If not, see . 18 | 19 | PLVision, developers@plvision.eu 20 | 21 | extends index 22 | block maincontent 23 | block tablecontent 24 | 25 | block scripts 26 | script. 27 | $(document).ready(function() { 28 | $("a.multiple", maincontent).click(function(event) { 29 | event.preventDefault(); 30 | 31 | var title = $(this).attr('modal-title'); 32 | $("#modal .modal-header > .modal-title").text(title); 33 | var html = $(this).children().html(); 34 | $("#modal .modal-body > table").html(html); 35 | $('#modal').modal({ 36 | backdrop: true, 37 | }); 38 | }); 39 | 40 | document.title = "#{table} - " + "Open_vMonitor"; 41 | 42 | // navigate to tabs 43 | var idx = document.location.href.lastIndexOf('#'); 44 | 45 | if (idx != -1) { 46 | var tab = document.location.href.substr(idx); 47 | $('.nav.nav-tabs a[href=' + tab + ']').tab('show'); 48 | } 49 | 50 | $('a[data-toggle="tab"]').on('shown.bs.tab', function (e) { 51 | window.location.hash = e.target.hash; 52 | window.scroll(0, 0); // FIXME: small workaround 53 | init_custom_scroller(".scrollbar", true); 54 | }); 55 | 56 | // update scoller on window resize 57 | $(window).resize(function() { 58 | update_custom_scroller(".scroller"); 59 | }); 60 | 61 | //$('a[data-toggle="tab"]').on('shown.bs.tab', function (e) { 62 | //e.target // activated tab 63 | //e.relatedTarget // previous tab 64 | //init_custom_scroller(".scrollbar", true); 65 | //}); 66 | }); 67 | -------------------------------------------------------------------------------- /views/htable.jade: -------------------------------------------------------------------------------- 1 | //- 2 | Open vMonitor is a WEB-based tool for monitoring and troubleshooting Open vSwitch 3 | Copyright (C) 2014-2016 PLVision 4 | Ihor Chumak, Roman Gotsiy 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Affero General Public License as 8 | published by the Free Software Foundation, either version 3 of the 9 | License, or (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Affero General Public License for more details. 15 | 16 | You should have received a copy of the GNU Affero General Public License 17 | along with this program. If not, see . 18 | 19 | PLVision, developers@plvision.eu 20 | 21 | extends general_table 22 | 23 | block tablecontent 24 | include include/htable_inc 25 | -------------------------------------------------------------------------------- /views/include/htable_inc.jade: -------------------------------------------------------------------------------- 1 | //- 2 | Open vMonitor is a WEB-based tool for monitoring and troubleshooting Open vSwitch 3 | Copyright (C) 2014-2016 PLVision 4 | Ihor Chumak, Roman Gotsiy 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Affero General Public License as 8 | published by the Free Software Foundation, either version 3 of the 9 | License, or (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Affero General Public License for more details. 15 | 16 | You should have received a copy of the GNU Affero General Public License 17 | along with this program. If not, see . 18 | 19 | PLVision, developers@plvision.eu 20 | 21 | include mixins 22 | if data.length == 0 23 | h3 #{title}: no data here 24 | else 25 | h3 #{title} 26 | - var rows = data 27 | div.scrollbar 28 | - var columns = Object.keys(table_schema.columns).sort(); 29 | table.table.table-bordered.table-hover.table-striped#use_datatable 30 | thead 31 | tr 32 | th 33 | +title("Uuid") 34 | each key, index in columns 35 | th(nowrap) 36 | +title(key) 37 | tbody 38 | each value, row_idx in rows 39 | tr 40 | //- displaying row uuid 41 | td(nowrap) 42 | +display_complex_data(rows[row_idx]["_uuid"], "", false, true) 43 | each _key, _index in columns 44 | td(nowrap) 45 | - var title = _key.substr(0, 1).toUpperCase() + _key.substr(1).toLowerCase() 46 | - title = title.split('_').join(' '); 47 | 48 | +display_complex_data(rows[row_idx][_key], title) 49 | -------------------------------------------------------------------------------- /views/include/mixins.jade: -------------------------------------------------------------------------------- 1 | //- 2 | Open vMonitor is a WEB-based tool for monitoring and troubleshooting Open vSwitch 3 | Copyright (C) 2014-2016 PLVision 4 | Ihor Chumak, Roman Gotsiy 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Affero General Public License as 8 | published by the Free Software Foundation, either version 3 of the 9 | License, or (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Affero General Public License for more details. 15 | 16 | You should have received a copy of the GNU Affero General Public License 17 | along with this program. If not, see . 18 | 19 | PLVision, developers@plvision.eu 20 | 21 | mixin title(value) 22 | -// handle 0 and false 23 | if typeof(value) !== 'undefined' 24 | if typeof(value) === 'number' 25 | |#{value} 26 | else 27 | - var words = value.split('_') 28 | - var res = [] 29 | each word in words 30 | - res.push(word.substr(0, 1).toUpperCase() + word.substr(1).toLowerCase()) 31 | - res = res.join(' ') 32 | |#{res} 33 | 34 | mixin display_complex_data(col, title, inline, is_row_uuid) 35 | - var type = col[0] 36 | 37 | case type 38 | when "map" 39 | - // this is a map object 40 | - var len = col.length - 1 41 | if len 42 | if col[1].length > 1 43 | if !inline 44 | a.multiple(href="#", modal-title="#{title}") Multiple values 45 | table.hidden 46 | tbody 47 | each value, idx in col[1] 48 | tr 49 | td(nowrap) 50 | +title(value[0]) 51 | td 52 | - // need to check on named-uuid here 53 | if value[1][0] && value[1][0] == 'uuid' 54 | +display_complex_data(value[1], '', true) 55 | else 56 | |#{value[1]} 57 | else 58 | -// do not generate popup 59 | - col = col[1] 60 | each value, idx in col 61 | |#{value[0]} #{value[1]} 62 | br 63 | else 64 | - for (var x = 1; x < col.length; x++) 65 | - for (var y = 0; y < col[x].length; y++) 66 | | #{col[x][y][0]} : 67 | if (col[x][y][1] instanceof Array) 68 | +display_complex_data(col[x][y][1], '', true) 69 | else 70 | | #{col[x][y][1]} 71 | when "set" 72 | - // this is a set of values 73 | - var len = col.length - 1 74 | if len && (col[1] != '') 75 | a.multiple(href="#", modal-title="#{title}") Multiple values 76 | table.hidden 77 | tbody 78 | - var c = col[1][0]; 79 | if (col[1] instanceof Array) && (c && c[0] == "uuid") 80 | each value, idx in col[1] 81 | tr 82 | td 83 | +title(value[3]) 84 | td 85 | - //display uuid as link 86 | +display_complex_data(value, '', true) 87 | else 88 | - // e.g. vlans 89 | tr 90 | td 91 | |#{title} 92 | td 93 | |#{col[1]} 94 | 95 | when "uuid" 96 | -// have to handle named-uuid 97 | if (col[2] instanceof Array) && (col[2][0] == "map") 98 | - col[0] = col[2][1], col[1] = col[3] 99 | - var res = '' 100 | each value, idx in col[0] 101 | - res += value[0] + ': ' + value[1] 102 | a.uuid(href="/#{col[1]}", data-page="#{col[1]}") #{res} 103 | else if (col[2] instanceof Array) && (col[2][0] == "uuid") 104 | - col[2] = col[2][1] 105 | a.uuid(href="/#{col[3]}/#{col[2]}", data-page="#{col[3]}") #{col[2]} 106 | else 107 | //- FIXME: hardcode (workaround to fix bug 68: bad link on SSL page) 108 | - var link = col[3] + "/" + col[2] 109 | - var title = col[2] 110 | if (col[3] === "SSL") 111 | - link = col[3] 112 | 113 | - // if this is uuid of the current row, then in title should be exact uuid value 114 | if (is_row_uuid) 115 | - title = col[1] 116 | 117 | a.uuid(href="/#{link}", data-page="#{col[3]}") #{title} 118 | default 119 | - // should not be here 120 | - if (col !== "") 121 | | #{col} 122 | - else 123 | | Empty 124 | 125 | mixin comma_separated_list_map(map) 126 | if map instanceof Array 127 | if map[0] === "map" 128 | - map = map[1] 129 | 130 | - var value = '' 131 | each element in map 132 | b #{element[0]}: 133 | |  #{element[1]} 134 | br 135 | |#{value} 136 | 137 | mixin comma_separated_set_td(set) 138 | - var value; 139 | if (set && (set instanceof Array)) 140 | if set[0] === "set" 141 | - set = set[1] 142 | else 143 | - set = [set] 144 | - value = set.join(", ") 145 | else 146 | - value = set 147 | - cls = (!value ? "Empty" : "") 148 | td(class="#{cls}") #{value} 149 | 150 | 151 | mixin comma_separated_uuids_links(set, link_to_table) 152 | if set[0] === "set" 153 | - uuids = set[1] 154 | else 155 | - uuids = [set] 156 | 157 | span.uuid-links-list 158 | each uuid, idx in uuids 159 | span.uuid-link 160 | - var link = uuid[3] + "/" + uuid[2] 161 | if (link_to_table) 162 | - link = uuid[3] 163 | a(href="/#{link}", data-page="#{uuid[3]}") #{uuid[2]} 164 | 165 | mixin set_length(set) 166 | if set[0] === "set" || set[0] === "map" 167 | - uuids = set[1] 168 | else 169 | - uuids = [set] 170 | |#{uuids.length} 171 | 172 | mixin children_list(caption, children, table, refTable, object_name) 173 | h4 #{caption} ( 174 | +set_length(children) 175 | |) 176 | .row 177 | .col-lg-12 178 | +comma_separated_uuids_links(children) 179 | br 180 | if (!object_name) 181 | span.uuid-list-details 182 | a(href="/#{refTable}" data-page="#{refTable}" class="text-info") Detailed view 183 | else 184 | span.uuid-list-details 185 | a(href="/#{table}/#{object_name}/#{refTable}" data-page="#{refTable}") Detailed view 186 | -------------------------------------------------------------------------------- /views/include/vtable_inc.jade: -------------------------------------------------------------------------------- 1 | //- 2 | Open vMonitor is a WEB-based tool for monitoring and troubleshooting Open vSwitch 3 | Copyright (C) 2014-2016 PLVision 4 | Ihor Chumak, Roman Gotsiy 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Affero General Public License as 8 | published by the Free Software Foundation, either version 3 of the 9 | License, or (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Affero General Public License for more details. 15 | 16 | You should have received a copy of the GNU Affero General Public License 17 | along with this program. If not, see . 18 | 19 | PLVision, developers@plvision.eu 20 | 21 | include mixins 22 | if data.length == 0 23 | h3 #{title}: no data here 24 | else 25 | h3 #{title} 26 | - var row = data 27 | if (row instanceof Array) 28 | - row = data[0] 29 | table.table.table-bordered.table-hover.table-striped#use_datatable 30 | thead 31 | tr 32 | th.col-lg-2(nowrap) Option 33 | th(nowrap) Value(s) 34 | tbody 35 | - var keys = Object.keys(table_schema.columns).sort(); 36 | //- displaying row (record) uuid 37 | tr 38 | td Uuid 39 | td 40 | +display_complex_data(row["_uuid"], "", false, true) 41 | each key in keys 42 | tr 43 | td 44 | +title(key) 45 | td 46 | - var title = key.substr(0, 1).toUpperCase() + key.substr(1).toLowerCase() 47 | +display_complex_data(row[key], title) 48 | -------------------------------------------------------------------------------- /views/index.jade: -------------------------------------------------------------------------------- 1 | //- 2 | Open vMonitor is a WEB-based tool for monitoring and troubleshooting Open vSwitch 3 | Copyright (C) 2014-2016 PLVision 4 | Ihor Chumak, Roman Gotsiy 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Affero General Public License as 8 | published by the Free Software Foundation, either version 3 of the 9 | License, or (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Affero General Public License for more details. 15 | 16 | You should have received a copy of the GNU Affero General Public License 17 | along with this program. If not, see . 18 | 19 | PLVision, developers@plvision.eu 20 | 21 | extends layout 22 | 23 | mixin list_objects(table, list, sub_items) 24 | each obj, key in list 25 | li 26 | - var id = obj.name + "_submenu" 27 | a.link(data-toggle="collapse",data-target="##{id}") #{obj.name} 28 | span.fa.arrow 29 | ul.nav.nav-third-level.collapse(id="#{id}") 30 | each item_details, item_link in sub_items 31 | li 32 | a.link(href="/#{table}/#{obj.name}/#{item_link}" data-page="#{item_details.caption}") #{item_details.caption} 33 | 34 | block sidebar 35 | - var c = "" 36 | - var auth_error_text = "" 37 | if authorized !== undefined 38 | - c = (authorized === true ? "bg-success" : "bg-danger"); 39 | - auth_error_text = auth_error ? "Server is unauthorized: " + auth_error : "Server is authorized"; 40 | // li(class="disabled #{c}" title="#{auth_error_text}") 41 | a Connection: #{connection} 42 | li(class="disabled") 43 | a #{connection}/Open_vSwitch 44 | // .btn-group 45 | //label.btn.btn-default.dropdown-toggle(type='button', data-toggle='dropdown') 46 | div 47 | | #{selected_db}  48 | //span.caret 49 | 50 | ul.dropdown-menu(role='menu', aria-labelledby='dLabel') 51 | each db in databases 52 | - var cls = (db===selected_db) ? "active" : "" 53 | li(role='presentation', class="#{cls}") 54 | a(role='menuitem', tabindex='-1', href='#', value="#{db}") #{db} 55 | -//li 56 | -//a(href="/", data-page="dashboard") Dashboard 57 | li 58 | a.link(href="/Open_vSwitch" data-page="Open_vSwitch") General info 59 | li 60 | a(href='#') 61 | -//i.fa.fa-bar-chart-o.fa-fw 62 | | Bridges 63 | span.fa.arrow 64 | - var bridge_subitems = { "" : { caption: "Bridge Details" }, "Port" : { caption : "Ports" }, "NetFlow" : { caption : "NetFlow" }, "Mirror" : { caption : "Mirrors" }, "Controller" : { caption : "Controller" }, "IPFIX" : { caption : "IPFIX" }, "sFlow" : {caption : "sFlow"} } 65 | ul.nav.nav-second-level.collapse 66 | li 67 | a.link(href="/Bridge", data-page="Bridge") Bridges List 68 | +list_objects("Bridge", bridges, bridge_subitems) 69 | li 70 | a.link(href="/Manager" data-page="Manager") Manager 71 | li 72 | a.link(href='/QoS' data-page="QoS") QoS 73 | li 74 | a.link(href='/Queue' data-page="Queue") Queues 75 | li 76 | a.link(href='/Flow_Table' data-page="Flow_Table") Flow Table 77 | li 78 | a.link(href='/Flow_Sample_Collector_Set' data-page="Flow_Sample_Collector_Set") Flow Sample Collector Set 79 | // li 80 | // a.link(href='/alerts' data-page="Alerts") Alerts 81 | // a.link(href="/SSL" data-page="SSL") SSL 82 | // li 83 | 84 | block content 85 | - if(!isAjax) 86 | .row 87 | .col-lg-12#maincontent 88 | block maincontent 89 | block scripts 90 | - else 91 | block maincontent 92 | block scripts 93 | -------------------------------------------------------------------------------- /views/layout.jade: -------------------------------------------------------------------------------- 1 | //- 2 | Open vMonitor is a WEB-based tool for monitoring and troubleshooting Open vSwitch 3 | Copyright (C) 2014-2016 PLVision 4 | Ihor Chumak, Roman Gotsiy 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Affero General Public License as 8 | published by the Free Software Foundation, either version 3 of the 9 | License, or (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Affero General Public License for more details. 15 | 16 | You should have received a copy of the GNU Affero General Public License 17 | along with this program. If not, see . 18 | 19 | PLVision, developers@plvision.eu 20 | 21 | mixin list_db(obj, db) 22 | if obj 23 | - var isArray = obj instanceof Array; 24 | - var isObject = obj instanceof Object; 25 | 26 | if isArray 27 | each val, index in obj 28 | +list_db(val, db) 29 | else if isObject 30 | each val, index in obj 31 | li 32 | a.link(href="/#{index}" data-page="#{index}") #{index} 33 | ul.collapse.in 34 | +list_db(val, db) 35 | else 36 | li 37 | a.link(href="/#{obj}" data-page="#{obj}", data-addr="#{db}") #{obj} 38 | 39 | - if(!isAjax) 40 | doctype html 41 | html(lang="en") 42 | head 43 | meta(charset="utf-8") 44 | meta(http-equiv="X-UA-Compatible", content="IE=edge") 45 | meta(name="viewport", content="width=device-width, initial-scale=1") 46 | meta(name="description", content="") 47 | meta(name="author", content="") 48 | title= title 49 | link(rel='stylesheet', href='/stylesheets/bootstrap.min.css') 50 | link(rel='stylesheet', href='/stylesheets/style.css') 51 | link(rel='stylesheet', href='/stylesheets/sb-admin-2.css') 52 | link(rel='stylesheet', href='/stylesheets/plugins/metisMenu.min.css') 53 | link(rel='stylesheet', href='/stylesheets/font-awesome.min.css') 54 | link(rel='stylesheet', href='/stylesheets/plugins/scroller.css') 55 | link(rel='stylesheet', href='/stylesheets/sweet-alert.css') 56 | link(rel='stylesheet', href='/stylesheets/bootstrap-select.min.css') 57 | script(src="/js/jquery-1.11.1.min.js") 58 | script(src="/js/bootstrap.min.js") 59 | script(src="/js/plugins/metisMenu.min.js") 60 | script(src="/js/sb-admin-2.js") 61 | script(src="/js/d3.v3.min.js") 62 | script(src="/js/d3.tip.v0.6.3.js") 63 | script(src="/js/event_emitter.js") 64 | script(src="/js/plugins/scroller.js") 65 | script(src="/js/navigation.js") 66 | script(src="/js/assets.js") 67 | script(src="/js/loading.js") 68 | script(src="/js/sweet-alert.min.js") 69 | script(src="/js/bootstrap-select.min.js") 70 | script(src="/js/plugins/jquery.cookie.js") 71 | 72 | style(type='text/css'). 73 | th.rotate { 74 | /* Something you can count on */ 75 | height: 140px; 76 | white-space: nowrap; 77 | } 78 | 79 | th.rotate > div { 80 | transform: rotate(-90deg); 81 | -webkit-transform:rotate(-90deg); 82 | width: 30px; 83 | } 84 | th.rotate > div > span { 85 | 86 | } 87 | 88 | body 89 | #wrapper 90 | nav.navbar.navbar-default.navbar-static-top(role="navigation", style="margin-bottom: 0px;") 91 | .navbar-header 92 | button.navbar-toggle(type='button') 93 | span.sr-only Toggle navigation 94 | span.icon-bar 95 | span.icon-bar 96 | span.icon-bar 97 | 98 | a.navbar-brand(href="/") Open_vMonitor 99 | ul.nav.navbar-top-links.navbar-right 100 | label(style="color: #fff; margin-right: 5px;") 101 | | Automatic refresh: 102 | select.selectpicker.show-tick(data-width="auto") 103 | optgroup(label="Refresh timeout") 104 | option(value="-1") 105 | | Off 106 | option(value="10") 107 | | 10 secs 108 | option(value="20") 109 | | 20 secs 110 | option(value="30") 111 | | 30 secs 112 | option(value="60") 113 | | 1 minute 114 | -//li.dropdown 115 | -//a.dropdown-toggle(data-toggle='dropdown', href='#') 116 | -//i.fa.fa-envelope.fa-fw 117 | -//i.fa.fa-caret-down 118 | -//ul.dropdown-menu.dropdown-messages 119 | -//li 120 | -//a(href='#') 121 | -//div 122 | -//strong John Smith 123 | -//span.pull-right.text-muted 124 | -//em Yesterday 125 | -//div 126 | -//| Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque eleifend... 127 | -//li.divider 128 | -//li 129 | -//a.text-center(href='/log') 130 | -//strong Read All Messages 131 | -//i.fa.fa-angle-right 132 | li.dropdown 133 | a.dropdown-toggle(data-toggle='dropdown', href='#') 134 | span.badge#num-of-updates 135 | i.fa.fa-bell.fa-fw 136 | i.fa.fa-caret-down 137 | ul.dropdown-menu.dropdown-alerts 138 | //li 139 | a(href='#') 140 | i.fa.fa-gear.fa-fw 141 | | Settings 142 | //li.divider 143 | li 144 | a(href="#") 145 | | Available only in Full Version 146 | //i.fa.fa-angle-right 147 | li.dropdown 148 | a.dropdown-toggle(data-toggle='dropdown', href='#') 149 | i.fa.fa-user.fa-fw 150 | i.fa.fa-caret-down 151 | ul.dropdown-menu.dropdown-user 152 | //li 153 | a(href='#') 154 | i.fa.fa-gear.fa-fw 155 | | Settings 156 | //li.divider 157 | li 158 | a(href='/logout') 159 | i.fa.fa-sign-out.fa-fw 160 | | Logout 161 | .navbar-default.sidebar(role="navigation") 162 | .sidebar-nav.navbar-collapse 163 | ul.nav#side-menu 164 | block sidebar 165 | 166 | #page-wrapper 167 | #page_overlay 168 | block content 169 | 170 | img(src="/images/loading.gif" id="loading-indicator" style="display:none") 171 | 172 | .modal#modal(tabindex="-1", role="dialog", aria-labelledby="", aria-hidden="true") 173 | .modal-dialog.modal-sm 174 | .modal-content 175 | .modal-header 176 | button.close(type="button", data-dismiss="modal") 177 | span(area-hidden="true") × 178 | span.sr-only Close 179 | h4.modal-title#modal-title 180 | .modal-body 181 | table.table.table-bordered.table-hover.table-striped 182 | .modal-footer 183 | button.btn.btn-default(type="button", data-dismiss="modal") Close 184 | 185 | script. 186 | $(document).ready(function() { 187 | var auto_refresh_interval = undefined; 188 | 189 | //var addr = $("[data-connection]").first().attr("data-connection"); 190 | Navigation.init({ 191 | container : "#maincontent", 192 | landing_page : window.location.pathname 193 | }); 194 | //Navigation.go(windo.location.pathname) 195 | 196 | Navigation.on("loaded", function(url) { 197 | if ((typeof url === "string") || (url instanceof String)) { 198 | var $all_items = $('a[data-page]', $("#side-menu")); 199 | $all_items.removeClass('active'); 200 | select_menu_item('[href="' + url + '"]'); 201 | $("a[data-page]").navigation(); 202 | init_custom_scroller(".scrollbar", true); 203 | 204 | } else { 205 | /* we have error here */ 206 | var xhr = url; 207 | if (xhr.status == 500) { 208 | swal({ 209 | title: "Error!", 210 | text: "Something went wrong. Try again. If the error persists, connection to ovsdb-server may be lost: try to log out and connect again", 211 | // type: "error" 212 | }); 213 | } 214 | } 215 | }); 216 | 217 | 218 | $(window).on('hashchange', function(e) { 219 | $("a[href='" + window.location.hash + "']").click(); 220 | }); 221 | 222 | $('.navbar-toggle').click(function() { 223 | $("#wrapper").toggleClass("sidebar-shown"); 224 | $(".navbar-collapse").toggleClass("in", $("#wrapper").hasClass("sidebar-shown")); 225 | }); 226 | 227 | // initialize update notifications with selected interval 228 | $('.selectpicker').change(function(e) { 229 | var value = $(this).find('option:selected').val(); 230 | 231 | if (typeof auto_refresh_interval !== "undefined") { 232 | clearInterval(auto_refresh_interval); 233 | $.cookie("refresh_interval", "-1"); 234 | } 235 | 236 | if (value != -1) { 237 | auto_refresh_interval = setInterval(function() { 238 | window.location.reload(); 239 | }, value + "000"); 240 | $.cookie("refresh_interval", value); 241 | } 242 | }); 243 | 244 | var value = $.cookie("refresh_interval"); 245 | if ((typeof value === "undefined") || value == 0) { 246 | value = -1; 247 | } 248 | 249 | if (value != '-1') { 250 | $('.selectpicker').val(value).change(); 251 | } 252 | 253 | }); 254 | - else 255 | block content 256 | -------------------------------------------------------------------------------- /views/raw_json.jade: -------------------------------------------------------------------------------- 1 | //- 2 | Open vMonitor is a WEB-based tool for monitoring and troubleshooting Open vSwitch 3 | Copyright (C) 2014-2016 PLVision 4 | Ihor Chumak, Roman Gotsiy 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Affero General Public License as 8 | published by the Free Software Foundation, either version 3 of the 9 | License, or (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Affero General Public License for more details. 15 | 16 | You should have received a copy of the GNU Affero General Public License 17 | along with this program. If not, see . 18 | 19 | PLVision, developers@plvision.eu 20 | 21 | extends index 22 | block maincontent 23 | #raw_packet 24 | .row 25 | .col-lg-12 26 | .panel.panel-default 27 | .panel-body 28 | .row 29 | .col-lg-12#raw_content 30 | h3 Raw JSON OVSDB: 31 | textarea.form-control(rows="3", placeholder="What's up?",required) 32 | textarea.form-control#raw_reply(rows="3", placeholder="What's up?",disabled) 33 | button.btn.btn-default(type="button", onclick="submit_raw_json();") Submit 34 | -------------------------------------------------------------------------------- /views/vtable.jade: -------------------------------------------------------------------------------- 1 | //- 2 | Open vMonitor is a WEB-based tool for monitoring and troubleshooting Open vSwitch 3 | Copyright (C) 2014-2016 PLVision 4 | Ihor Chumak, Roman Gotsiy 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Affero General Public License as 8 | published by the Free Software Foundation, either version 3 of the 9 | License, or (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Affero General Public License for more details. 15 | 16 | You should have received a copy of the GNU Affero General Public License 17 | along with this program. If not, see . 18 | 19 | PLVision, developers@plvision.eu 20 | 21 | extends general_table 22 | 23 | block tablecontent 24 | include include/vtable_inc.jade 25 | --------------------------------------------------------------------------------