aboutsummaryrefslogtreecommitdiffstats
path: root/web
diff options
context:
space:
mode:
authorKristian Lyngstol <kristian@bohemians.org>2015-05-03 11:18:54 +0200
committerKristian Lyngstol <kristian@bohemians.org>2015-05-03 11:18:54 +0200
commit4c08d48e712b2475d61cbba8785549be12021e60 (patch)
treed7c212f755953b9dd14d5b1d4e5fe2c4087eab81 /web
parentc3684f65dabfffd3a2df5a22685194547c7d213c (diff)
parent2c9231ff1b71e608b369df8666b3b1ec2f32bb74 (diff)
Merge branch 'master' of github.com:tech-server/tgmanage
Diffstat (limited to 'web')
-rw-r--r--web/etc/varnish/default.vcl2
-rw-r--r--web/nms.gathering.org/nms2/img/tg15-salkart-clean-big.pngbin0 -> 84362 bytes
-rw-r--r--web/nms.gathering.org/nms2/index.html659
-rw-r--r--web/nms.gathering.org/nms2/js/nms-color-util.js111
-rw-r--r--web/nms.gathering.org/nms2/js/nms-map-handlers.js200
-rw-r--r--web/nms.gathering.org/nms2/js/nms.js578
-rwxr-xr-xweb/nms.gathering.org/port-state.pl7
-rwxr-xr-xweb/nms.gathering.org/uplinkkart-text.pl2
8 files changed, 1134 insertions, 425 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/img/tg15-salkart-clean-big.png b/web/nms.gathering.org/nms2/img/tg15-salkart-clean-big.png
new file mode 100644
index 0000000..8d647a3
--- /dev/null
+++ b/web/nms.gathering.org/nms2/img/tg15-salkart-clean-big.png
Binary files differ
diff --git a/web/nms.gathering.org/nms2/index.html b/web/nms.gathering.org/nms2/index.html
index 42d7a35..304cece 100644
--- a/web/nms.gathering.org/nms2/index.html
+++ b/web/nms.gathering.org/nms2/index.html
@@ -21,91 +21,109 @@
<!--[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="#traffictot" onclick="setUpdater(handler_traffic_tot)">Total switch traffic</a></li>
+ <li><a href="#disco" onclick="setUpdater(handler_disco)">DISCO</a></li>
+ <li class="divider"> </li>
+ <li><a href="#" onclick="toggleLayer('nowPickerBox');document.getElementById('nowPicker').focus();">Travel in time</a></li>
+ <li><a href="#" onclick="startReplay();" title="Replay from opening 120 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">&times;</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">&times;</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 +148,374 @@
<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="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">&times;</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">&times;</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 total switch traffic map</td>
+ </tr>
+ <tr>
+ <td>7</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" style="position: absolute; display: none; z-index: 130;" class="col-md-4">
+ <div class="panel panel-default">
+ <div class="panel-heading">
+ <h3 class="panel-title">Time travel
+ <button type="button" class="close" aria-labe="Close" onclick="document.getElementById('nowPickerBox').style.display = 'none';" style="float: right;">
+ <span aria-hidden="true">&times;</span>
+ </button>
+ </h3>
+ </div>
+ <div class="panel-body">
+ <p>Some features do not have time travel support (comment
+ spotting and DHCP map at the moment). We also lack
+ compatible SNMP data for the first day or so, so you'll
+ only have ping data for the first day of TG15.</p>
+ <p>It could take some time to load a specific point in time
+ for the first time. See "About performance" under the help
+ menu for more information.</p>
+ <p>You can also step backwards and forwards in time, stop
+ and start replay and go back to real time using keyboard
+ shortcuts. See the help menu for an overview of keyboard
+ shortcuts.</p>
+ <div class="input-group">
+ <input type="text" class="form-control" placeholder="YYYY-MM-DD hh:mm:ss" id="nowPicker">
+ <span class="input-group-btn">
+ <button class="btn btn-default" onclick="changeNow();">Travel</button>
+ </span>
</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>
+ </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">&times;</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>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-&gt;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>
+ <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">&times;</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>Create name spaces in nms.*: It's just barely better
+ than global stuff now.</li>
+ <li>Add DHCP map</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-&gt;quote()</li>
+ </ul>
+ <h3>Todo for backend:</h3>
+ <ul>
+ <li>IPv6 support</li>
+ <li>Provide public API's</li>
+ <li>Investigate a json tree filter/massager</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? (Probably not needed)</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">&times;</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">&times;</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">&times;</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">&times;</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">&times;</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>
+ <canvas id="hiddenCanvas" width="1000" height="10" style="display: none; position: absolute; z-index: 1000 "></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-clean-big.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 +523,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..72d6f61 100644
--- a/web/nms.gathering.org/nms2/js/nms-color-util.js
+++ b/web/nms.gathering.org/nms2/js/nms-color-util.js
@@ -1,54 +1,91 @@
+/*
+ * 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) {
- return rgb_from_latency(latency_ms);
- }
- return 'linear-gradient(' +
- rgb_from_latency(latency_ms) + ', ' +
- rgb_from_latency(latency_secondary_ms) + ')';
+ if (latency_ms == undefined)
+ return blue;
+ return getColorStop(parseInt(latency_ms) * 10);
}
-function rgb_from_latency(latency_ms)
+/*
+ * Return a random-ish color (for testing)
+ */
+function getRandomColor()
{
- if (latency_ms === null || latency_ms === undefined) {
- return '#0000ff';
- }
-
- var l = latency_ms / 40.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);
- 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)';
- }
+ var colors = [ "white", red, teal, orange, green, blue ];
+ var i = Math.round(Math.random() * (colors.length-1));
+ return colors[i];
}
/*
- * Give us a color from blue (0) to red (100).
+ * Set up the hidden gradient canvas, using an array as input.
+ *
+ * This gives us a flexible way to get gradients between any number of
+ * colors (green to red, or blue to green to orange to red to white to pink
+ * to black and so on).
+ *
+ * Typically called when setting up a map handler. Currently "single
+ * tenant", since there's just one canvas.
+ *
+ * XXX: We have to store the gradients in nms.* and restore this when we
+ * resize for the moment, because this canvas is also re-sized (which isn't
+ * really necessary, but avoids special handling).
*/
-function rgb_from_max(x)
+function drawGradient(gradients)
{
- x = x/100;
- var colorred = 255 * x;
- var colorblue = 255 - colorred;
-
- return 'rgb(' + Math.floor(colorred) + ", 0, " + Math.floor(colorblue) + ')';
+ var gradient = dr.hidden.ctx.createLinearGradient(0,0,1000,0);
+ var stops = gradients.length - 1;
+ nms.gradients = gradients;
+ for (var color in gradients) {
+ var i = color / stops;
+ gradient.addColorStop(i, gradients[color]);
+ }
+ dr.hidden.ctx.beginPath();
+ dr.hidden.ctx.strokeStyle = gradient;
+ dr.hidden.ctx.moveTo(0,0);
+ dr.hidden.ctx.lineTo(1000,0);
+ dr.hidden.ctx.lineWidth = 10;
+ dr.hidden.ctx.closePath();
+ dr.hidden.ctx.stroke();
+ dr.hidden.ctx.moveTo(0,0);
}
/*
- * Return a random-ish color (for testing)
+ * Get the color of a gradient, range is from 0 to 999 (inclusive).
*/
-function getRandomColor()
-{
- var i = Math.round(Math.random() * 5);
- var colors = [ "white", "red", "pink", "yellow", "orange", "green" ];
- return colors[i];
+function getColorStop(x) {
+ x = parseInt(x);
+ if (x > 999)
+ x = 999;
+ if (x < 0)
+ x = 0;
+ return getColor(x,0);
}
+/*
+ * Get the color on the hidden canvas at a specific point. Could easily be
+ * made generic.
+ */
+function getColor(x,y) {
+ var imageData = dr.hidden.ctx.getImageData(x, y, 1, 1);
+ var data = imageData.data;
+ if (data.length < 4)
+ return false;
+ var ret = 'rgb(' + data[0] + ',' + data[1] + ',' + data[2] + ')';
+ return ret;
+}
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..9b2d44c 100644
--- a/web/nms.gathering.org/nms2/js/nms-map-handlers.js
+++ b/web/nms.gathering.org/nms2/js/nms-map-handlers.js
@@ -21,38 +21,62 @@
var handler_uplinks = {
updater:uplinkUpdater,
init:uplinkInit,
+ tag:"uplink",
name:"Uplink map"
};
var handler_temp = {
updater:tempUpdater,
init:tempInit,
+ tag:"temp",
name:"Temperature map"
};
var handler_ping = {
updater:pingUpdater,
init:pingInit,
+ tag:"ping",
name:"IPv4 Ping map"
};
var handler_traffic = {
updater:trafficUpdater,
init:trafficInit,
+ tag:"traffic",
name:"Uplink traffic map"
};
+var handler_traffic_tot = {
+ updater:trafficTotUpdater,
+ init:trafficTotInit,
+ tag:"traffictot",
+ name:"Switch traffic map"
+};
+
var handler_disco = {
updater:randomizeColors,
init:discoInit,
+ tag:"disco",
name:"Disco fever"
};
var handler_comment = {
updater:commentUpdater,
init:commentInit,
+ tag:"comment",
name:"Fresh comment spotter"
};
+
+var handlers = [
+ handler_uplinks,
+ handler_temp,
+ handler_ping,
+ handler_traffic,
+ handler_disco,
+ handler_comment,
+ handler_traffic_tot
+ ];
+
/*
* Update function for uplink map
* Run periodically when uplink map is active.
@@ -78,15 +102,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 +118,27 @@ 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;
+ drawGradient([lightgreen,green,orange,red]);
+ setLegend(1,colorFromSpeed(0),"0 (N/A)");
+ setLegend(5,colorFromSpeed(2000 * m) , "2000Mb/s");
+ setLegend(4,colorFromSpeed(1500 * m),"1500Mb/s");
+ setLegend(3,colorFromSpeed(500 * m),"500Mb/s");
+ setLegend(2,colorFromSpeed(10 * m),"10Mb/s");
}
function trafficUpdater()
@@ -115,37 +153,62 @@ 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 trafficTotInit()
{
- 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;
+ drawGradient([lightgreen,green,orange,red]);
+ setLegend(1,colorFromSpeed(0),"0 (N/A)");
+ setLegend(5,colorFromSpeed(5000 * m,5) , "5000Mb/s");
+ setLegend(4,colorFromSpeed(3000 * m,5),"3000Mb/s");
+ setLegend(3,colorFromSpeed(1000 * m,5),"1000Mb/s");
+ setLegend(2,colorFromSpeed(100 * m,5),"100Mb/s");
+}
+
+function trafficTotUpdater()
+{
+ if (!nms.switches_now["switches"])
+ return;
+ for (sw in nms.switches_now["switches"]) {
+ var speed = 0;
+ for (port in nms.switches_now["switches"][sw]["ports"]) {
+ 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"]));
+ }
+ setSwitchColor(sw,colorFromSpeed(speed,5));
+ }
+}
+
+function colorFromSpeed(speed,factor)
+{
+ var m = 1024 * 1024 / 8;
+ if (factor == undefined)
+ factor = 2;
+ if (speed == 0)
+ return blue;
+ speed = speed < 0 ? 0 : speed;
+ return getColorStop( 1000 * (speed / (factor * (1000 * m))));
}
@@ -158,11 +221,11 @@ 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);
- return rgb_from_max(t);
+ t = parseInt(t) - 12;
+ t = Math.floor((t / 23) * 1000);
+ return getColorStop(t);
}
function tempUpdater()
@@ -179,23 +242,28 @@ function tempUpdater()
function tempInit()
{
- setLegend(1,temp_color(20),"20 °C");
- setLegend(2,temp_color(22),"22 °C");
- setLegend(3,temp_color(27),"27 °C");
- setLegend(4,temp_color(31),"31 °C");
+ drawGradient(["black",blue,lightblue,lightgreen,green,orange,red]);
+ setLegend(1,temp_color(15),"15 °C");
+ setLegend(2,temp_color(20),"20 °C");
+ setLegend(3,temp_color(25),"25 °C");
+ setLegend(4,temp_color(30),"30 °C");
setLegend(5,temp_color(35),"35 °C");
}
function pingUpdater()
{
+ if (!nms.ping_data || !nms.ping_data["switches"]) {
+ resetColors();
+ return;
+ }
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]);
@@ -207,56 +275,58 @@ function pingUpdater()
function pingInit()
{
+ drawGradient([green,lightgreen,orange,red]);
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,gradient_from_latency(undefined) ,"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");
+ setLegend(3,red, "New");
+ setLegend(4,orange,"Active");
+ setLegend(5,green ,"Old/inactive only");
}
/*
* Testing-function to randomize colors of linknets and switches
@@ -274,10 +344,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..d4cf4e8 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,41 @@ 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,
+ '7':setMapModeFromN,
+ 'h':moveTimeFromKey,
+ 'j':moveTimeFromKey,
+ 'k':moveTimeFromKey,
+ 'l':moveTimeFromKey,
+ 'p':moveTimeFromKey,
+ 'r':moveTimeFromKey,
+ 'not-default':keyDebug
+ }
};
-
/*
* Returns a handler object.
*
@@ -85,6 +117,7 @@ function nmsTimer(handler, interval, name, description) {
};
}
+
/*
* Drawing primitives.
*
@@ -147,7 +180,7 @@ var margin = {
var tgStart = stringToEpoch('2015-04-01T09:00:00');
var tgEnd = stringToEpoch('2015-04-05T12:00:00');
var replayTime = 0;
-var replayIncrement = 30 * 60;
+var replayIncrement = 60 * 60;
/*
* Convenience-function to populate the 'dr' structure.
@@ -173,6 +206,12 @@ 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');
+ dr['hidden'] = {};
+ dr['hidden']['c'] = document.getElementById("hiddenCanvas");
+ dr['hidden']['ctx'] = dr['hidden']['c'].getContext('2d');
}
/*
@@ -196,6 +235,7 @@ function byteCount(bytes) {
function toggleNightMode()
{
setNightMode(!nms.nightMode);
+ saveSettings();
}
/*
@@ -226,7 +266,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,12 +308,15 @@ function epochToString(t)
*/
function timeReplay()
{
+ replayTime = stringToEpoch(nms.now);
if (replayTime >= tgEnd) {
nms.timers.replay.stop();
return;
}
replayTime = parseInt(replayTime) + parseInt(replayIncrement);
nms.now = epochToString(replayTime);
+ updatePorts();
+ updatePing();
}
/*
@@ -289,9 +334,11 @@ function timeReplay()
function startReplay() {
nms.timers.replay.stop();
resetColors();
- replayTime = tgStart;
+ nms.now = epochToString(tgStart);
timeReplay();
nms.timers.replay.start();;
+ nms.timers.ping.stop();;
+ nms.timers.ports.stop();;
}
/*
@@ -307,13 +354,36 @@ function changeNow() {
newnow = false;
nms.now = newnow;
+ if (newnow) {
+ nms.timers.ping.stop();;
+ nms.timers.ports.stop();;
+ }
updatePorts();
+ updatePing();
var boxHide = document.getElementById("nowPickerBox");
if (boxHide) {
boxHide.style.display = "none";
}
}
+function stepTime(n)
+{
+ var now;
+ nms.timers.replay.stop();
+ nms.timers.ping.stop();;
+ nms.timers.ports.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();
+ updatePing();
+}
+
/*
* Hide switch info-box
*/
@@ -335,7 +405,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 +415,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 +555,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 +582,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,16 +598,46 @@ 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()
{
+ /*
+ * XXX: This a bit hacky: There are a bunch of links that use
+ * href="#foo" but probably shouldn't. This breaks refresh since we
+ * base this on the location hash. To circumvent that issue
+ * somewhat, we just update the location hash if it's not
+ * "correct".
+ */
+ if (nms.updater) {
+ if (document.location.hash != ('#' + nms.updater.tag)) {
+ document.location.hash = nms.updater.tag;
+ }
+ }
+ if (!newerSwitches())
+ return;
+ if (!nms.drawn)
+ setScale();
+ if (!nms.drawn)
+ return;
+ if (!nms.ping_data)
+ return;
+ nms.update_time = Date.now();
+
if (nms.updater != undefined && nms.switches_now && nms.switches_then) {
- nms.updater();
+ nms.updater.updater();
}
drawNow();
}
@@ -552,12 +650,13 @@ function setUpdater(fo)
nms.updater = undefined;
resetColors();
fo.init();
- nms.updater = fo.updater;
+ nms.updater = fo;
var foo = document.getElementById("updater_name");
foo.innerHTML = fo.name + " ";
+ document.location.hash = fo.tag;
initialUpdate();
if (nms.ping_data && nms.switches_then && nms.switches_now) {
- nms.updater();
+ nms.updater.updater();
}
}
@@ -568,17 +667,43 @@ function setUpdater(fo)
*/
function initialUpdate()
{
+ return;
if (nms.ping_data && nms.switches_then && nms.switches_now && nms.updater != undefined && nms.did_update == false ) {
resizeEvent();
if (!nms.drawn) {
drawSwitches();
drawLinknets();
}
- nms.updater();
+ nms.updater.updater();
nms.did_update = true;
}
}
+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 +712,7 @@ function updatePing()
var now = nms.now ? ("?now=" + nms.now) : "";
if (nms.outstandingAjaxRequests > 5) {
nms.ajaxOverflow++;
+ updateAjaxInfo();
return;
}
nms.outstandingAjaxRequests++;
@@ -597,50 +723,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 +800,7 @@ function updatePorts()
var now = "";
if (nms.outstandingAjaxRequests > 5) {
nms.ajaxOverflow++;
+ updateAjaxInfo();
return;
}
nms.outstandingAjaxRequests++;
@@ -665,15 +815,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 +843,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 +882,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 +890,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 +945,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 +992,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 +1055,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.
@@ -907,7 +1093,6 @@ function drawScene()
*/
function setScale()
{
-
canvas.height = orig.height * canvas.scale ;
canvas.width = orig.width * canvas.scale ;
for (var a in dr) {
@@ -916,8 +1101,11 @@ function setScale()
}
nms.nightBlur = {};
nms.textDrawn = {};
+ if (nms.gradients)
+ drawGradient(nms.gradients);
drawBG();
drawScene();
+ drawNow();
document.getElementById("scaler").value = canvas.scale;
document.getElementById("scaler-text").innerHTML = (parseFloat(canvas.scale)).toPrecision(3);
@@ -978,7 +1166,10 @@ function scaleChange()
*/
function switchClick(sw)
{
- switchInfo(sw);
+ if (nms.switch_showing == sw)
+ hideSwitch();
+ else
+ showSwitch(sw);
}
/*
@@ -993,11 +1184,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 +1249,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 +1278,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 +1359,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);
@@ -1178,8 +1388,6 @@ function connectSwitches(insw1, insw2,color1, color2) {
*/
function initNMS() {
initDrawing();
- updatePorts();
- updatePing();
window.addEventListener('resize',resizeEvent,true);
document.addEventListener('load',resizeEvent,true);
@@ -1189,36 +1397,23 @@ 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");
+ nms.timers.replay = new nmsTimer(timeReplay, 500, "Time machine", "Handler used to change time");
detectHandler();
+ updatePorts();
+ updatePing();
+ setupKeyhandler();
+ restoreSettings();
}
function detectHandler() {
var url = document.URL;
- if (/#ping/.exec(url)) {
- setUpdater(handler_ping);
- }else if (/#uplink/.exec(url)) {
- setUpdater(handler_uplinks);
- } else if (/#temp/.exec(url)) {
- setUpdater(handler_temp);
- } else if (/#traffic/.exec(url)) {
- setUpdater(handler_traffic);
- } else if (/#comment/.exec(url)) {
- setUpdater(handler_comment);
- } else if (/#disco/.exec(url)) {
- setUpdater(handler_disco);
- } else {
- setUpdater(handler_ping);
- }
- if (/nightMode/.exec(url)) {
- toggleNightMode();
+ for (var i in handlers) {
+ if (('#' + handlers[i].tag) == document.location.hash) {
+ setUpdater(handlers[i]);
+ return;
+ }
}
+ setUpdater(handler_ping);
}
/*
@@ -1227,7 +1422,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 +1433,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 +1464,175 @@ 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_traffic_tot);
+ break;
+ case '7':
+ 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;
+ nms.timers.ping.start();;
+ nms.timers.ports.start();;
+ updatePorts();
+ updatePing();
+ 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+)\)/;