diff options
author | Kristian Lyngstol <kristian@bohemians.org> | 2015-04-25 19:10:33 +0200 |
---|---|---|
committer | Kristian Lyngstol <kristian@bohemians.org> | 2015-04-25 19:10:33 +0200 |
commit | 867d787c4ab152719dfe13b1f13ab3e303a4780e (patch) | |
tree | aec1a96709f86faa3cb2cedf53008ec0d81a6554 /web | |
parent | c7ebb7269d9ea8ead03b717153bfa54e3c8226c9 (diff) |
NMS: Monster-commit after local .git corruption
This constitutes ... I don't know.... 20 commits or something like that.
Unfortunately my local .git directory got corrupted while the check out was
OK (after the VM froze). Instead of trying to recreate the local history,
I'm just hurrying the hell up and getting this work OFF my local laptop and
VM so I don't lose anything for good.
Diffstat (limited to 'web')
-rw-r--r-- | web/etc/varnish/default.vcl | 2 | ||||
-rw-r--r-- | web/nms.gathering.org/nms2/index.html | 637 | ||||
-rw-r--r-- | web/nms.gathering.org/nms2/js/nms-color-util.js | 36 | ||||
-rw-r--r-- | web/nms.gathering.org/nms2/js/nms-map-handlers.js | 127 | ||||
-rw-r--r-- | web/nms.gathering.org/nms2/js/nms.js | 499 | ||||
-rwxr-xr-x | web/nms.gathering.org/port-state.pl | 7 | ||||
-rwxr-xr-x | web/nms.gathering.org/uplinkkart-text.pl | 2 |
7 files changed, 942 insertions, 368 deletions
diff --git a/web/etc/varnish/default.vcl b/web/etc/varnish/default.vcl index 5f825a6..b4445d1 100644 --- a/web/etc/varnish/default.vcl +++ b/web/etc/varnish/default.vcl @@ -95,7 +95,7 @@ sub vcl_backend_response { set beresp.ttl = 0s; } if(bereq.url ~ "port-state.pl" && beresp.status == 200) { - set beresp.ttl = 30s; + set beresp.ttl = 1s; } if (beresp.status == 200 && bereq.url ~ "now=") { set beresp.ttl = 60m; diff --git a/web/nms.gathering.org/nms2/index.html b/web/nms.gathering.org/nms2/index.html index 42d7a35..141ef5f 100644 --- a/web/nms.gathering.org/nms2/index.html +++ b/web/nms.gathering.org/nms2/index.html @@ -21,91 +21,108 @@ <!--[if lt IE 9]> <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script> <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script> - <![endif]--> - <style type="text/css"> - canvas { - -webkit-touch-callout: none; - -webkit-user-select: none; - -khtml-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - outline: none; - -webkit-tap-highlight-color: rgba(255, 255, 255, 0); /* mobile webkit */ - } - </style> + <![endif]--> + <style type="text/css"> + canvas { + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + outline: none; + -webkit-tap-highlight-color: rgba(255, 255, 255, 0); /* mobile webkit */ + } + </style> </head> -<body id="body"> + <body id="body"> <nav class="navbar navbar-default navbar-static-top"> <div class="container-fluid"> - <div class="navbar-header"> - <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar"> - <span class="sr-only">Toggle navigation</span> - <span class="icon-bar"></span> - <span class="icon-bar"></span> - <span class="icon-bar"></span> - </button> - <a class="navbar-brand" href="#">NMS</a> - </div> - <div id="navbar" class="navbar-collapse collapse"> - <ul class="nav navbar-nav"> - <li class="dropdown"> - <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">Map mode<span class="caret"></span></a> - <ul class="dropdown-menu" role="menu"> - <li><a href="#ping" onclick="setUpdater(handler_ping)">Ping map</a></li> - <li><a href="#uplink" onclick="setUpdater(handler_uplinks)">Uplink map</a></li> - <li><a href="#temp" onclick="setUpdater(handler_temp)">Temperature map</a></li> - <li><a href="#traffic" onclick="setUpdater(handler_traffic)">Traffic map</a></li> - <li><a href="#comment" onclick="setUpdater(handler_comment)">Comment spotter</a></li> - <li><a href="#disco" onclick="setUpdater(handler_disco)">DISCO</a></li> - <li class="divider"> </li> - <li><a href="#" onclick="toggleNightMode()" title="Add 'nightMode' anywhere in the url to auto-enable">Toggle Night Mode</a></li> - <li><a href="#" onclick='showLayer("layerVisibility");'>Set layer visibility</a></li> - <li class="divider"> </li> - <li><a href="#" onclick="document.getElementById('nowPickerBox').style.display = 'block';">Travel in time</a></li> - <li><a href="#" onclick="startReplay();" title="Replay from opening 30 minutes per second">Replay TG</a></li> - <li><a href="#" onclick="document.getElementById('aboutData').style.display = 'block';">About TG15 data</a></li> - <li class="divider"> </li> - <li class="dropdown-header">Map scale</li> - <li><input type="range" id="scaler" name="points" min="0.2" max="3" step="0.01" onchange="scaleChange()" /></li> - <li><a href="#">Scale: <div id="scaler-text"></div></a></li> - <li class="divider"> </li> - <li><a onclick="document.getElementById('aboutBox').style.display = 'block'; hideSwitch();" style="cursor: pointer;" >About</a></li> - <li><a onclick="showTimerDebug(); hideSwitch();" style="cursor: pointer;" >Debug timers</a></li> - </ul> - </li> - <li><p id="updater_name" class="navbar-text"></p></li> - <div class="navbar-form navbar-left"> - <div class="form-group"> - <button class="btn btn-default" id="legend-1"></button> - <button class="btn btn-default" id="legend-2"></button> - <button class="btn btn-default" id="legend-3"></button> - <button class="btn btn-default" id="legend-4"></button> - <button class="btn btn-default" id="legend-5"></button> - </div> - </div> - </li> - </ul> - <ul class="nav navbar-nav navbar-right"> - <li><p id="speed" class="navbar-text" title="Client port speed / Total port speed"></p></li> - </ul> - </div><!--/.nav-collapse --> + <div class="navbar-header"> + <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar"> + <span class="sr-only">Toggle navigation</span> + <span class="icon-bar"></span> + <span class="icon-bar"></span> + <span class="icon-bar"></span> + </button> + <a class="navbar-brand" onclick="toggleLayer('aboutBox'); //document.getElementById('aboutBox').style.display = 'block'; hideSwitch();" style="cursor: pointer;" >NMS</a> + </div> + <div id="navbar" class="navbar-collapse collapse"> + <ul class="nav navbar-nav"> + <li class="dropdown"> + <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">Map mode + <span class="caret"></span> + </a> + <ul class="dropdown-menu" role="menu"> + <li><a href="#ping" onclick="setUpdater(handler_ping)">Ping map</a></li> + <li><a href="#uplink" onclick="setUpdater(handler_uplinks)">Uplink map</a></li> + <li><a href="#temp" onclick="setUpdater(handler_temp)">Temperature map</a></li> + <li><a href="#traffic" onclick="setUpdater(handler_traffic)">Traffic map</a></li> + <li><a href="#comment" onclick="setUpdater(handler_comment)">Comment spotter</a></li> + <li><a href="#disco" onclick="setUpdater(handler_disco)">DISCO</a></li> + <li class="divider"> </li> + <li><a href="#" onclick="document.getElementById('nowPickerBox').style.display = 'block';">Travel in time</a></li> + <li><a href="#" onclick="startReplay();" title="Replay from opening 30 minutes per second">Replay TG</a></li> + <li class="divider"> </li> + <li><a onclick="showTimerDebug(); hideSwitch();" style="cursor: pointer;" >Debug timers</a></li> + </ul> + </li> + <li class="dropdown"> + <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">View<span class="caret"></span></a> + <ul class="dropdown-menu" role="menu"> + <li><a href="#" onclick="toggleNightMode()">Toggle Night Mode</a></li> + <li><a href="#" onclick="showBlurBox()">Tweak Night Mode blur</a></li> + <li><a href="#" onclick='showLayer("layerVisibility");'>Set layer visibility</a></li> + <li class="divider"> </li> + <li class="dropdown-header">Map scale</li> + <li><a href="#"><label id="scaler-text" for='scaler'></label><input type="range" id="scaler" name="points" min="0.2" max="3" step="0.01" onchange="scaleChange()" /></a></li> + </ul> + </li> + <li><p id="updater_name" class="navbar-text"></p></li> + <div class="navbar-form navbar-left"> + <div class="form-group"> + <button class="btn btn-default" id="legend-1"></button> + <button class="btn btn-default" id="legend-2"></button> + <button class="btn btn-default" id="legend-3"></button> + <button class="btn btn-default" id="legend-4"></button> + <button class="btn btn-default" id="legend-5"></button> + </div> + </div> + </li> + </ul> + <ul class="nav navbar-nav navbar-right"> + <li><p id="speed" class="navbar-text" title="Client port speed / Total port speed"></p></li> + <li class="dropdown"> + <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">Help + <span class="caret"></span> + </a> + <ul class="dropdown-menu" role="menu"> + <li><a href="#" onclick="toggleLayer('aboutData');">About TG15 data</a></li> + <li><a href="#" onclick="toggleLayer('aboutBox');" >About NMS</a></li> + <li><a href="#" onclick="toggleLayer('aboutPerformance');" >About Performance</a></li> + <li><a href="#" onclick="toggleLayer('aboutKeybindings');" >Keyboard Shortcuts</a></li> + </ul> + </li> + </ul> + </div><!--/.nav-collapse --> </div> </nav> <div class="container-fluid"> - <div class="row-fluid"> - <div class="span12"> - <div id="aboutData" class="col-md-4" - style="position: absolute; display:none; z-index: 130;"> - <div id="abotData" class="panel panel-default"> - <div class="panel-heading"><h3 class="panel-title">About - the TG15 data - <button type="button" class="close" aria-label="Close" onclick="document.getElementById('aboutData').style.display = 'none';" style="float: right"><span - aria-hidden="true">×</span></button></h3></div> - <div class="panel-body"> + <div class="row-fluid"> + <div class="span12"> + <div id="aboutData" class="col-md-4" style="position: absolute; display:none; z-index: 130;"> + <div id="abotData" class="panel panel-default"> + <div class="panel-heading"> + <h3 class="panel-title">About the TG15 data + <button type="button" class="close" aria-label="Close" onclick="document.getElementById('aboutData').style.display = 'none';" style="float: right"> + <span aria-hidden="true">×</span> + </button> + </h3> + </div> + <div class="panel-body"> <p>The data you see from The Gathering 2015 will seem "broken up". This is not because we don't have data from the first day, but because the backend was re-written on @@ -130,147 +147,351 @@ <p>It is also worth mentioning that things like switch positions are not logged historically, so you see the final position on the map.</p> - </div> - - </div> - </div> - <div id="nowPickerBox" class="panel panel-default" style="position: absolute; display:none; z-index: 130;" > - <div class="panel-heading"><h3 - class="panel-title">Time travel</h3></div> - <div class="panel-body"> - <input type="text" class="form-control" placeholder="YYYY-MM-DD hh:mm:ss" id="nowPicker" value="" /> - <button class="btn btn-default" onclick="changeNow();">Travel</button> - <button class="btn" onclick="document.getElementById('nowPickerBox').style.display = 'none';">Cancel</button> - </div> - </div> - <div style="position: absolute; z-index: 120;" class="col-md-3"> - <div id="info-switch-parent" class="panel panel-default col-d-6" style="display: none; backgroun:silver; position: absolute; z-index: 120;"> - <div class="panel-heading"><h3 class="panel-title" - id="info-switch-title"></h3></div> - <div id="info-switch-panel-body"> - <table class="table" id="info-switch-table"></table> - </div> - </div> - </div> - <div id="aboutBox" class="col-md-4" style="display: none; - position: absolute; z-index: 100;"> - <div id="abotBox" class="panel panel-default"> - <div class="panel-heading"><h3 class="panel-title">Welcome to NMS - <button type="button" class="close" aria-labe="Close" onclick="document.getElementById('aboutBox').style.display = 'none';" style="float: right;"><span aria-hidden="true">×</span></button></h3></div> + </div> + </div> + </div> + <div id="aboutPerformance" class="col-md-4" style="position: absolute; display:none; z-index: 130;"> + <div class="panel panel-default"> + <div class="panel-heading"> + <h3 class="panel-title">Performance + <button type="button" class="close" aria-label="Close" onclick="document.getElementById('aboutPerformance').style.display = 'none';" style="float: right"> + <span aria-hidden="true">×</span> + </button> + </h3> + </div> + <table class="table"> + <tr> + <td>Outstanding AJAX requests</td> + <td id="outstandingAJAX"></td> + </tr> + <tr> + <td>Overflowed AJAX requests</td> + <td id="overflowAJAX"></td> + </tr> + </table> + <div class="panel-body"> + <p>NMS performance is surprisingly complex. It's split into + several parts and dealt with differently.</p> + <p>Poller performance is a matter of efficiently collecting + data and is mostly handled in the Perl code (and ensuring + we use sensible database schemas).</p> + <p>Backend performance for the GUI is mostly about not + killing the database server. We do NOT try to protect + against malicious clients directly, since this is a + management system not public-facing, but Varnish is used to + cache requests. To be able to do that properly, we need use + absolute time when reviewing past events (so "2015-04-02 + 17:30:00", not "2 hours ago"). We've also tried to minimize + the stupidity in the queries. There's still work to be done + here, though, as we need to split up a few large backend + requests (port-state.pl).</p> + <p>Front-end performance is mostly about drawing things + sensibly and not completely bombing the memory usage. And + about gracefully handling slow backends This will affect + you. For example, if you are reviewing past events and the + DB is struggling, we'll simply skip a backend request if we + have too many outstanding requests, that means you may jump + from "17:00" to "18:30" instead of going through + "17:30" and "18:00" too. This is working as intended. It + also means that you can happily spam the forward/backward + keyboard bindings to jump 18 hours forward: You'll overflow + the extra AJAX requests for individual requests, but you'll + land at the right time when you let go. But there could be + a 1 second delay (or more if the backend really struggles) + since you'll have to rely on the periodic backend requests + instead of the explicit ones triggered on hitting a + button.</p> + <p>Note that the counters on top are updated on a timer, + but this timer is set up at the same time as everything + else, which means that it's likely to update at the same + time as we fire off AJAX requests, so the 'outstanding ajax + requests' counter might either show almost constantly 3 or + 0 depending on what timer happens to fire first. This does + NOT mean that NMS has 3 requests all the time, just that + we're checking right after we fire off AJAX requests every + time.</p> + <p>NMS also tries to handle drawing OK, which is why things + are split into different HTML5 canvases. Blur and text are + particularly expensive, but there's no reason to re-paint + that all the time, etc).</p> + <p>The basic performance experiments are done on TG15 data + using a laptop and a VM with 6GB of memory, so it should + hold up quite well on "proper" hardware.</p> + </div> + </div> + </div> + <div id="aboutKeybindings" class="col-md-4" style="position: absolute; display:none; z-index: 130;"> + <div class="panel panel-default"> + <div class="panel-heading"> + <h3 class="panel-title">Keyboard Shortcuts + <button type="button" class="close" aria-label="Close" onclick="document.getElementById('aboutKeybindings').style.display = 'none';" style="float: right"> + <span aria-hidden="true">×</span> + </button> + </h3> + </div> + <table class="table table-condensed"> + <tr> + <th>Key</th> + <th>Description</th> + </tr> + <tr> + <td>?</td> + <td>Toggle navigation bar</td> + </tr> + <tr> + <td>n</td> + <td>Toggle night mode</td> + </tr> + <tr> + <td>1</td> + <td>View Ping map</td> + </tr> + <tr> + <td>2</td> + <td>View uplink map</td> + </tr> + <tr> + <td>3</td> + <td>View temperature map</td> + </tr> + <tr> + <td>4</td> + <td>View uplink traffic map</td> + </tr> + <tr> + <td>5</td> + <td>View comment spotter map</td> + </tr> + <tr> + <td>6</td> + <td>View Disco map</td> + </tr> + <tr> + <td>h</td> + <td>Step 1 hour back in time</td> + </tr> + <tr> + <td>j</td> + <td>Step 5 minutes back in time</td> + </tr> + <tr> + <td>k</td> + <td>Step 5 minutes forward in time</td> + </tr> + <tr> + <td>l</td> + <td>Step 1 hour forward in time</td> + </tr> + <tr> + <td>p</td> + <td>Toggle playback (1 hour per second)</td> + </tr> + <tr> + <td>r</td> + <td>Return to real time</td> + </tr> + </table> + </div> + </div> + <div id="nowPickerBox" class="panel panel-default" style="position: absolute; display:none; z-index: 130;" > + <div class="panel-heading"> + <h3 class="panel-title">Time travel</h3> + </div> <div class="panel-body"> - - <h3>Cool stuff:</h3> - <ul> - <li>Click a switch for more info</li> - <li>Rewind: You can check out state at a specific time or replay from the beginning of the event. Only works for data where we keep time-series (so not for comments)</li> - <li>Night mode, now with blur. Optional disco-mode (that's - mainly for testing, though).</li> - <li>Auto-scaling the viewport/canvas</li> - <li>Total client speed (up right)</li> - <li>Generic(-ish) map handlers: provide a name, init-function - and an update-function and the nms lib does the rest as far as - integration goes.</li> - </ul> - <h3>Todo list front end:</h3> - <ul> - <li>Polish time travel UI (Allow playing from a given time at a given speed, play/pause buttons, etc)</li> - <li>Toggle auto-scale on/off</li> - <li>Clean up various global variables</li> - <li>Split blur into separate canvas (canvas is there, it's just - not used)</li> - <li>Add DHCP map</li> - <li>Add magic map (combined map of sorts)</li> - <li>Adjust updatePorts() frequency based on necessity (1sec - updates is overkill for regular operation, but needed for time - travel)</li> - <li>More info on switches: Port state, possibly link time - trends</li> - <li>Moving switches around (like ping.html + edit)</li> - <li>Split nms.js into multiple components to unclutter the - code</li> - <li>Comments: Fix UTF8 garbligash caused by $dbh->quote()</li> - <li>More.</li> - </ul> - <h3>Todo for backend:</h3> - <ul> - <li>IPv6 support</li> - <li>Close SQL injections (IT'S WIDE OPEN BECAUSE WHY NOT THAT'S NEVER A PROBLEM)</li> - <li>Split port-state.pl into multiple appropriate pieces. Right - it mixes heavy time-critical data with less time-critical and - cheap computation.</li> - <li>Consider time log of DHCP (right now it just stores the - most recent timestamp, making time travel impossible)</li> - <li>Fix SNMP-fetcher so it gets ifXTable and at least ifOperStatus from ifTable. Don't request the entire ifXTable if we can avoid it. Possibly other tweaks.</li> - <li>Support for adding switches through an API, not just pure SQL.</li> - <li>Integrate with FAP</li> - <li>Clean up old interfaces</li> - <li>Review various agents/tools</li> - <li>Improve cache headers</li> - <li>Cache invalidation of comments?</li> - <li>Re-test the SQL schema. It's been modified and works fine - on my laptop, but I need to dump it, commit it and test it.</li> - <li>Munin plugin for ports.</li> - </ul> + <input type="text" class="form-control" placeholder="YYYY-MM-DD hh:mm:ss" id="nowPicker" value="" /> + <button class="btn btn-default" onclick="changeNow();">Travel</button> + <button class="btn" onclick="document.getElementById('nowPickerBox').style.display = 'none';">Cancel</button> </div> - + </div> + <div style="position: absolute; z-index: 120;" class="col-md-4"> + <div id="info-switch-parent" class="panel panel-default col-d-6" style="display: none; backgroun:silver; position: absolute; z-index: 120;"> + <div class="panel-heading"> + <h3 class="panel-title" id="info-switch-title"></h3> + </div> + <div id="info-switch-panel-body"> + <table class="table" id="info-switch-table"></table> + </div> </div> + </div> + <div id="aboutBox" class="col-md-4" style="display: none; position: absolute; z-index: 100;"> + <div id="abotBox" class="panel panel-default"> + <div class="panel-heading"> + <h3 class="panel-title">Welcome to NMS + <button type="button" class="close" aria-labe="Close" onclick="document.getElementById('aboutBox').style.display = 'none';" style="float: right;"> + <span aria-hidden="true">×</span> + </button> + </h3> + </div> + <div class="panel-body"> + <h3>Cool stuff:</h3> + <ul> + <li>Click a switch for more info</li> + <li>Rewind: You can check out state at a specific time or + replay from the beginning of the event. Only works for + data where we keep time-series (so not for + comments)</li> + <li>Press '?' to toggle the menu.</li> + <li>Auto-scaling the viewport/canvas</li> + <li>Total client speed (up right)</li> + <li>Generic(-ish) map handlers: provide a name, init-function + and an update-function and the nms lib does the rest as far as + integration goes.</li> + </ul> + <h3>Todo list front end:</h3> + <ul> + <li>Polish time travel UI (Allow playing from a given time at a given speed, play/pause buttons, etc)</li> + <li>Better "popup" boxes: It's growing out of control.</li> + <li>Toggle auto-scale on/off</li> + <li>Clean up various global variables</li> + <li>Add DHCP map</li> + <li>Adjust updatePorts() frequency based on necessity (1sec + updates is overkill for regular operation, but needed for time + travel)</li> + <li>More info on switches: Port state, possibly link time + trends</li> + <li>Moving switches around (like ping.html + edit)</li> + <li>Split nms.js into multiple components to unclutter the + code</li> + <li>Comments: Fix UTF8 garbligash caused by $dbh->quote()</li> + <li>Comments: Fix feedback, possibly update the switch-box + too, when comments change.</li> + </ul> + <h3>Todo for backend:</h3> + <ul> + <li>IPv6 support</li> + <li>Provide public API's</li> + <li>Close SQL injections (IT'S WIDE OPEN BECAUSE WHY NOT THAT'S NEVER A PROBLEM)</li> + <li>Split port-state.pl into multiple appropriate pieces. Right + it mixes heavy time-critical data with less time-critical and + cheap computation.</li> + <li>Rip comments out of port-state.pl completely so it's not + bound by the same cache issues and can be reliably + refreshed.</li> + <li>Consider time log of DHCP (right now it just stores the + most recent timestamp, making time travel impossible)</li> + <li>Fix SNMP-fetcher so it gets ifXTable and at least + ifOperStatus from ifTable. Don't request the entire + ifXTable if we can avoid it. Possibly other + tweaks.</li> + <li>Support for adding switches through an API, not just pure SQL.</li> + <li>Integrate with FAP</li> + <li>Clean up old interfaces</li> + <li>Review various agents/tools</li> + <li>Improve cache headers</li> + <li>Cache invalidation of comments?</li> + <li>Re-test the SQL schema. It's been modified and works fine + on my laptop, but I need to dump it, commit it and test it.</li> + <li>Munin plugin for ports.</li> + </ul> + </div> + </div> + </div> + <div id="blurManic" class="panel panel-default" style="display: none; position: absolute; z-index: 100;"> + <div class="panel-heading"> + <h1 class="panel-title">Blur tweaks + <button type="button" class="close" aria-labe="Close" onclick="document.getElementById('blurManic').style.display = 'none';" style="float: right;"> + <span aria-hidden="true">×</span> + </button> + </h1> + </div> + <div class="panel-body"> + <div class="form"> + <div class="form-group"> + <label for="shadowBlur">Blur strength</label> + <input type="number" id="shadowBlur" class="form-control"> + </div> + <div class="form-group"> + <label for="shadowColor">Blur color</label> + <input type="color" id="shadowColor" class="form-control"> + </div> + <button type="button" class="btn btn-default" onclick="applyBlur();">Apply</button> + </div> </div> - <div id="debugTimers" class="panel panel-default" style="display: none; position: absolute; z-index: 100;"> - <div class="panel-heading"><h1 class="panel-title">Debug - timers (e.g.: Break stuff! FAST!) - <button type="button" class="close" aria-labe="Close" onclick="document.getElementById('debugTimers').style.display = 'none';" style="float: right;"><span aria-hidden="true">×</span></button></h1></div> - <div id="timerTableTop" class="panel-body"> + </div> + </div> + <div id="debugTimers" class="panel panel-default" style="display: none; position: absolute; z-index: 100;"> + <div class="panel-heading"> + <h1 class="panel-title">Debug timers (e.g.: Break stuff! FAST!) + <button type="button" class="close" aria-labe="Close" onclick="document.getElementById('debugTimers').style.display = 'none';" style="float: right;"> + <span aria-hidden="true">×</span> + </button> + </h1> + </div> + <div id="timerTableTop" class="panel-body"> <p>These are internal timers for the NMS frontend. They are provided mainly to debug the frontend. Setting AJAX-triggering counters to ridiculous numbers is not advised (mainly because it causes server load).</p> - <table id="timerTable"> </table> - </div> - </div> - <div id="layerVisibility" class="panel panel-default" style="display: none; position: absolute; z-index: 100;"> - <div class="panel-heading"><h1 class="panel-title">Set layer visibility - <button type="button" class="close" aria-labe="Close" onclick="document.getElementById('layerVisibility').style.display = 'none';" style="float: right;"><span aria-hidden="true">×</span></button></h1></div> - <div class="panel-body"> + </div> + <table id="timerTable"> </table> + </div> + <div id="layerVisibility" class="panel panel-default" style="display: none; position: absolute; z-index: 100;"> + <div class="panel-heading"> + <h1 class="panel-title">Set layer visibility + <button type="button" class="close" aria-labe="Close" onclick="document.getElementById('layerVisibility').style.display = 'none';" style="float: right;"> + <span aria-hidden="true">×</span> + </button> + </h1> + </div> + <div class="panel-body"> <table id="visibilityTable" class="table"> - <tr><td>Background</td><td> - <button onclick='hideLayer("bgCanvas");'>Hide</button> - <button onclick='showLayer("bgCanvas");'>Show</button> - </td></tr> - <tr><td>Linknets</td><td> - <button onclick='hideLayer("linkCanvas");'>Hide</button> - <button onclick='showLayer("linkCanvas");'>Show</button> - </td></tr> - <tr><td>Blur</td><td> - <button onclick='hideLayer("blurCanvas");'>Hide</button> - <button onclick='showLayer("blurCanvas");'>Show</button> - </td></tr> - <tr><td>Switches</td><td> - <button onclick='hideLayer("switchCanvas");'>Hide</button> - <button onclick='showLayer("switchCanvas");'>Show</button> - </td></tr> - <tr><td>Text</td><td> - <button onclick='hideLayer("textCanvas");'>Hide</button> - <button onclick='showLayer("textCanvas");'>Show</button> - </td></tr> - <tr><td>Top</td><td> - <button onclick='hideLayer("topCanvas");'>Hide</button> - <button onclick='showLayer("topCanvas");'>Show</button> - </td></tr> - </table> - </div> - </div> + <tr> + <td>Background</td> + <td> + <button onclick='hideLayer("bgCanvas");'>Hide</button> + <button onclick='showLayer("bgCanvas");'>Show</button> + </td> + </tr> + <tr> + <td>Linknets</td> + <td> + <button onclick='hideLayer("linkCanvas");'>Hide</button> + <button onclick='showLayer("linkCanvas");'>Show</button> + </td> + </tr> + <tr> + <td>Blur</td> + <td> + <button onclick='hideLayer("blurCanvas");'>Hide</button> + <button onclick='showLayer("blurCanvas");'>Show</button> + </td> + </tr> + <tr> + <td>Switches</td> + <td> + <button onclick='hideLayer("switchCanvas");'>Hide</button> + <button onclick='showLayer("switchCanvas");'>Show</button> + </td> + </tr> + <tr> + <td>Text</td> + <td> + <button onclick='hideLayer("textCanvas");'>Hide</button> + <button onclick='showLayer("textCanvas");'>Show</button> + </td> + </tr> + <tr> + <td>Timestamp</td> + <td> + <button onclick='hideLayer("topCanvas");'>Hide</button> + <button onclick='showLayer("topCanvas");'>Show</button> + </td> + </tr> + </table> + </div> + </div> -<canvas id="bgCanvas" width="1920" height="1032" style="position: absolute; z-index: 1;"> </canvas> -<canvas id="linkCanvas" width="1920" height="1032" style="position: absolute; z-index: 10;"> </canvas> -<canvas id="blurCanvas" width="1920" height="1032" style="position: absolute; z-index: 20;"> </canvas> -<canvas id="switchCanvas" width="1920" height="1032" style="position: absolute; z-index: 30;"> </canvas> -<canvas id="textCanvas" width="1920" height="1032" style="position: absolute; z-index: 40;"> </canvas> -<canvas id="topCanvas" width="1920" height="1032" style="position: absolute; z-index: 50; cursor: pointer;" onclick="canvasClick(event)"> -</canvas> + <canvas id="bgCanvas" width="1920" height="1032" style="position: absolute; z-index: 1;"> </canvas> + <canvas id="linkCanvas" width="1920" height="1032" style="position: absolute; z-index: 10;"> </canvas> + <canvas id="blurCanvas" width="1920" height="1032" style="position: absolute; z-index: 20;"> </canvas> + <canvas id="switchCanvas" width="1920" height="1032" style="position: absolute; z-index: 30;"> </canvas> + <canvas id="textCanvas" width="1920" height="1032" style="position: absolute; z-index: 40;"> </canvas> + <canvas id="topCanvas" width="1920" height="1032" style="position: absolute; z-index: 50;"> </canvas> + <canvas id="inputCanvas" width="1920" height="1032" style="position: absolute; z-index: 60; cursor: pointer;" onclick="canvasClick(event)"> + </canvas> -<div style="display:none;"><img id="source" src="img/tg15-salkart-full.png" ></div> - </div> - </div> + <div style="display:none;"><img id="source" src="img/tg15-salkart-full.png" ></div> + </div> </div><!--/.fluid-container--> <script src="js/jquery.min.js" type="text/javascript"></script> <script src="js/bootstrap.min.js" type="text/javascript"></script> @@ -278,7 +499,7 @@ <script type="text/javascript" src="js/nms-color-util.js"></script> <script type="text/javascript" src="js/nms-map-handlers.js"></script> <script type="text/javascript"> - initNMS(); +initNMS(); </script> -</body> + </body> </html> diff --git a/web/nms.gathering.org/nms2/js/nms-color-util.js b/web/nms.gathering.org/nms2/js/nms-color-util.js index 28f7e1b..8ac4ab8 100644 --- a/web/nms.gathering.org/nms2/js/nms-color-util.js +++ b/web/nms.gathering.org/nms2/js/nms-color-util.js @@ -1,4 +1,20 @@ +/* + * Some stolen colors that look OK. + * + * PS: Stolen from boostrap, because we use bootstrap and these look good + * and match. + */ +var lightblue = "#d9edf7"; +var lightgreen = "#dff0d8"; +var lightred = "#f2dede"; +var lightorange = "#fcf8e3"; +var blue = "#337ab7"; +var green = "#5cb85c"; +var teal = "#5bc0de"; // Or whatever the hell that is +var orange = "#f0ad4e"; +var red = "#d9534f"; + function gradient_from_latency(latency_ms, latency_secondary_ms) { if (latency_secondary_ms === undefined) { @@ -12,21 +28,21 @@ function gradient_from_latency(latency_ms, latency_secondary_ms) function rgb_from_latency(latency_ms) { if (latency_ms === null || latency_ms === undefined) { - return '#0000ff'; + return blue; } - var l = latency_ms / 40.0; + var l = latency_ms / 50.0; if (l >= 2.0) { return 'rgb(255, 0, 0)'; } else if (l >= 1.0) { l = 2.0 - l; l = Math.pow(l, 1.0/2.2); - l = Math.floor(l * 255.0); + l = Math.floor(l * 205.0); return 'rgb(255, ' + l + ', 0)'; } else { l = Math.pow(l, 1.0/2.2); l = Math.floor(l * 255.0); - return 'rgb(' + l + ', 255, 0)'; + return 'rgb(' + l + ', 205, 0)'; } } @@ -42,13 +58,21 @@ function rgb_from_max(x) return 'rgb(' + Math.floor(colorred) + ", 0, " + Math.floor(colorblue) + ')'; } +function rgb_from_max2(x) +{ + x = x/100; + var colorred = 255 * x; + var colorgreen = 250 - colorred; + + return 'rgb(' + Math.floor(colorred) + "," + Math.floor(colorgreen) + ', 0 )'; +} /* * Return a random-ish color (for testing) */ function getRandomColor() { - var i = Math.round(Math.random() * 5); - var colors = [ "white", "red", "pink", "yellow", "orange", "green" ]; + var colors = [ "white", red, teal, orange, green, blue ]; + var i = Math.round(Math.random() * (colors.length-1)); return colors[i]; } diff --git a/web/nms.gathering.org/nms2/js/nms-map-handlers.js b/web/nms.gathering.org/nms2/js/nms-map-handlers.js index 762788a..f85b06f 100644 --- a/web/nms.gathering.org/nms2/js/nms-map-handlers.js +++ b/web/nms.gathering.org/nms2/js/nms-map-handlers.js @@ -53,6 +53,7 @@ var handler_comment = { init:commentInit, name:"Fresh comment spotter" }; + /* * Update function for uplink map * Run periodically when uplink map is active. @@ -78,15 +79,15 @@ function uplinkUpdater() } } if (uplinks == 0) { - setSwitchColor(sw,"blue"); + setSwitchColor(sw,"white"); } else if (uplinks == 1) { - setSwitchColor(sw,"red"); + setSwitchColor(sw,red); } else if (uplinks == 2) { - setSwitchColor(sw, "yellow"); + setSwitchColor(sw, orange); } else if (uplinks == 3) { - setSwitchColor(sw, "green"); + setSwitchColor(sw, green); } else if (uplinks > 3) { - setSwitchColor(sw, "white"); + setSwitchColor(sw, blue); } } } @@ -94,13 +95,26 @@ function uplinkUpdater() /* * Init-function for uplink map */ +function uplinkInit() +{ + setLegend(1,"white","0 uplinks"); + setLegend(2,red,"1 uplink"); + setLegend(3,orange,"2 uplinks"); + setLegend(4,green,"3 uplinks"); + setLegend(5,blue,"4 uplinks"); +} + +/* + * Init-function for uplink map + */ function trafficInit() { - setLegend(1,"blue","0 (N/A)"); - setLegend(5,"red", "1000Mb/s or more"); - setLegend(4,"yellow","100Mb/s to 800Mb/s"); - setLegend(3,"green", "5Mb/s to 100Mb/s"); - setLegend(2,"white","0 to 5Mb/s"); + var m = 1024 * 1024 / 8; + setLegend(1,colorFromSpeed(0),"0 (N/A)"); + setLegend(5,colorFromSpeed(2000 * m) , "2000Mb/s"); + setLegend(4,colorFromSpeed(1200 * m),"1200Mb/s"); + setLegend(3,colorFromSpeed(500 * m),"500Mb/s"); + setLegend(2,colorFromSpeed(10 * m),"10Mb/s"); } function trafficUpdater() @@ -115,37 +129,27 @@ function trafficUpdater() /ge-0\/0\/46$/.exec(port) || /ge-0\/0\/47$/.exec(port)) { + if (!nms.switches_then["switches"][sw] || + !nms.switches_then["switches"][sw]["ports"] || + !nms.switches_then["switches"][sw]["ports"][port]) + continue; var t = nms.switches_then["switches"][sw]["ports"][port]; var n = nms.switches_now["switches"][sw]["ports"][port]; speed += (parseInt(t["ifhcoutoctets"]) -parseInt(n["ifhcoutoctets"])) / (parseInt(t["time"] - n["time"])); speed += (parseInt(t["ifhcinoctets"]) -parseInt(n["ifhcinoctets"])) / (parseInt(t["time"] - n["time"])); } } - var m = 1024 * 1024 / 8; - if (speed == 0) { - setSwitchColor(sw,"blue"); - } else if (speed > (1000 * m)) { - setSwitchColor(sw,"red"); - } else if (speed > (800 * m)) { - setSwitchColor(sw, "yellow"); - } else if (speed > (5 * m)) { - setSwitchColor(sw, "green"); - } else { - setSwitchColor(sw, "white"); - } + setSwitchColor(sw,colorFromSpeed(speed)); } } -/* - * Init-function for uplink map - */ -function uplinkInit() +function colorFromSpeed(speed) { - setLegend(1,"blue","0 uplinks"); - setLegend(2,"red","1 uplink"); - setLegend(3,"yellow","2 uplinks"); - setLegend(4,"green","3 uplinks"); - setLegend(5,"white","4 uplinks"); + var m = 1024 * 1024 / 8; + if (speed == 0) + return blue; + speed = speed < 0 ? 0 : speed; + return rgb_from_max2( 100 * (speed / (2 * (1000 * m)))); } @@ -158,7 +162,7 @@ function temp_color(t) { if (t == undefined) { console.log("Temp_color, but temp is undefined"); - return "blue"; + return blue; } t = parseInt(t) - 20; t = Math.floor((t / 15) * 100); @@ -189,13 +193,13 @@ function tempInit() function pingUpdater() { for (var sw in nms.switches_now["switches"]) { - var c = "blue"; + var c = blue; if (nms.ping_data['switches'] && nms.ping_data['switches'][sw]) c = gradient_from_latency(nms.ping_data["switches"][sw]["latency"]); setSwitchColor(sw, c); } for (var ln in nms.switches_now["linknets"]) { - var c1 = "blue"; + var c1 = blue; var c2 = c1; if (nms.ping_data['linknets'] && nms.ping_data['linknets'][ln]) { c1 = gradient_from_latency(nms.ping_data["linknets"][ln][0]); @@ -210,53 +214,54 @@ function pingInit() setLegend(1,gradient_from_latency(1),"1ms"); setLegend(2,gradient_from_latency(30),"30ms"); setLegend(3,gradient_from_latency(60),"60ms"); - setLegend(4,gradient_from_latency(80),"80ms"); - setLegend(5,"#0000ff" ,"No response"); + setLegend(4,gradient_from_latency(100),"100ms"); + setLegend(5,blue ,"No response"); } function commentUpdater() { var realnow = Date.now(); - if (nms.now) { - realnow = Date.parse(nms.now); - } var now = Math.floor(realnow / 1000); for (var sw in nms.switches_now["switches"]) { - var c = "green"; + var c = "white"; var s = nms.switches_now["switches"][sw]; if (s["comments"] && s["comments"].length > 0) { var then = 0; + var active = 0; + var persist = 0; c = "yellow"; for (var v in s["comments"]) { var then_test = parseInt(s["comments"][v]["time"]); - if (then_test > then && then_test <= now) + if (then_test > then && s["comments"][v]["state"] != "inactive") then = then_test; + if (s["comments"][v]["state"] == "active") { + active++; + } + if (s["comments"][v]["state"] == "persist") + persist++; } if (then > (now - (60*15))) { - c = "red"; - } else if (then > (now - (120*60))) { - c = "orange"; - } else if (then < (now - (60*60*24))) { - c = "white"; + c = red; + } else if (active > 0) { + c = orange; + } else if (persist > 0) { + c = blue; + } else { + c = green; } - /* - * Special case during time travel: We have - * comments, but are not showing them yet. - */ - if (then == 0) - c = "green"; } setSwitchColor(sw, c); } } + function commentInit() { - setLegend(1,"green","0 comments"); - setLegend(2,"white","1d+ old"); - setLegend(3,"red", "0 - 15m old"); - setLegend(4,"orange","15m - 120m old"); - setLegend(5,"yellow" ,"2h - 24h old"); + setLegend(1,"white","0 comments"); + setLegend(2,blue,"Persistent comments"); + setLegend(3,red, "New comments"); + setLegend(4,orange,"Active comments"); + setLegend(5,green ,"Old/inactive only"); } /* * Testing-function to randomize colors of linknets and switches @@ -274,10 +279,10 @@ function randomizeColors() function discoInit() { setNightMode(true); - setLegend(1,"blue","Y"); - setLegend(2,"red", "M"); - setLegend(3,"yellow","C"); - setLegend(4,"green", "A"); + setLegend(1,blue,"Y"); + setLegend(2,red, "M"); + setLegend(3,orange,"C"); + setLegend(4,green, "A"); setLegend(5,"white","!"); } diff --git a/web/nms.gathering.org/nms2/js/nms.js b/web/nms.gathering.org/nms2/js/nms.js index 41b39d6..cf2690e 100644 --- a/web/nms.gathering.org/nms2/js/nms.js +++ b/web/nms.gathering.org/nms2/js/nms.js @@ -1,11 +1,14 @@ var nms = { updater:undefined, // Active updater + update_time:0, // Client side timestamp for last update switches_now:undefined, // Most recent data switches_then:undefined, // 2 minutes old speed:0, // Current aggregated speed ping_data:undefined, // JSON data for ping history. drawn:false, // Set to 'true' when switches are drawn switch_showing:"", // Which switch we are displaying (if any). + repop_switch:false, // True if we need to repopulate the switch info when port state updates (e.g.: added comments); + repop_time:false, // Timestamp in case we get a cached result nightMode:false, /* * Switch-specific variables. These are currently separate from @@ -13,6 +16,8 @@ var nms = { * new data. */ nightBlur:{}, // Have we blurred this switch or not? + shadowBlur:10, + shadowColor:"#EEEEEE", switch_color:{}, // Color for switch linknet_color:{}, // color for linknet textDrawn:{}, // Have we drawn text for this switch? @@ -44,14 +49,40 @@ var nms = { timers: { replay:false, ports:false, - ping:false, - map:false, - speed:false + ping:false }, - deleteComment:0 + menuShowing:true, + /* + * This is a list of nms[x] variables that we store in our + * settings-cookie when altered and restore on load. + */ + settingsList:[ + 'shadowBlur', + 'shadowColor', + 'nightMode', + 'menuShowing', + 'layerVisibility' + ], + layerVisibility:{}, + keyBindings:{ + '?':toggleMenu, + 'n':toggleNightMode, + '1':setMapModeFromN, + '2':setMapModeFromN, + '3':setMapModeFromN, + '4':setMapModeFromN, + '5':setMapModeFromN, + '6':setMapModeFromN, + 'h':moveTimeFromKey, + 'j':moveTimeFromKey, + 'k':moveTimeFromKey, + 'l':moveTimeFromKey, + 'p':moveTimeFromKey, + 'r':moveTimeFromKey, + 'default':keyDebug + } }; - /* * Returns a handler object. * @@ -85,6 +116,7 @@ function nmsTimer(handler, interval, name, description) { }; } + /* * Drawing primitives. * @@ -173,6 +205,9 @@ function initDrawing() { dr['top'] = {}; dr['top']['c'] = document.getElementById("topCanvas"); dr['top']['ctx'] = dr['top']['c'].getContext('2d'); + dr['input'] = {}; + dr['input']['c'] = document.getElementById("inputCanvas"); + dr['input']['ctx'] = dr['top']['c'].getContext('2d'); } /* @@ -196,6 +231,7 @@ function byteCount(bytes) { function toggleNightMode() { setNightMode(!nms.nightMode); + saveSettings(); } /* @@ -226,7 +262,9 @@ function checkNow(now) */ function stringToEpoch(t) { - var ret = new Date(Date.parse(t)); + var foo = t.toString(); + foo = foo.replace('T',' '); + var ret = new Date(Date.parse(foo)); return parseInt(parseInt(ret.valueOf()) / 1000); } @@ -266,6 +304,7 @@ function epochToString(t) */ function timeReplay() { + replayTime = stringToEpoch(nms.now); if (replayTime >= tgEnd) { nms.timers.replay.stop(); return; @@ -289,7 +328,7 @@ function timeReplay() function startReplay() { nms.timers.replay.stop(); resetColors(); - replayTime = tgStart; + nms.now = epochToString(tgStart); timeReplay(); nms.timers.replay.start();; } @@ -314,6 +353,21 @@ function changeNow() { } } +function stepTime(n) +{ + var now; + nms.timers.replay.stop(); + if (nms.now && nms.now != 0) + now = parseInt(stringToEpoch(nms.now)); + else if (nms.switches_now) + now = parseInt(stringToEpoch(/^[^.]*/.exec(nms.switches_now.time))); + else + return; + newtime = parseInt(now) + parseInt(n); + nms.now = epochToString(parseInt(newtime)); + updatePorts(); +} + /* * Hide switch info-box */ @@ -335,7 +389,7 @@ function hideSwitch() /* * Display info on switch "x" in the info-box */ -function switchInfo(x) +function showSwitch(x) { var sw = nms.switches_now["switches"][x]; var swtop = document.getElementById("info-switch-parent"); @@ -345,13 +399,8 @@ function switchInfo(x) var td1; var td2; - if (nms.switch_showing == x) { - hideSwitch(); - return; - } else { - hideSwitch(); - nms.switch_showing = x; - } + hideSwitch(); + nms.switch_showing = x; document.getElementById("aboutBox").style.display = "none"; var switchele = document.createElement("table"); switchele.id = "info-switch-table"; @@ -490,16 +539,19 @@ function switchInfo(x) if (comment["state"] == "active") col = "danger"; else if (comment["state"] == "inactive") - col = "active"; + col = false; else col = "info"; tr.className = col; + tr.id = "commentRow" + comment["id"]; td1 = tr.insertCell(0); td2 = tr.insertCell(1); - var txt = '<div class="btn-group" role="group" aria-label="..."><button type="button" class="btn btn-xs" data-trigger="focus" data-toggle="popover" title="Info" data-content="Comment added ' + epochToString(comment["time"]) + " by user " + comment["username"] + ' and listed as ' + comment["state"] + '">?</button>'; - txt += '<button type="button" class="btn btn-xs" data-trigger="focus" data-toggle="tooltip" title="Mark as deleted" onclick="commentDelete(' + comment["id"] + ');">X</button>'; - txt += '<button type="button" class="btn btn-xs" data-trigger="focus" data-toggle="tooltip" title="Mark as inactive/fixed" onclick="commentInactive(' + comment["id"] + ');">V</button>'; - txt += '<button type="button" class="btn btn-xs" data-trigger="focus" data-toggle="tooltip" title="Mark as persistent" onclick="commentPersist(' + comment["id"] + ');">!</button></div>'; + td1.style.whiteSpace = "nowrap"; + td1.style.width = "8em"; + var txt = '<div class="btn-group" role="group" aria-label="..."><button type="button" class="btn btn-xs btn-default" data-trigger="focus" data-toggle="popover" title="Info" data-content="Comment added ' + epochToString(comment["time"]) + " by user " + comment["username"] + ' and listed as ' + comment["state"] + '"><span class="glyphicon glyphicon-info-sign" aria-hidden="true"></span></button>'; + txt += '<button type="button" class="btn btn-xs btn-danger" data-trigger="focus" data-toggle="tooltip" title="Mark as deleted" onclick="commentDelete(' + comment["id"] + ');"><span class="glyphicon glyphicon-remove" aria-hidden="true"></span></button>'; + txt += '<button type="button" class="btn btn-xs btn-success" data-trigger="focus" data-toggle="tooltip" title="Mark as inactive/fixed" onclick="commentInactive(' + comment["id"] + ');"><span class="glyphicon glyphicon-ok" aria-hidden="true"></span></button>'; + txt += '<button type="button" class="btn btn-xs btn-info" data-trigger="focus" data-toggle="tooltip" title="Mark as persistent" onclick="commentPersist(' + comment["id"] + ');"><span class="glyphicon glyphicon-star" aria-hidden="true"></span></button></div>'; td1.innerHTML = txt; td2.textContent = comment['comment']; } @@ -514,7 +566,7 @@ function switchInfo(x) commentbox.id = "commentbox"; commentbox.className = "panel-body"; commentbox.style.width = "100%"; - commentbox.innerHTML = '<div class="form form-inline"><div class="form-group"><input type="text" class="form-control" placeholder="Comment" id="' + x + '-comment"></div><button class="btn btn-default" onclick="addComment(\'' + x + '\',document.getElementById(\'' + x + '-comment\').value); document.getElementById(\'' + x + '-comment\').value = \'added. Wait for it....\';">Add comment</button></div>'; + commentbox.innerHTML = '<div class="input-group"><input type="text" class="form-control" placeholder="Comment" id="' + x + '-comment"><span class=\"input-group-btn\"><button class="btn btn-default" onclick="addComment(\'' + x + '\',document.getElementById(\'' + x + '-comment\').value); document.getElementById(\'' + x + '-comment\').value = \'\'; document.getElementById(\'' + x + '-comment\').placeholder = \'Comment added. Wait for next refresh.\';">Add comment</button></span></div>'; swtop.appendChild(commentbox); swtop.style.display = 'block'; } @@ -530,14 +582,28 @@ function setLegend(x,color,name) { var el = document.getElementById("legend-" + x); el.style.background = color; - el.innerHTML = name; + el.title = name; + el.textContent = name; } +function updateAjaxInfo() +{ + var out = document.getElementById('outstandingAJAX'); + var of = document.getElementById('overflowAJAX'); + out.textContent = nms.outstandingAjaxRequests; + of.textContent = nms.ajaxOverflow; +} /* * Run periodically to trigger map updates when a handler is active */ function updateMap() { + if (!newerSwitches()) + return; + if (!(nms.update_time < (Date.now() - 100) || nms.update_time == 0)) + return; + nms.update_time = Date.now(); + if (nms.updater != undefined && nms.switches_now && nms.switches_then) { nms.updater(); } @@ -579,6 +645,31 @@ function initialUpdate() } } +function resetBlur() +{ + nms.nightBlur = {}; + dr.blur.ctx.clearRect(0,0,canvas.width,canvas.height); + drawSwitches(); +} + +function applyBlur() +{ + var blur = document.getElementById("shadowBlur"); + var col = document.getElementById("shadowColor"); + nms.shadowBlur = blur.value; + nms.shadowColor = col.value; + resetBlur(); + saveSettings(); +} + +function showBlurBox() +{ + var blur = document.getElementById("shadowBlur"); + var col = document.getElementById("shadowColor"); + blur.value = nms.shadowBlur; + col.value = nms.shadowColor; + document.getElementById("blurManic").style.display = ''; +} /* * Update nms.ping_data */ @@ -587,6 +678,7 @@ function updatePing() var now = nms.now ? ("?now=" + nms.now) : ""; if (nms.outstandingAjaxRequests > 5) { nms.ajaxOverflow++; + updateAjaxInfo(); return; } nms.outstandingAjaxRequests++; @@ -597,50 +689,73 @@ function updatePing() success: function (data, textStatus, jqXHR) { nms.ping_data = JSON.parse(data); initialUpdate(); + updateMap(); }, complete: function(jqXHR, textStatus) { nms.outstandingAjaxRequests--; + updateAjaxInfo(); } }); } -function commentInactive(id) { +function commentInactive(id) +{ commentChange(id,"inactive"); } -function commentPersist(id) { +function commentPersist(id) +{ commentChange(id,"persist"); } -function commentDelete(id) { - if (id != nms.deleteComment) { - nms.deleteComment = id; - alert("Click the button again to delete it"); - return; + +function commentDelete(id) +{ + var r = confirm("Really delete comment? (Delted comments are still stored in the database, but never displayed)"); + if (r == true) { + commentChange(id,"delete"); } - commentChange(id,"delete"); } -function commentChange(id,state) { + +/* + * FIXME: Neither of these two handle failures in any way, shape or form. + * Nor do they really give user-feedback. They work, but only by magic. + */ +function commentChange(id,state) +{ var myData = { comment:id, state:state }; + var foo = document.getElementById("commentRow" + id); + if (foo) { + foo.className = ''; + foo.style.backgroundColor = "silver"; + } $.ajax({ type: "POST", url: "/comment-change.pl", dataType: "text", - data:myData + data:myData, + success: function (data, textStatus, jqXHR) { + nms.repop_switch = true; + } }); } -function addComment(sw,comment) { + +function addComment(sw,comment) +{ var myData = { switch:sw, - comment:comment}; - console.log(myData); + comment:comment + }; $.ajax({ type: "POST", url: "/switch-comment.pl", dataType: "text", - data:myData + data:myData, + success: function (data, textStatus, jqXHR) { + nms.repop_switch = true; + } }); } /* @@ -651,6 +766,7 @@ function updatePorts() var now = ""; if (nms.outstandingAjaxRequests > 5) { nms.ajaxOverflow++; + updateAjaxInfo(); return; } nms.outstandingAjaxRequests++; @@ -665,15 +781,26 @@ function updatePorts() nms.switches_now = switchdata; parseIntPlacements(); initialUpdate(); + updateSpeed(); + updateMap(); + if (nms.repop_time == false && nms.repop_switch) + nms.repop_time = nms.switches_now.time; + else if (nms.repop_switch && nms.switch_showing && nms.repop_time != nms.switches_now.time) { + showSwitch(nms.switch_showing,true); + nms.repop_switch = false; + nms.repop_time = false; + } }, complete: function(jqXHR, textStatus) { nms.outstandingAjaxRequests--; + updateAjaxInfo(); } }); now=""; if (nms.now != false) now = "&now=" + nms.now; nms.outstandingAjaxRequests++; + updateAjaxInfo(); $.ajax({ type: "GET", url: "/port-state.pl?time=5m" + now, @@ -682,14 +809,32 @@ function updatePorts() var switchdata = JSON.parse(data); nms.switches_then = switchdata; initialUpdate(); + updateSpeed(); + updateMap(); }, complete: function(jqXHR, textStatus) { nms.outstandingAjaxRequests--; + updateAjaxInfo(); } }) } /* + * Returns true if we have now and then-data for switches and that the + * "now" is actually newer. Useful for basic sanity and avoiding negative + * values when rewinding time. + */ +function newerSwitches() +{ + if (!nms.switches_now || !nms.switches_then) + return false; + var now_timestamp = stringToEpoch(nms.switches_now.time); + var then_timestamp = stringToEpoch(nms.switches_then.time); + if (now_timestamp == 0 || then_timestamp == 0 || then_timestamp >= now_timestamp) + return false; + return true; +} +/* * Use nms.switches_now and nms.switches_then to update 'nms.speed'. * * nms.speed is a total of ifHCInOctets across all client-interfaces @@ -703,6 +848,7 @@ function updatePorts() * FIXME: Err, yeah, add this to the tail-end of updatePorts instead :D * */ + function updateSpeed() { var speed_in = parseInt(0); @@ -710,6 +856,8 @@ function updateSpeed() var counter=0; var sw; var speedele = document.getElementById("speed"); + if (!newerSwitches()) + return; for (sw in nms.switches_now["switches"]) { for (port in nms.switches_now["switches"][sw]["ports"]) { if (!nms.switches_now["switches"][sw]["ports"][port]) { @@ -763,8 +911,8 @@ function updateSpeed() */ function drawLinknet(i) { - var c1 = nms.linknet_color[i] && nms.linknet_color[i].c1 ? nms.linknet_color[i].c1 : "blue"; - var c2 = nms.linknet_color[i] && nms.linknet_color[i].c2 ? nms.linknet_color[i].c2 : "blue"; + var c1 = nms.linknet_color[i] && nms.linknet_color[i].c1 ? nms.linknet_color[i].c1 : blue; + var c2 = nms.linknet_color[i] && nms.linknet_color[i].c2 ? nms.linknet_color[i].c2 : blue; if (nms.switches_now.switches[nms.switches_now.linknets[i].sysname1] && nms.switches_now.switches[nms.switches_now.linknets[i].sysname2]) { connectSwitches(nms.switches_now.linknets[i].sysname1,nms.switches_now.linknets[i].sysname2, c1, c2); } @@ -810,16 +958,18 @@ function drawSwitch(sw) var box = nms.switches_now['switches'][sw]['placement']; var color = nms.switch_color[sw]; if (color == undefined) { - color = "blue"; + color = blue; } dr.switch.ctx.fillStyle = color; + /* + * XXX: This is a left-over from before NMS did separate + * canvases, and might be done better elsewhere. + */ if (nms.nightMode && nms.nightBlur[sw] != true) { - dr.switch.ctx.shadowBlur = 10; - dr.switch.ctx.shadowColor = "#00EE00"; + dr.blur.ctx.shadowBlur = nms.shadowBlur; + dr.blur.ctx.shadowColor = nms.shadowColor; + drawBoxBlur(box['x'],box['y'],box['width'],box['height']); nms.nightBlur[sw] = true; - } else { - dr.switch.ctx.shadowBlur = 0; - dr.switch.ctx.shadowColor = "#000000"; } drawBox(box['x'],box['y'],box['width'],box['height']); dr.switch.ctx.shadowBlur = 0; @@ -871,18 +1021,20 @@ function drawSwitches() */ function drawNow() { + if (!nms.switches_now) + return; // XXX: Get rid of microseconds that we get from the backend. var now = /^[^.]*/.exec(nms.switches_now.time); dr.top.ctx.font = Math.round(2 * nms.fontSize * canvas.scale) + "px " + nms.fontFace; dr.top.ctx.clearRect(0,0,Math.floor(800 * canvas.scale),Math.floor(100 * canvas.scale)); dr.top.ctx.fillStyle = "white"; dr.top.ctx.strokeStyle = "black"; - dr.top.ctx.lineWidth = Math.floor(4 * canvas.scale); + dr.top.ctx.lineWidth = Math.floor(2 * canvas.scale); if (dr.top.ctx.lineWidth == 0) { - dr.top.ctx.lineWidth = Math.round(4 * canvas.scale); + dr.top.ctx.lineWidth = Math.round(2 * canvas.scale); } - dr.top.ctx.strokeText(now, 0 + margin.text, 30 * canvas.scale); - dr.top.ctx.fillText(now, 0 + margin.text, 30 * canvas.scale); + dr.top.ctx.strokeText(now, 0 + margin.text, 25 * canvas.scale); + dr.top.ctx.fillText(now, 0 + margin.text, 25 * canvas.scale); } /* * Draw foreground/scene. @@ -918,6 +1070,7 @@ function setScale() nms.textDrawn = {}; drawBG(); drawScene(); + drawNow(); document.getElementById("scaler").value = canvas.scale; document.getElementById("scaler-text").innerHTML = (parseFloat(canvas.scale)).toPrecision(3); @@ -978,7 +1131,10 @@ function scaleChange() */ function switchClick(sw) { - switchInfo(sw); + if (nms.switch_showing == sw) + hideSwitch(); + else + showSwitch(sw); } /* @@ -993,11 +1149,11 @@ function resetColors() return; if (nms.switches_now.linknets) { for (var i in nms.switches_now.linknets) { - setLinknetColors(i, "blue","blue"); + setLinknetColors(i, blue,blue); } } for (var sw in nms.switches_now.switches) { - setSwitchColor(sw, "blue"); + setSwitchColor(sw, blue); } } @@ -1058,6 +1214,14 @@ function setNightMode(toggle) { nms.nightMode = toggle; var body = document.getElementById("body"); body.style.background = toggle ? "black" : "white"; + var nav = document.getElementsByTagName("nav")[0]; + if (toggle) { + dr.blur.c.style.display = ''; + nav.classList.add('navbar-inverse'); + } else { + dr.blur.c.style.display = 'none'; + nav.classList.remove('navbar-inverse'); + } setScale(); } /* @@ -1079,6 +1243,17 @@ function drawBox(x,y,boxw,boxh) } /* + * Draw the blur for a box. + */ +function drawBoxBlur(x,y,boxw,boxh) +{ + var myX = Math.floor(x * canvas.scale); + var myY = Math.floor(y * canvas.scale); + var myX2 = Math.floor((boxw) * canvas.scale); + var myY2 = Math.floor((boxh) * canvas.scale); + dr.blur.ctx.fillRect(myX,myY, myX2, myY2); +} +/* * Draw text on a box - sideways! * * XXX: This is pretty nasty and should also probably take a box as input. @@ -1149,9 +1324,9 @@ function connectSwitches(insw1, insw2,color1, color2) { var sw1 = nms.switches_now.switches[insw1].placement; var sw2 = nms.switches_now.switches[insw2].placement; if (color1 == undefined) - color1 = "blue"; + color1 = blue; if (color2 == undefined) - color2 = "blue"; + color2 = blue; var x0 = Math.floor((sw1.x + sw1.width/2) * canvas.scale); var y0 = Math.floor((sw1.y + sw1.height/2) * canvas.scale); var x1 = Math.floor((sw2.x + sw2.width/2) * canvas.scale); @@ -1189,14 +1364,10 @@ function initNMS() { nms.timers.ping = new nmsTimer(updatePing, 1000, "Ping updater", "AJAX request to update ping data"); nms.timers.ping.start(); - nms.timers.map = new nmsTimer(updateMap, 1000, "Map handler", "Updates the map using the chosen map handler (ping, uplink, traffic, etc)"); - nms.timers.map.start(); - - nms.timers.speed = new nmsTimer(updateSpeed, 1000, "Speed updater", "Recompute total speed (no backend requests)"); - nms.timers.speed.start(); - nms.timers.replay = new nmsTimer(timeReplay, 1000, "Time machine", "Handler used to change time"); detectHandler(); + setupKeyhandler(); + restoreSettings(); } function detectHandler() { @@ -1216,9 +1387,6 @@ function detectHandler() { } else { setUpdater(handler_ping); } - if (/nightMode/.exec(url)) { - toggleNightMode(); - } } /* @@ -1227,7 +1395,7 @@ function detectHandler() { * Could probably be cleaned up. */ function showTimerDebug() { - var tableTop = document.getElementById('timerTableTop'); + var tableTop = document.getElementById('debugTimers'); var table = document.getElementById('timerTable'); var tr, td1, td2; if (table) @@ -1238,38 +1406,29 @@ function showTimerDebug() { table.className = "table"; table.classList.add("table"); table.classList.add("table-default"); - table.border = "1"; - tr = document.createElement("tr"); - td = document.createElement("th"); + var header = table.createTHead(); + tr = header.insertRow(0); + td = tr.insertCell(0); td.innerHTML = "Handler"; - tr.appendChild(td); - td = document.createElement("th"); + td = tr.insertCell(1); td.innerHTML = "Interval (ms)"; - tr.appendChild(td); - td = document.createElement("th"); + td = tr.insertCell(2); td.innerHTML = "Name"; - tr.appendChild(td); - td = document.createElement("th"); + td = tr.insertCell(3); td.innerHTML = "Description"; - tr.appendChild(td); - table.appendChild(tr); for (var v in nms.timers) { - console.log(v); - tr = document.createElement("tr"); - td = document.createElement("td"); - td.innerHTML = nms.timers[v].handle; - tr.appendChild(td); - td = document.createElement("td"); - td.innerHTML = "<input type=\"text\" id='handlerValue" + v + "' value='" + nms.timers[v].interval + "'>"; - td.innerHTML += "<button type=\"button\" class=\"btn btn-default\" onclick=\"nms.timers['" + v + "'].setInterval(document.getElementById('handlerValue" + v + "').value);\">Apply</button>"; - tr.appendChild(td); - td = document.createElement("td"); - td.innerHTML = nms.timers[v].name; - tr.appendChild(td); - td = document.createElement("td"); - td.innerHTML = nms.timers[v].description; - tr.appendChild(td); - table.appendChild(tr); + tr = table.insertRow(-1); + td = tr.insertCell(0); + td.textContent = nms.timers[v].handle; + td = tr.insertCell(1); + td.style.width = "15em"; + var tmp = "<div class=\"input-group\"><input type=\"text\" id='handlerValue" + v + "' value='" + nms.timers[v].interval + "' class=\"form-control\"></input>"; + tmp += "<span class=\"input-group-btn\"><button type=\"button\" class=\"btn btn-default\" onclick=\"nms.timers['" + v + "'].setInterval(document.getElementById('handlerValue" + v + "').value);\">Apply</button></span></div>"; + td.innerHTML = tmp; + td = tr.insertCell(2); + td.textContent = nms.timers[v].name; + td = tr.insertCell(3); + td.textContent = nms.timers[v].description; } tableTop.appendChild(table); document.getElementById('debugTimers').style.display = 'block'; @@ -1278,9 +1437,169 @@ function showTimerDebug() { function hideLayer(layer) { var l = document.getElementById(layer); l.style.display = "none"; + if (layer != "layerVisibility") + nms.layerVisibility[layer] = false; + saveSettings(); } function showLayer(layer) { var l = document.getElementById(layer); l.style.display = ""; + if (layer != "layerVisibility") + nms.layerVisibility[layer] = true; + saveSettings(); +} + +function toggleLayer(layer) { + var l = document.getElementById(layer); + if (l.style.display == 'none') + l.style.display = ''; + else + l.style.display = 'none'; +} + +function applyLayerVis() +{ + for (var l in nms.layerVisibility) { + var layer = document.getElementById(l); + if (layer) + layer.style.display = nms.layerVisibility[l] ? '' : 'none'; + } +} + +function setMenu() +{ + var nav = document.getElementsByTagName("nav")[0]; + nav.style.display = nms.menuShowing ? '' : 'none'; + resizeEvent(); + +} +function toggleMenu() +{ + nms.menuShowing = ! nms.menuShowing; + setMenu(); + saveSettings(); +} + +function setMapModeFromN(e,key) +{ + switch(key) { + case '1': + setUpdater(handler_ping); + break; + case '2': + setUpdater(handler_uplinks); + break; + case '3': + setUpdater(handler_temp); + break; + case '4': + setUpdater(handler_traffic); + break; + case '5': + setUpdater(handler_comment); + break; + case '6': + setUpdater(handler_disco); + break; + } + return true; +} + +function moveTimeFromKey(e,key) +{ + switch(key) { + case 'h': + stepTime(-3600); + break; + case 'j': + stepTime(-300); + break; + case 'k': + stepTime(300); + break; + case 'l': + stepTime(3600); + break; + case 'p': + if(nms.timers.replay.handle) + nms.timers.replay.stop(); + else { + timeReplay(); + nms.timers.replay.start(); + } + break; + case 'r': + nms.timers.replay.stop(); + nms.now = false; + updatePorts(); + break; + } + return true; +} + +var debugEvent; +function keyDebug(e) +{ + console.log(e); + debugEvent = e; +} + +function keyPressed(e) +{ + if (e.target.nodeName == "INPUT") { + return false; + } + var key = String.fromCharCode(e.keyCode); + if (nms.keyBindings[key]) + return nms.keyBindings[key](e,key); + if (nms.keyBindings['default']) + return nms.keyBindings['default'](e,key); + return false; +} + +function setupKeyhandler() +{ + var b = document.getElementsByTagName("body")[0]; + b.onkeypress = function(e){keyPressed(e);}; +} + + +function getCookie(cname) { + var name = cname + "="; + var ca = document.cookie.split(';'); + for(var i=0; i<ca.length; i++) { + var c = ca[i]; + while (c.charAt(0)==' ') + c = c.substring(1); + if (c.indexOf(name) == 0) + return c.substring(name.length,c.length); + } + return ""; +} + +function saveSettings() +{ + var foo={}; + for (var v in nms.settingsList) { + foo[nms.settingsList[v]] = nms[nms.settingsList[v]]; + } + document.cookie = 'nms='+btoa(JSON.stringify(foo)); +} + +function restoreSettings() +{ + var retrieve = JSON.parse(atob(getCookie("nms"))); + for (var v in retrieve) { + nms[v] = retrieve[v]; + } + setScale(); + setMenu(); + setNightMode(nms.nightMode); + applyLayerVis(); +} + +function forgetSettings() +{ + document.cookie = 'nms=' + btoa('{}'); } diff --git a/web/nms.gathering.org/port-state.pl b/web/nms.gathering.org/port-state.pl index b21bd8d..9df31c0 100755 --- a/web/nms.gathering.org/port-state.pl +++ b/web/nms.gathering.org/port-state.pl @@ -69,7 +69,12 @@ while (my $ref = $q4->fetchrow_hashref()) { # push @{$json{'linknets'}}, $ref; } -my $q5 = $dbh->prepare ('select ' . $now . ' as time;'); +my $q5; +if (defined($cin)) { + $q5 = $dbh->prepare ('select (' . $now . ' - \'' . $cin . '\'::interval) as time;'); +} else { + $q5 = $dbh->prepare ('select ' . $now . ' as time;'); +} $q5->execute(); $json{'time'} = $q5->fetchrow_hashref()->{'time'}; diff --git a/web/nms.gathering.org/uplinkkart-text.pl b/web/nms.gathering.org/uplinkkart-text.pl index de91e92..c4b31a9 100755 --- a/web/nms.gathering.org/uplinkkart-text.pl +++ b/web/nms.gathering.org/uplinkkart-text.pl @@ -17,7 +17,7 @@ print <<"EOF"; <map name="switches"> EOF -my $q = $dbh->prepare("SELECT * FROM switches NATURAL JOIN placements WHERE switchtype = 'dlink3100'"); +my $q = $dbh->prepare("SELECT * FROM switches NATURAL JOIN placements WHERE switchtype = 'ex2200'"); $q->execute(); while (my $ref = $q->fetchrow_hashref()) { $ref->{'placement'} =~ /\((\d+),(\d+)\),\((\d+),(\d+)\)/; |