24 |
View Aggregates
25 | 0) {
30 | echo '
';
31 | while ($row = mysqli_fetch_assoc($result)) {
32 | $aggregate_id = $row['aggregate_id'];
33 |
34 | $url = "graph.php?aggregate_id={$aggregate_id}&height=100&width=400";
35 | if (!is_null($start)) {
36 | $url .= '&duration='.$start;
37 | }
38 |
39 | echo '';
40 | echo '';
41 | echo ' ';
42 | echo ' ';
43 | echo ' ';
44 | }
45 | echo ' ';
46 | echo '
* Shift-click a graph to start building a new aggregate.
';
47 | echo '
';
48 |
49 | } else {
50 | echo "No aggregates were found. Shift-click a graph to start building an aggregate.";
51 | }
52 | } else {
53 | echo "
Connection to FITB database failed, have you set up the database and specified the correct connection parameters in config.php?";
54 | }
55 | ?>
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
--------------------------------------------------------------------------------
/ajax/deleteAggregate.php:
--------------------------------------------------------------------------------
1 | $deleted);
9 | echo json_encode($res);
--------------------------------------------------------------------------------
/ajax/getRRDNamesForHost.php:
--------------------------------------------------------------------------------
1 | $friendlytitle,
19 | 'type' => $type,
20 | 'stack' => $stack
21 | );
22 | $agg_id = saveAggregate($graphs_array, $meta);
23 | } else {
24 | $agg_id = null;
25 | }
26 |
27 | $success = !is_null($agg_id);
28 |
29 | $res = array('success' => $success, 'aggregate_id' => $agg_id);
30 | echo json_encode($res);
31 |
--------------------------------------------------------------------------------
/config.php.sample:
--------------------------------------------------------------------------------
1 | array("prettyname" => "yourswitchname", "enabled" => true, "showoninterface" => true, "ip" => "yourswitchname.yourcompany.com", "snmpcommunity" => "public", "graphtypes" => array('bits','ucastpkts','errors')),
12 | # more hosts go here
13 | );
14 |
15 | # Verbosity. Choose your level of logging here. This affects everything that logs, which at the minute is
16 | # just the poller and it's child processes.
17 | #
18 | # 0 for almost nothing, 1 for some information, 2 for lots of information.
19 | $verbose = 1;
20 |
21 | # The path to your desired rrdtool binary.
22 | $path_rrdtool = "/usr/bin/rrdtool";
23 |
24 | # The path where you want to store your RRD files. The default is the "rrds" directory beneath this file.
25 | $path_rrd = dirname(__FILE__) . "/rrds/";
26 |
27 |
28 | # Feel free to adjust these RRA definitions. Default stores 1 month at 1 minute resolution, and 1 year at 60 minutes
29 | $RRA_average = "RRA:AVERAGE:0.5:1:44640 RRA:AVERAGE:0.5:60:8760 ";
30 | $RRA_max = "RRA:MAX:0.5:1:44640 RRA:MAX:0.5:60:8760";
31 |
32 | # Also send data to graphite.
33 | # $carbon_host = "graphite.example.org";
34 | # $carbon_port = 2009;
35 | # $graphite_prefix = "network";
36 | # $graphite_metrics = array("inerrors","outdiscards");
37 | # $graphite_datacenter = "dc1";
38 |
39 | # Database connection parameters
40 | $mysql_host = "localhost";
41 | $mysql_user = "fitbuser";
42 | $mysql_pass = "f1tbP4ss";
43 | $mysql_db = "fitb";
44 |
45 | # Old age:
46 | # Mark graphs older than this many seconds as stale
47 | $staleage = 1800;
48 |
49 | # DELETE graphs that have been stale for this many seconds
50 | # WARNING! You will lose your data if you don't set this correctly!
51 | # You have been warned!
52 | # Set to 0 to disable deletion of ports that have since been downed.
53 | $purgeage = 2592000;
54 |
55 | # Time periods:
56 | # An array of time periods that you wish to be selectable from the dropdown at the top of every page.
57 | # E.g. 300 seconds = 5 minutes
58 | $configtimeperiods = array (-300 => '5 minutes', -3600 => '1 hour', -7200 => '2 hours', -14400 => '4 hours', -43200 => '12 hours', -86400 => '1 day', -172800 => '2 days', -604800 => '7 days', -1209600 => '14 days', -2678400 => '1 month');
59 |
60 | $default_duration = "-86400";
61 |
62 | # PHP Error reporting
63 | # Incase you want to debug my crappy code, change this
64 | error_reporting(E_ERROR | E_WARNING | E_PARSE);
65 |
66 | # Uncomment if you are using PHP version 5.3.0 or newer. PHP 5.3.0+ require timezone to be set or it
67 | # will produce a line of warning output for every port the poller calls date functions. Adjust to match
68 | # your system time configuration, such as 'America/Los_Angeles', etc)
69 | # date_default_timezone_set('UTC');
70 |
71 | # This file ends WITHOUT the normal php closing tag. Do not change.
72 |
--------------------------------------------------------------------------------
/fitb.css:
--------------------------------------------------------------------------------
1 | body,
2 | html {
3 | font-family: "Lucida Grande";
4 | margin: 0px;
5 | }
6 |
7 | .graphcaption {
8 | font-size: 10px;
9 | }
10 |
11 | .headerinfo {
12 | font-size: 10px;
13 | margin-bottom: 5px;
14 | text-align: right;
15 | }
16 |
17 | #wrap {
18 | margin:0 auto;
19 | margin-top: -15px;
20 | }
21 |
22 | #side {
23 | float: left;
24 | margin-left: 5px;
25 | margin-top: 0px;
26 | padding-top: 0px;
27 | width: 200px;
28 | font-size: 0.9em;
29 | }
30 |
31 | #side ul {
32 | padding-left: 1em;
33 | margin-left: 1em;
34 | }
35 |
36 | #main {
37 | margin-left: 210px;
38 | }
39 |
40 | h1 {
41 | margin: 2px;
42 | }
43 |
44 | h2 {
45 | margin-bottom: 10px;
46 | }
47 |
48 | #header {
49 | height: 50px;
50 | background: #D36B00
51 | }
52 |
53 | #headside {
54 | float: left;
55 | color: white;
56 | padding: 4px;
57 | padding-left: 10px;
58 | }
59 |
60 | #headside a {
61 | color: white;
62 | }
63 |
64 | #headright {
65 | float: right;
66 | margin-top: 5px;
67 | margin-right: 5px;
68 | color: white;
69 | }
70 |
71 | form {
72 | display: inline;
73 | }
74 |
75 | img {
76 | border: none
77 | }
78 |
79 | .red {
80 | color: red;
81 | }
82 |
83 | .green {
84 | color: green;
85 | }
86 |
87 | .small {
88 | font-size: 0.8em;
89 | margin: 4px;
90 | }
91 |
92 | p.help-text {
93 | color: #999;
94 | font-size: .8em;
95 | }
96 |
97 | #host-graphs, #aggregate-graphs {
98 | list-style-type: none;
99 | }
100 | #host-graphs li, #aggregate-graphs li {
101 | display: inline;
102 | margin: 2px;
103 | display: inline-block;
104 | vertical-align: top;
105 | }
106 | .sortable-placeholder {
107 | background-color: #FBF9EE;
108 |
109 | }
110 |
111 | #aggregate-builder-status {
112 | position: fixed;
113 | bottom: 2;
114 | right: 20;
115 | width: 350px;
116 | padding: 5px;
117 | background-color: rgba(231, 144, 64, 0.90);
118 | border: 1px solid #FF7000;
119 | display: none;
120 | font-size: .9em;
121 | color: #000000;
122 | }
123 | #aggregate-builder-status a {
124 | color: #033999;
125 | text-decoration: none;
126 | }
127 | #aggregate-builder-status a:hover {
128 | text-decoration: underline;
129 | }
130 |
131 | #aggregate-builder-status .agg-builder-error {
132 | color: #AA0000;
133 | }
134 |
135 | #aggregate-builder-status.updated {
136 | border: 3px solid red;
137 | }
138 |
139 | #aggregate-graph-overlay {
140 | position: fixed;
141 | top: 70px;
142 | height: 460px;
143 | width: 650px;
144 | left: 50%;
145 | margin-left: -300px;
146 | padding: 20px;
147 | background-color: #DEDEDE;
148 | border: 1px solid #999;
149 | display: none;
150 | overflow-y: auto;
151 | }
152 |
153 | #aggregate-graph-overlay img {
154 | margin: auto;
155 | display: block;
156 | }
157 |
158 | #aggregate-graph-overlay #aggregate-img {
159 | min-height: 200px;
160 | }
161 |
162 | #aggregate-graph-overlay .close-icon {
163 | position: absolute;
164 | top: 6px;
165 | right: 6px;
166 | padding: 0 5px 2px;
167 | cursor: pointer;
168 | background-color: #eee;
169 | color: #999;
170 | font-size: 0.8em;
171 | }
172 |
173 | #aggregate-graph-overlay .close-icon:hover {
174 | background-color: #ccc;
175 | color: #333;
176 | }
177 |
178 |
179 | #edit-aggregate-tools {
180 | font-size: .8em;
181 | }
182 |
183 | #edit-aggregate-tools #add-aggregate-link {
184 | cursor: pointer;
185 | font-size: .9em;
186 | display: block;
187 | margin-top: 5px;
188 | color: #D87900;
189 | }
190 | #edit-aggregate-tools #add-aggregate-link:before {
191 | content: '+ ';
192 | }
193 |
194 | #edit-aggregate-tools #add-aggregate-link:hover {
195 | text-decoration: underline;
196 | }
197 |
198 | #edit-aggregate-tools .edit-graph-form {
199 | margin-top: 5px;
200 | }
201 |
202 | #edit-aggregate-tools .edit-graph-form input, #edit-aggregate-tools .edit-graph-form select {
203 | margin-right: 3px;
204 | }
205 | #edit-aggregate-tools .edit-graph-form input.edit-graph-form-host {
206 | width: 80px;
207 | }
208 | #edit-aggregate-tools .edit-graph-form input.edit-graph-form-rrdname {
209 | width: 180px;
210 | }
211 |
212 | #edit-aggregate-tools .edit-graph-form input.edit-graph-form-custom-label {
213 | width: 140px;
214 | }
215 |
216 |
217 | #edit-aggregate-tools .edit-graph-form input.edit-graph-form-color {
218 | width: 50px;
219 | vertical-align: bottom;
220 | padding: 0;
221 | }
222 |
223 | #edit-aggregate-tools .edit-graph-form .remove-link {
224 | margin-right: 3px;
225 | cursor: pointer;
226 | }
227 |
228 | #edit-aggregate-tools .edit-graph-form .remove-link:hover {
229 | color: #ff0000;
230 | }
231 |
232 | #edit-aggregate-tools input#aggregate-title{
233 | width: 200px;
234 | }
235 |
236 | #edit-aggregate-tools input.meta-field {
237 | margin-right: 5px;
238 | }
239 |
240 |
241 | /* some styles from jqueryui for autocomplete menus */
242 | .ui-widget-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }
243 | .ui-autocomplete {
244 | position: absolute;
245 | top: 0;
246 | left: 0;
247 | cursor: default;
248 | font-size: .9em;
249 | }
250 |
251 | .ui-menu { list-style:none; padding: 2px; margin: 0; display:block; outline: none; }
252 | .ui-menu .ui-menu { margin-top: -3px; position: absolute; }
253 | .ui-menu .ui-menu-item { margin: 0; padding: 0; zoom: 1; width: 100%; }
254 | .ui-menu .ui-menu-divider { margin: 5px -2px 5px -2px; height: 0; font-size: 0; line-height: 0; border-width: 1px 0 0 0; }
255 | .ui-menu .ui-menu-item a { text-decoration: none; display: block; padding: 2px .4em; line-height: 1.5; zoom: 1; font-weight: normal; }
256 | .ui-menu .ui-menu-item a.ui-state-focus,
257 | .ui-menu .ui-menu-item a.ui-state-active { font-weight: normal; margin: -1px; }
258 |
259 | .ui-menu .ui-state-disabled { font-weight: normal; margin: .4em 0 .2em; line-height: 1.5; }
260 | .ui-menu .ui-state-disabled a { cursor: default; }
261 |
262 | /* Component containers
263 | ----------------------------------*/
264 | .ui-widget-content { border: 1px solid #dddddd; background: #eeeeee 50% top repeat-x; color: #333333; }
265 | .ui-widget-content a { color: #333333; }
266 | .ui-widget-header { border: 1px solid #e78f08; background: #f6a828 50% 50% repeat-x; color: #ffffff; font-weight: bold; }
267 | .ui-widget-header a { color: #ffffff; }
268 |
269 | /* Interaction states
270 | ----------------------------------*/
271 | .ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { border: 1px solid #cccccc; background: #f6f6f6 50% 50% repeat-x; font-weight: bold; color: #1c94c4; }
272 | .ui-state-default a, .ui-state-default a:link, .ui-state-default a:visited { color: #1c94c4; text-decoration: none; }
273 | .ui-state-hover, .ui-widget-content .ui-state-hover, .ui-widget-header .ui-state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus, .ui-widget-header .ui-state-focus { border: 1px solid #fbcb09; background: #fdf5ce 50% 50% repeat-x; font-weight: bold; color: #c77405; }
274 | .ui-state-hover a, .ui-state-hover a:hover, .ui-state-hover a:link, .ui-state-hover a:visited { color: #c77405; text-decoration: none; }
275 | .ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active { border: 1px solid #fbd850; background: #ffffff 50% 50% repeat-x; font-weight: bold; color: #eb8f00; }
276 | .ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited { color: #eb8f00; text-decoration: none; }
277 |
278 | /* Interaction Cues
279 | ----------------------------------*/
280 | .ui-state-highlight, .ui-widget-content .ui-state-highlight, .ui-widget-header .ui-state-highlight {border: 1px solid #fed22f; background: #ffe45c 50% top repeat-x; color: #363636; }
281 | .ui-state-highlight a, .ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a { color: #363636; }
282 | .ui-state-error, .ui-widget-content .ui-state-error, .ui-widget-header .ui-state-error {border: 1px solid #cd0a0a; background: #b81900 50% 50% repeat; color: #ffffff; }
283 | .ui-state-error a, .ui-widget-content .ui-state-error a, .ui-widget-header .ui-state-error a { color: #ffffff; }
284 | .ui-state-error-text, .ui-widget-content .ui-state-error-text, .ui-widget-header .ui-state-error-text { color: #ffffff; }
285 | .ui-priority-primary, .ui-widget-content .ui-priority-primary, .ui-widget-header .ui-priority-primary { font-weight: bold; }
286 | .ui-priority-secondary, .ui-widget-content .ui-priority-secondary, .ui-widget-header .ui-priority-secondary { opacity: .7; filter:Alpha(Opacity=70); font-weight: normal; }
287 | .ui-state-disabled, .ui-widget-content .ui-state-disabled, .ui-widget-header .ui-state-disabled { opacity: .35; filter:Alpha(Opacity=35); background-image: none; }
288 | .ui-state-disabled .ui-icon { filter:Alpha(Opacity=35); } /* For IE8 - See #6059 *
289 |
290 | /* Corner radius */
291 | .ui-corner-all, .ui-corner-top, .ui-corner-left, .ui-corner-tl { -moz-border-radius-topleft: 4px; -webkit-border-top-left-radius: 4px; -khtml-border-top-left-radius: 4px; border-top-left-radius: 4px; }
292 | .ui-corner-all, .ui-corner-top, .ui-corner-right, .ui-corner-tr { -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; -khtml-border-top-right-radius: 4px; border-top-right-radius: 4px; }
293 | .ui-corner-all, .ui-corner-bottom, .ui-corner-left, .ui-corner-bl { -moz-border-radius-bottomleft: 4px; -webkit-border-bottom-left-radius: 4px; -khtml-border-bottom-left-radius: 4px; border-bottom-left-radius: 4px; }
294 | .ui-corner-all, .ui-corner-bottom, .ui-corner-right, .ui-corner-br { -moz-border-radius-bottomright: 4px; -webkit-border-bottom-right-radius: 4px; -khtml-border-bottom-right-radius: 4px; border-bottom-right-radius: 4px; }
--------------------------------------------------------------------------------
/fitb.js:
--------------------------------------------------------------------------------
1 | var GraphManager = function() {};
2 |
3 | GraphManager.prototype = {
4 | graphs: null,
5 |
6 | init: function(graph_img_selector) {
7 | this.status_el = $('#aggregate-builder-status');
8 | this.initAggregateBuilder(graph_img_selector);
9 |
10 | this.status_el.on('click', 'a.show-agg-builder', (function(event) {
11 | event.preventDefault();
12 | this.aggregateBuilder.show();
13 | }).bind(this));
14 |
15 | this.status_el.on('click', 'a.reset-agg-builder', (function(event) {
16 | event.preventDefault();
17 | this.aggregateBuilder.reset();
18 | this.status_el.hide();
19 | }).bind(this));
20 | },
21 |
22 | getAggregateGraphData: function(aggParts, meta) {
23 | meta = meta || {};
24 | var data = {};
25 | meta.height = meta.height || 200;
26 | meta.width = meta.width || 500;
27 | $.extend(data, meta);
28 | data.host = aggParts.map(function(g) { return g.host; } ).join('|');
29 | data.rrdname = aggParts.map(function(g) { return g.rrdname; } ).join('|');
30 | data.subtype = aggParts.map(function(g) { return g.subtype; } ).join('|');
31 | data.color = aggParts.map(function(g) { return g.color; } ).join('|');
32 | data.graphing_method = aggParts.map(function(g) { return g.graphing_method; } ).join('|');
33 | data.custom_label = aggParts.map(function(g) { return g.custom_label; } ).join('|');
34 | data.count = aggParts.length;
35 | return data;
36 | },
37 |
38 | getDeserializedQueryString: function() {
39 | var data = {};
40 | var querystring = window.location.search;
41 | if (querystring && querystring != '') {
42 | var kvs = querystring.substring(1).split('&');
43 | var duration = ''
44 | kvs.map(function(e) {
45 | var kv = e.split('=');
46 | data[kv[0]] = kv.length ? kv[1] : '';
47 | });
48 | }
49 | return data;
50 | },
51 |
52 | getAggregateGraphUrl: function(aggParts, meta) {
53 | var data = this.getAggregateGraphData(aggParts, meta);
54 |
55 | var qs = this.getDeserializedQueryString();
56 | if (qs.duration) {
57 | data.duration = qs.duration;
58 | }
59 | return 'graph.php?' + Object.keys(data).map(function(k) { return [k, encodeURIComponent(data[k])].join('='); } ).join('&');
60 | },
61 |
62 | saveAggregate: function(aggParts, meta) {
63 | if (!aggParts) return false;
64 | var agg = {
65 | aggParts: aggParts,
66 | meta: meta
67 | };
68 | $.post('ajax/saveAggregate.php', this.getAggregateGraphData(aggParts, meta))
69 | .done(this.saveSuccess.bind(this))
70 | .fail(this.saveError.bind(this));
71 | this.aggregates.push(agg);
72 | },
73 |
74 | saveSuccess: function(data) {
75 | if (data.success) {
76 | this.aggregateBuilder.reset();
77 | this.aggregateBuilder.hide();
78 | var agg_id = data.aggregate_id;
79 | this.showStatusMessage('aggregate saved!
view aggregate |
view all ', 8000);
80 | } else {
81 | this.saveError.apply(this, arguments);
82 | }
83 | },
84 |
85 | saveError: function() {
86 | console.error(arguments);
87 | },
88 |
89 | initAggregateBuilder: function(graph_img_selector) {
90 | var aggParts = null;
91 | if ('localStorage' in window && window['localStorage'] !== null) {
92 | aggParts = JSON.parse(localStorage.getItem("aggregateGraphParts"));
93 | }
94 | this.aggregateBuilder = new GraphManager.AggregateBuilder(this, aggParts, graph_img_selector);
95 | this.aggregateBuilder.init();
96 | },
97 |
98 | showStatusMessage: function(msg, duration) {
99 | this.status_el.find('.agg-builder-message').html(msg).show();
100 | this.status_el.find('.agg-builder-error').hide();
101 | this.status_el.removeClass('error').show();
102 | if (this.status_msg_timeout) {
103 | clearTimeout(this.status_msg_timeout);
104 | this.status_msg_timeout = null;
105 | }
106 | if (duration) {
107 | this.status_msg_timeout = setTimeout((function() {
108 | this.status_el.fadeOut();
109 | }).bind(this), duration);
110 | }
111 | },
112 |
113 | showStatusError: function(msg, duration) {
114 | this.status_el.find('.agg-builder-message').hide();
115 | this.status_el.find('.agg-builder-error').html(msg).show();
116 | this.status_el.addClass('error').show();
117 | if (this.status_msg_timeout) {
118 | clearTimeout(this.status_msg_timeout);
119 | this.status_msg_timeout = null;
120 | }
121 | if (duration) {
122 | this.status_msg_timeout = setTimeout((function() {
123 | this.status_el.fadeOut((function() {
124 | this.onAggregateBuilderUpdate(this.aggregateBuilder.aggParts.length);
125 | }).bind(this));
126 | }).bind(this), duration);
127 | }
128 | },
129 |
130 | onAggregateBuilderUpdate: function(numAggParts) {
131 | if (numAggParts > 0) {
132 | this.showStatusMessage(
133 | '
' +
134 | 'show aggregate builder (' + numAggParts + ' graph' + (numAggParts == 1 ? '' : 's') + ')' +
135 | ' | ' +
136 | '
reset '
137 | );
138 | }
139 | this.status_el.toggle(numAggParts > 0);
140 | },
141 |
142 | onAggregateBuilderError: function(errorMsg) {
143 | this.showStatusError(errorMsg, 8000);
144 | }
145 |
146 | };
147 |
148 | GraphManager.AggregateBuilder = function(graphManager, aggParts, graph_img_selector) {
149 | this.graphManager = graphManager;
150 | this.aggParts = aggParts || [];
151 | this.inited = false;
152 | this.graph_img_selector = graph_img_selector;
153 | };
154 |
155 | GraphManager.AggregateBuilder.prototype = {
156 | init: function() {
157 | $(this.graph_img_selector).click((function(event) {
158 | if (event.shiftKey) {
159 | event.preventDefault();
160 | this.addToAggregate($(event.target));
161 | }
162 | }).bind(this))
163 | .attr('title', 'shift-click to add to aggregate');
164 |
165 | this.overlay = $('#aggregate-graph-overlay');
166 | this.overlay.on('click', '.close-icon', this.hide.bind(this));
167 | this.overlay.on('click', '#add-aggregate-link', this.addGraphForm.bind(this));
168 |
169 | this.overlay.on('click', '#do-update-aggregate', this.update.bind(this));
170 | this.overlay.on('click', '#do-save-aggregate', this.save.bind(this));
171 | this.overlay.on('click', '#do-reset-aggregate', this.reset.bind(this));
172 |
173 | this.overlay.on('change', '#aggregate-type-selector input', this.changeType.bind(this));
174 |
175 | this.forms = [];
176 | this.type = this.aggParts.length > 0 ? this.aggParts[0].type : null;
177 |
178 | $.each(this.aggParts, (function (i, aggPart) {
179 | this.addGraphForm(aggPart);
180 | }).bind(this));
181 | this.inited = true;
182 | this.updateUI();
183 | this.graphManager.onAggregateBuilderUpdate(this.aggParts.length);
184 | },
185 |
186 | addToAggregate: function(img) {
187 | var data = this.extractGraphData(img.attr('src'));
188 | if (data.type != null && (!this.type || data.type == this.type)) {
189 | this.type = data.type;
190 | this.aggParts.push(data);
191 | this.saveAggParts();
192 | this.addGraphForm(data);
193 | this.updateUI();
194 | this.graphManager.onAggregateBuilderUpdate(this.aggParts.length);
195 | } else {
196 | this.graphManager.onAggregateBuilderError('please select a graph of the same type as the others in this aggregate (' + this.type + ')');
197 | }
198 | },
199 |
200 | extractGraphData: function(url) {
201 | var kvs = url.replace('graph.php?', '').split('&');
202 | var data = {};
203 | $.each(kvs, function(i, kvp) {
204 | var kv = kvp.split('=');
205 | data[kv[0]] = kv.length > 0 ? kv[1] : '';
206 | });
207 | return data;
208 | },
209 |
210 | addGraphForm: function(data) {
211 | var form = new GraphManager.GraphEditForm(this.type, data, this.forms.length);
212 | this.overlay.find('#graph-forms').append(form.getForm());
213 | this.forms.push(form);
214 | this.updateUI(false);
215 | return form;
216 | },
217 |
218 | update: function() {
219 | this.aggParts = this.forms.filter(function(el) { return el.enabled; } ).map(function(f) {
220 | return f.getData();
221 | });
222 | this.saveAggParts();
223 | this.updateUI();
224 | this.graphManager.onAggregateBuilderUpdate(this.aggParts.length);
225 | },
226 |
227 | show: function() {
228 | this.overlay.show();
229 | },
230 |
231 | hide: function() {
232 | this.overlay.hide();
233 | },
234 |
235 | updateUI: function(redraw) {
236 | if (!this.inited) return;
237 | var hasAggParts = this.aggParts.length > 0;
238 | var hasType = !!this.type;
239 | var hasAggForms = this.forms.filter(function(f) { return f.enabled; }).length > 0;
240 | if (redraw !== false) {
241 | if (hasAggParts) {
242 | var imgUrl = this.graphManager.getAggregateGraphUrl(this.aggParts, this.getMeta());
243 | this.overlay.find('#aggregate-img').html($('
').error(this.imgError.bind(this)).attr('src', imgUrl)).show();
244 | } else {
245 | this.overlay.find('#aggregate-img').empty().hide();
246 | }
247 | }
248 |
249 | var aggTypeSelector = this.overlay.find('#aggregate-type-selector');
250 |
251 | aggTypeSelector.find('input').attr('disabled', hasAggParts || hasAggForms ? 'disabled' : null).attr('checked', null);
252 | if (hasType) {
253 | aggTypeSelector.find('input[value="' + this.type + '"]').attr('checked', 'checked');
254 | }
255 |
256 | this.overlay.find('#add-aggregate-link').toggle(hasType);
257 | this.overlay.find('.meta-field').toggle(hasAggForms);
258 |
259 | $('#aggregate-builder-status').toggle(hasAggParts);
260 | },
261 |
262 | getMeta: function() {
263 | var meta = {};
264 | meta.friendlytitle = $.trim(this.overlay.find('#edit-aggregate-tools input#aggregate-title').val());
265 | meta.stack = this.overlay.find('#edit-aggregate-tools input#aggregate-stack').is(':checked');
266 | meta.type = this.type;
267 | return meta;
268 | },
269 |
270 | imgError: function() {
271 | this.overlay.find('#aggregate-img').html($('
There was a problem loading this graph. '));
272 | },
273 |
274 | saveAggParts: function() {
275 | if ('localStorage' in window && window['localStorage'] !== null) {
276 | localStorage.setItem("aggregateGraphParts", JSON.stringify(this.aggParts));
277 | }
278 | },
279 |
280 | save: function() {
281 | this.graphManager.saveAggregate(this.aggParts, this.getMeta());
282 | },
283 |
284 | changeType: function() {
285 | var selectedType = this.overlay.find('#aggregate-type-selector input:checked').val();
286 | this.type = selectedType;
287 | this.updateUI();
288 | },
289 |
290 | reset: function() {
291 | $.each(this.forms, function(i, f) {
292 | f.destroy();
293 | });
294 | this.forms = [];
295 | this.aggParts = [];
296 | this.type = null;
297 | this.overlay.find('#edit-aggregate-tools input#aggregate-title').val('');
298 | this.overlay.find('#edit-aggregate-tools input#aggregate-stack').attr('checked', 'checked');
299 | this.update();
300 | }
301 | };
302 |
303 | GraphManager.GraphEditForm = function(type, data, idx) {
304 | this.type = type;
305 | this.data = data || {};
306 | this.idx = idx;
307 | this.portsByHost = {};
308 | this.form = this.createForm();
309 | this.form.find('.edit-graph-form-host').on("autocompletechange change", (function() {
310 | this.form.find('.edit-graph-form-rrdname').val('');
311 | }).bind(this));
312 | this.form.find('.remove-link').on("click", (function() {
313 | this.destroy();
314 | }).bind(this));
315 | this.enabled = true;
316 | };
317 |
318 | GraphManager.GraphEditForm.prototype = {
319 | getHosts: function() {
320 | if (GraphManager.HOSTS) {
321 | return Object.keys(GraphManager.HOSTS);
322 | }
323 | return [];
324 | },
325 |
326 | getRRDNames: function(request, response) {
327 | var host = this.getData().host;
328 | if (host && host != '' && this.type) {
329 | if (this.portsByHost[host]) {
330 | response(this.portsByHost[host].filter(function(el) { return el.match(new RegExp(request.term, "i")); }));
331 | } else {
332 | $.getJSON('ajax/getRRDNamesForHost.php', {host: host, type: this.type})
333 | .done((function(data) {
334 | this.portsByHost[host] = data;
335 | response(this.portsByHost[host].filter(function(el) { return el.match(new RegExp(request.term, "i")); }));
336 | }.bind(this)))
337 | .fail(function() {
338 | console.error(arguments);
339 | response([]);
340 | });
341 | }
342 | } else {
343 | response([]);
344 | }
345 | },
346 |
347 | getSubtypes: function() {
348 | if (this.type != null) {
349 | return {
350 | bits: ['bits_in', 'bits_out'],
351 | ucastpkts: ['ucastpkts_in', 'ucastpkts_out'],
352 | errors: ['discards_in', 'errors_in', 'discards_out', 'errors_out'],
353 | mcastpkts: ['mcastpkts_in', 'mcastpkts_out'],
354 | bcastpkts: ['bcastpkts_in', 'bcastpkts_out']
355 | }[this.type];
356 | }
357 | return [];
358 | },
359 |
360 | getData: function() {
361 | var data = {};
362 | data.host = this.form.find('.edit-graph-form-host').val();
363 | data.rrdname = this.form.find('.edit-graph-form-rrdname').val();
364 | data.subtype = this.form.find('.edit-graph-form-subtype').val();
365 | data.color = this.form.find('.edit-graph-form-color').val();
366 | data.graphing_method = this.form.find('.edit-graph-form-graphing-method').val();
367 | data.custom_label = this.form.find('.edit-graph-form-custom-label').val();
368 | data.type = this.type;
369 | return data;
370 | },
371 |
372 | getDefaultColor: function() {
373 | // these should match the colors in functions.php
374 | var colors = {
375 | 'bits': ['#0A2868', '#FFCA00', '#EC4890', '#517CD7', '#00C169', '#D1F94C'],
376 | 'ucastpkts': ['#2008E6', '#D401E2', '#00DFD6', '#08004E'],
377 | 'errors': ['#30B6C9', '#FFFE39', '#AD34CF', '#09616D'],
378 | 'mcastpkts': ['#53B0B8', '#7E65C7', '#C2F2C6', '#FFB472'],
379 | 'bcastpkts': ['#FFEF9F', '#C17AC3', '#7FA1C3', '#AA9737']
380 | }[this.type];
381 | return colors[this.idx % colors.length];
382 | },
383 |
384 | createForm: function() {
385 | var form = $('
');
386 | form.append($('
x '));
387 | form.append($('
').autocomplete({ source: this.getHosts() }).val(this.data.host || '').attr('placeholder', 'host'));
388 | form.append($('
').autocomplete({ source: (function (request, response) { this.getRRDNames(request, response) }).bind(this) }).val(this.data.rrdname || '').attr('placeholder', 'rrdname'));
389 | var subtype_select = $('
');
390 | subtype_select.append($('
').val('').attr('selected', !this.data.subtype));
391 | $.each(this.getSubtypes(), (function(i, subtype) {
392 | subtype_select.append($(' ').val(subtype).attr('selected', (subtype == this.data.subtype || (i == 0 && !this.data.subtype)) ? 'selected' : null).text(subtype));
393 | }).bind(this));
394 | form.append(subtype_select);
395 |
396 | var graphing_method_select = $(' ');
397 | graphing_method_select.append($(' ').val('AREA').attr('selected', !this.data.graphing_method || this.data.graphing_method == 'AREA').text('area'));
398 | graphing_method_select.append($(' ').val('LINE1').attr('selected', this.data.graphing_method == 'LINE1').text('line'));
399 | form.append(graphing_method_select);
400 |
401 | form.append($(' ').val(this.data.color || this.getDefaultColor()));
402 | form.append($(' ').val(this.data.custom_label || '').attr('placeholder', 'custom label'));
403 |
404 | form.on('change', '.edit-graph-form-host', function(){ form.find('.edit-graph-form-rrdname').empty(); });
405 |
406 | return form;
407 | },
408 |
409 | getForm: function() {
410 | return this.form;
411 | },
412 |
413 | destroy: function() {
414 | this.form.remove();
415 | this.enabled = false;
416 | }
417 | };
418 |
--------------------------------------------------------------------------------
/fitb.sql:
--------------------------------------------------------------------------------
1 | CREATE TABLE `ports` (
2 | `host` varchar(255) NOT NULL,
3 | `name` varchar(255) NOT NULL,
4 | `safename` varchar(255) NOT NULL,
5 | `filename` varchar(255) NOT NULL,
6 | `alias` text NOT NULL,
7 | `graphtype` varchar(255) NOT NULL,
8 | `lastpoll` int(10) unsigned NOT NULL,
9 | PRIMARY KEY (`filename`),
10 | KEY `host_name` (`host`),
11 | KEY `port_name` (`name`)
12 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
13 |
14 | CREATE TABLE `aggregates` (
15 | `aggregate_id` int(11) unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY,
16 | `friendlytitle` varchar(255) DEFAULT NULL,
17 | `type` varchar(255) NOT NULL,
18 | `stack` tinyint(1) NOT NULL DEFAULT '1'
19 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
20 |
21 | CREATE TABLE `aggregate_parts` (
22 | `aggregate_part_id` int(11) unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY,
23 | `aggregate_id` int(11) unsigned NOT NULL,
24 | `host` varchar(255) NOT NULL,
25 | `rrdname` varchar(255) NOT NULL,
26 | `subtype` varchar(255) DEFAULT NULL,
27 | `options` text DEFAULT NULL
28 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
--------------------------------------------------------------------------------
/functions.php:
--------------------------------------------------------------------------------
1 | $rrdname,
122 | 'rrdfolder' => $rrdfolder,
123 | 'subtype' => $subtype
124 | );
125 | }
126 | return getStackedGraphsCmd($graphs_array, $type, false, $start, $end, $height, $width, $friendlytitle);
127 | }
128 |
129 | function getStackedGraphsCmd($graphs_array, $type, $stack = false, $start = "-86400", $end = "-60", $height = "120", $width = "500", $friendlytitle = "") {
130 | global $path_rrdtool, $path_rrd;
131 |
132 | $type_to_title = array('bits' => 'bits/sec', 'ucastpkts' => 'unicast packets/sec', 'errors' => 'Errors/sec', 'mcastpkts' => 'multicast packets/sec', 'bcastpkts' => 'broadcast packets/sec');
133 | $type_to_label = array('bits' => 'bits per second', 'ucastpkts' => 'packets per sec', 'errors' => 'errors per sec', 'mcastpkts' => 'packets per sec', 'bcastpkts' => 'packets sec');
134 | if ($friendlytitle == "") {
135 | $f = function($graph) {
136 | return $graph['rrdname'];
137 | };
138 | $titlename = implode(' | ', array_map($f, $graphs_array));
139 | } else {
140 | $titlename = $friendlytitle;
141 | }
142 | $title = str_replace('"', '\"', $titlename . ' - ' . $type_to_title[$type]);
143 |
144 | $idx = 0;
145 | $data_cmds = '';
146 | foreach ($graphs_array as $graph) {
147 | if (isset($graph['opts'])) {
148 | $opts = $graph['opts'];
149 | } else {
150 | $opts = array();
151 | }
152 |
153 | # Only generate graph commands for RRD files that exist, in case the ports go down later on (or don't yet exist)
154 | if (file_exists("{$path_rrd}{$graph['rrdfolder']}/{$graph['rrdname']}_{$type}.rrd")) {
155 | $data_cmds .= " " . getCommandForRRD(
156 | $graph['rrdname'],
157 | $graph['rrdfolder'],
158 | $type,
159 | $graph['subtype'],
160 | $opts,
161 | $stack,
162 | $idx++
163 | );
164 | }
165 | }
166 |
167 | $rrd_cmd = "{$path_rrdtool} graph - --imgformat=PNG --font TITLE:8: --start={$start} --end={$end} --title=\"{$title}\" ";
168 | $rrd_cmd .= "--rigid --vertical-label='{$type_to_label[$type]}' --slope-mode --height={$height} --width={$width} --lower-limit=0 ";
169 | $rrd_cmd .= $data_cmds;
170 | return $rrd_cmd;
171 | }
172 |
173 | function getDataColumnAndLabel($subtype) {
174 | switch($subtype) {
175 | case "bits_in": return array('data_column' => 'traffic_in', 'data_label' => 'Inbound ');
176 | case "bits_out": return array('data_column' => 'traffic_out', 'data_label' => 'Outbound');
177 | case "ucastpkts_in": return array('data_column' => 'unicast_in', 'data_label' => 'Unicast In ');
178 | case "ucastpkts_out": return array('data_column' => 'unicast_out', 'data_label' => 'Unicast Out');
179 | case "errors_in": return array('data_column' => 'errors_in', 'data_label' => 'Errors In ');
180 | case "errors_out": return array('data_column' => 'errors_out', 'data_label' => 'Errors Out ');
181 | case "discards_in": return array('data_column' => 'discards_in', 'data_label' => 'Discards In ');
182 | case "discards_out": return array('data_column' => 'discards_out', 'data_label' => 'Discards Out');
183 | case "mcastpkts_in": return array('data_column' => 'multicast_in', 'data_label' => 'Multicast In ');
184 | case "mcastpkts_out": return array('data_column' => 'multicast_out', 'data_label' => 'Multicast Out');
185 | case "bcastpkts_in": return array('data_column' => 'broadcast_in', 'data_label' => 'Broadcast In ');
186 | case "bcastpkts_out": return array('data_column' => 'broadcast_out', 'data_label' => 'Broadcast Out');
187 | }
188 | }
189 |
190 | function getDefaultColor($type, $idx, $stack) {
191 | if ($stack) {
192 | // these should match the colors in fitb.js
193 | $colors = array(
194 | 'bits' => array('#0A2868', '#FFCA00', '#EC4890', '#517CD7', '#00C169', '#D1F94C'),
195 | 'ucastpkts' => array('#2008E6', '#D401E2', '#00DFD6', '#08004E'),
196 | 'errors' => array('#30B6C9', '#FFFE39', '#AD34CF', '#09616D'),
197 | 'mcastpkts' => array('#53B0B8', '#7E65C7', '#C2F2C6', '#FFB472'),
198 | 'bcastpkts' => array('#FFEF9F', '#C17AC3', '#7FA1C3', '#AA9737')
199 | );
200 | } else {
201 | $colors = array(
202 | 'bits' => array('#00CF00FF', '#002A97FF', '#C4FD3DFF', '#00694AFF'),
203 | 'ucastpkts' => array('#FFF200FF', '#00234BFF', '#C4FD3DFF', '#00694AFF'),
204 | 'errors' => array('#FFAB00FF', '#F51D30FF', '#C4FD3DFF', '#00694AFF'),
205 | 'mcastpkts' => array('#FFF200FF', '#00234BFF', '#C4FD3DFF', '#00694AFF'),
206 | 'bcastpkts' => array('#99B898FF', '#00234BFF', '#C4FD3DFF', '#00694AFF')
207 | );
208 | }
209 | return $colors[$type][$idx % count($colors[$type])];
210 | }
211 |
212 | function getCommandForRRD($rrdname, $rrdfolder, $type, $subtype, $opts, $stack, $idx = 0) {
213 | global $path_rrd;
214 |
215 | $buildname = "{$path_rrd}{$rrdfolder}/{$rrdname}_{$type}.rrd";
216 |
217 | if (isset($opts['graphing_method']) && $opts['graphing_method'] != '') {
218 | $gm = $opts['graphing_method'];
219 | } else {
220 | if (!$stack && ($idx > 0 || $type == 'errors')) {
221 | $gm = 'LINE1';
222 | } else {
223 | $gm = 'AREA';
224 | }
225 | }
226 |
227 | if (isset($opts['color']) && $opts['color'] != '') {
228 | $color = $opts['color'];
229 | } else {
230 | $color = getDefaultColor($type, $idx, $stack);
231 | }
232 |
233 | $data_column_label = getDataColumnAndLabel($subtype);
234 | $data_column = $data_column_label['data_column'];
235 |
236 | if (isset($opts['custom_label']) && $opts['custom_label'] != '') {
237 | $data_label = $opts['custom_label'];
238 | } else {
239 | // if the graphs are stacked, then they'd all have the same label. lets just use the rrd name by default.
240 | if ($stack) {
241 | $data_label = $rrdname;
242 | } else {
243 | $data_label = $data_column_label['data_label'];
244 | }
245 | }
246 | $data_label = str_replace('"', '\"', $data_label);
247 |
248 | $def = "a{$idx}"; //avoid collisions by using idx to name data
249 | $rrdcmd = "DEF:{$def}='{$buildname}':{$data_column}:AVERAGE ";
250 | $data_key = $def;
251 | if ($type == "bits") {
252 | $cdef = "${def},8,*"; // bits/sec
253 | $rrdcmd .= "CDEF:cdef{$def}={$cdef} ";
254 | $data_key = "cdef{$def}";
255 | }
256 |
257 | if ($stack) {
258 | $stk = ":STACK";
259 | } else {
260 | $stk = "";
261 | }
262 | $rrdcmd .= "{$gm}:{$data_key}{$color}:\"{$data_label}\"{$stk} ";
263 |
264 | $rrdcmd .= "GPRINT:{$data_key}:LAST:\"Curr\:%8.2lf %s\" ";
265 | $rrdcmd .= "GPRINT:{$data_key}:AVERAGE:\"Ave\:%8.2lf %s\" ";
266 | if ($type == 'bits') {
267 | $rrdcmd .= "GPRINT:{$data_key}:MIN:\"Min\:%8.2lf %s\" ";
268 | }
269 | $rrdcmd .= "GPRINT:{$data_key}:MAX:\"Max\:%8.2lf %s\\n\" ";
270 |
271 | return $rrdcmd;
272 | }
273 |
274 | function getAggregateGraphsArrayFromRequest($request_data) {
275 | $rrdnames = explode('|', $request_data['rrdname']);
276 | $hosts = explode('|', $request_data['host']);
277 | $type = $request_data['type'];
278 |
279 | $graph_count = count($rrdnames);
280 |
281 | if (isset($request_data['subtype'])) {
282 | $subtypes = explode('|', $request_data['subtype']);
283 | if (count($subtypes) < $graph_count) {
284 | $subtypes = array_merge($subtypes, array_fill(count($subtypes) - 1, $graph_count - count($subtypes), getDefaultSubtypeForGraphType($type)));
285 | }
286 | } else {
287 | $subtypes = array_fill(0, $graph_count, getDefaultSubtypeForGraphType($type));
288 | }
289 |
290 | $colors = array();
291 | if (isset($request_data['color'])) {
292 | $colors = explode('|', $request_data['color']);
293 | if (count($colors) < $graph_count) {
294 | $colors = array_merge($colors, array_fill(count($colors) - 1, $graph_count - count($colors), ''));
295 | }
296 | }
297 |
298 | $graphing_methods = array();
299 | if (isset($request_data['graphing_method'])) {
300 | $graphing_methods = explode('|', $request_data['graphing_method']);
301 | if (count($graphing_methods) < $graph_count) {
302 | $graphing_methods = array_merge($graphing_methods, array_fill(count($graphing_methods) - 1, $graph_count - count($graphing_methods), ''));
303 | }
304 | }
305 |
306 | $custom_labels = array();
307 | if (isset($request_data['custom_label'])) {
308 | $custom_labels = explode('|', $request_data['custom_label']);
309 | if (count($custom_labels) < $graph_count) {
310 | $custom_labels = array_merge($custom_labels, array_fill(count($custom_labels) - 1, $graph_count - count($custom_labels), ''));
311 | }
312 | }
313 |
314 | if (count($rrdnames) == $graph_count && count($hosts) == $graph_count) {
315 | $graphs_array = array();
316 | foreach (range(0, $graph_count - 1) as $i) {
317 | $graphs_array[] = array(
318 | 'rrdname' => $rrdnames[$i],
319 | 'rrdfolder' => $hosts[$i],
320 | 'subtype' => ($subtypes[$i] == "" ? getDefaultSubtypeForGraphType($type) : $subtypes[$i]),
321 | 'opts' => array()
322 | );
323 | if (count($colors) > $i) {
324 | $graphs_array[$i]['opts']['color'] = $colors[$i];
325 | }
326 | if (count($graphing_methods) > $i) {
327 | $graphs_array[$i]['opts']['graphing_method'] = $graphing_methods[$i];
328 | }
329 | if (count($custom_labels) > $i) {
330 | $graphs_array[$i]['opts']['custom_label'] = $custom_labels[$i];
331 | }
332 | }
333 | return $graphs_array;
334 | }
335 | return null;
336 | }
337 |
338 | function saveAggregate($graphs_array, $meta) {
339 | if(count($graphs_array) < 1) return false;
340 | $link = connectToDB();
341 | if(!$link) return false;
342 |
343 | $res = mysqli_query($link, 'START TRANSACTION');
344 |
345 | $friendlytitle = $meta['friendlytitle'] ? $meta['friendlytitle'] : null;
346 | $type = $meta['type'];
347 | $stack = $meta['stack'] !== false ? '1' : '0';
348 | $fields = array(
349 | $friendlytitle ? "'" . mysqli_real_escape_string($link, $friendlytitle) . "'" : 'NULL',
350 | "'" . mysqli_real_escape_string($link, $type) . "'",
351 | "'" . mysqli_real_escape_string($link, $stack) . "'"
352 | );
353 | $query = "INSERT INTO aggregates (friendlytitle, type, stack) VALUES (" . implode(',', $fields) . ")";
354 |
355 | $inserted = mysqli_query($link, $query);
356 |
357 | if ($inserted) {
358 | $agg_id = mysqli_insert_id($link);
359 |
360 | $inserts = array();
361 | foreach ($graphs_array as $g) {
362 | $options = 'NULL';
363 | if ($g['opts'] != null) {
364 | $options = mysqli_real_escape_string($link, json_encode($g['opts']));
365 | }
366 |
367 | $fields = array(
368 | $agg_id,
369 | "'" . mysqli_real_escape_string($link, $g['rrdfolder']) . "'",
370 | "'" . mysqli_real_escape_string($link, $g['rrdname']) . "'",
371 | $g['subtype'] ? "'" . mysqli_real_escape_string($link, $g['subtype']) . "'" : 'NULL',
372 | $g['opts'] ? "'" . mysqli_real_escape_string($link, json_encode($g['opts'])) . "'" : 'NULL'
373 | );
374 | $inserts[] = '(' . implode(',', $fields) . ')';
375 | }
376 | $query = "INSERT INTO aggregate_parts (aggregate_id, host, rrdname, subtype, options) VALUES " . implode(',', $inserts);
377 | $inserted = mysqli_query($link, $query);
378 | }
379 |
380 | if ($inserted) {
381 | $inserted = mysqli_query($link, 'COMMIT');
382 | return $agg_id;
383 | } else {
384 | $res = mysqli_query($link, 'ROLLBACK');
385 | }
386 | return null;
387 | }
388 |
389 | function deleteAggregate($aggregate_id) {
390 | if (!$link=connectToDB()) {
391 | return false;
392 | }
393 |
394 | $res = mysqli_query($link, 'START TRANSACTION');
395 |
396 | $query = 'DELETE FROM aggregates WHERE aggregate_id = "'.mysqli_real_escape_string($link, $aggregate_id).'"';
397 | $deleted = mysqli_query($link, $query);
398 |
399 | if ($deleted) {
400 | $query = 'DELETE FROM aggregate_parts WHERE aggregate_id = "'.mysqli_real_escape_string($link, $aggregate_id).'"';
401 | $deleted = mysqli_query($link, $query);
402 | }
403 |
404 | if ($deleted) {
405 | $deleted = mysqli_query($link, 'COMMIT');
406 | } else {
407 | $res = mysqli_query($link, 'ROLLBACK');
408 | }
409 | return $deleted;
410 | }
411 |
412 | function getAggregateData($agg_id) {
413 | $link=connectToDB();
414 |
415 | $result = mysqli_query($link, 'SELECT * FROM aggregates WHERE aggregate_id='.mysqli_real_escape_string($link, $agg_id));
416 | if (mysqli_num_rows($result) == 1) {
417 | $agg = mysqli_fetch_assoc($result);
418 | $meta = array(
419 | 'friendlytitle' => !is_null($agg['friendlytitle']) ? $agg['friendlytitle'] : '',
420 | 'type' => $agg['type'],
421 | 'stack' => $agg['stack'] == '1'
422 | );
423 |
424 | $result = mysqli_query($link, 'SELECT * FROM aggregate_parts WHERE aggregate_id='.mysqli_real_escape_string($link, $agg_id));
425 | $graphs_array = array();
426 | if (mysqli_num_rows($result) > 0) {
427 | while ($part = mysqli_fetch_assoc($result)) {
428 | $graphs_array[] = array(
429 | 'rrdfolder' => $part['host'],
430 | 'rrdname' => $part['rrdname'],
431 | 'subtype' => $part['subtype'],
432 | 'opts' => json_decode($part['options'], true)
433 | );
434 | }
435 | }
436 | return array('graphs_array' => $graphs_array, 'meta' => $meta);
437 | }
438 |
439 | return null;
440 | }
441 |
442 | function purgeOld($loglevel) {
443 | global $purgeage, $path_rrd;
444 |
445 | if ($purgeage == 0) {
446 | logline("PURGER: Purge has been disabled in the config. Quitting now. ", 0, $loglevel);
447 | return false;
448 | }
449 | $unixpurgeage = time() - $purgeage;
450 | logline("PURGER: Beginning purge of old RRD files that have since gone down", 0, $loglevel);
451 | logline("PURGER: Your purge age is set to delete any RRDs/ports older than " . _ago($unixpurgeage), 1, $loglevel);
452 |
453 | $link=connectToDB();
454 | $findold = "SELECT * FROM ports WHERE lastpoll < {$unixpurgeage}";
455 | $results = mysqli_query($link, $findold);
456 |
457 | $numresults = mysqli_num_rows($results);
458 | if($numresults > 0) {
459 | logline("PURGER: Found {$numresults} candidate old RRDs/ports for deletion (older than " . _ago($unixpurgeage) . ")", 0, $loglevel);
460 | while ($row = mysqli_fetch_assoc($results)) {
461 | logline("PURGER: Deleting '{$row['name']}' from host '{$row['host']}' and graphtype '{$row['graphtype']}' from the database", 2, $loglevel);
462 | $filetodelete = "{$path_rrd}{$row['host']}/{$row['host']}-{$row['safename']}_{$row['graphtype']}.rrd";
463 | logline("PURGER: Deleting '{$filetodelete}'. ", 2, $loglevel);
464 | unlink($filetodelete);
465 | logline("PURGER: Deleting row for '{$row['name']}' from the database", 2, $loglevel);
466 | $deleterow = 'DELETE FROM ports WHERE host="' . $row['host']. '" AND safename="'. $row['safename'] .'" AND graphtype="' . $row['graphtype']. '"';
467 | mysqli_query($link, $deleterow);
468 | logline("PURGER: Done deleting '{$row['name']}' ", 1, $loglevel);
469 | }
470 | } else {
471 | logline("PURGER: No RRDs/ports were older than " . _ago($unixpurgeage), 0, $loglevel);
472 | }
473 | return true;
474 |
475 | }
476 |
477 | function getAllGraphTypesInUse() {
478 | global $pollhosts;
479 |
480 | $graphsarray = array();
481 | foreach($pollhosts as $thishost) {
482 | $graphsarray = array_merge($thishost['graphtypes'], $graphsarray);
483 | }
484 | $graphsarray = array_unique($graphsarray);
485 | return $graphsarray;
486 | }
487 |
488 | function logline($message, $messverbose, $reqverbose) {
489 | # Prints a log message if the message verbosity is in the requested range
490 | if ($reqverbose >= $messverbose) {
491 | echo date(DATE_RFC822) . " " . $message . "\n";
492 | }
493 | }
494 |
495 | function connectToDB() {
496 | global $mysql_host, $mysql_user, $mysql_pass, $mysql_db;
497 | static $link = false;
498 | if(!$link) {
499 | $link = mysqli_connect($mysql_host, $mysql_user, $mysql_pass);
500 | mysqli_select_db($link, $mysql_db);
501 | }
502 | return $link;
503 | }
504 |
505 | function getAllEnabledHosts() {
506 | global $pollhosts;
507 | $isEnabled = function($host) {
508 | return $host['enabled'];
509 | };
510 | return array_filter($pollhosts, $isEnabled);
511 | }
512 |
513 | function getPortsForHostAndType($host, $type) {
514 | $ports = array();
515 | if($link=connectToDB()) {
516 | $result = mysqli_query($link, 'SELECT * FROM ports WHERE host like "%' . mysqli_real_escape_string($link, $host). '%" AND graphtype like "%' . mysqli_real_escape_string($link, $type) . '%" ORDER BY lastpoll DESC, safename ASC');
517 | if (mysqli_num_rows($result) > 0) {
518 | while ($row = mysqli_fetch_assoc($result)) {
519 | $ports[] = $host . '-' . $row["safename"];
520 | }
521 | }
522 | return $ports;
523 | }
524 | return array();
525 | }
526 |
527 | function htmlHostsInConfig() {
528 | # Prints HTML output of all the switches in the config
529 | echo "";
530 | echo 'aggregate graphs ';
531 | $currentHost = @$_GET['host'];
532 | foreach (getAllEnabledHosts() as $thishost) {
533 | if($thishost['showoninterface'] == true) {
534 | echo '' . $thishost['prettyname'] . ' ';
535 | if ($currentHost == $thishost['prettyname']) {
536 | echo '';
537 | } else {
538 | echo '';
539 | }
540 | foreach ($thishost['graphtypes'] as $thistype) {
541 | echo '' . $thistype . ' ';
542 | }
543 | echo ' ';
544 | echo ' ';
545 | }
546 | }
547 | echo " ";
548 | }
549 |
550 | function htmlLastPollerTime() {
551 | global $path_rrd;
552 |
553 | if (is_readable($path_rrd . "lastpolltime.txt")) {
554 | $lastpolltime = file_get_contents($path_rrd . "lastpolltime.txt");
555 | if ((time() - $lastpolltime) > 400) {
556 | $color = '';
557 | } else {
558 | $color = '';
559 | }
560 | echo "Last poll time: {$color}" . _ago($lastpolltime) . " ago ";
561 | } else {
562 | echo 'Never polled or permissions issue ';
563 | }
564 | }
565 |
566 | function htmlNumberOfGraphs() {
567 | if($link=connectToDB()) {
568 | $results = mysqli_query($link, "SELECT COUNT(*) from ports;");
569 | $ports = mysqli_fetch_row($results);
570 | echo "{$ports[0]} graphs";
571 | } else {
572 | echo " Connect to database failed, are your MySQL details correct? ";
573 | }
574 | }
575 |
576 | function htmlNumberOfHosts() {
577 | if($link=connectToDB()) {
578 | $results = mysqli_query($link, "SELECT COUNT(DISTINCT(host)) from ports;");
579 | $hosts = mysqli_fetch_row($results);
580 | echo "{$hosts[0]} hosts";
581 | }
582 | }
583 |
584 | function _ago($tm,$rcs = 0) {
585 | $cur_tm = time(); $dif = $cur_tm-$tm;
586 | $pds = array('second','minute','hour','day','week','month','year','decade');
587 | $lngh = array(1,60,3600,86400,604800,2630880,31570560,315705600);
588 | for($v = sizeof($lngh)-1; ($v >= 0)&&(($no = $dif/$lngh[$v])<=1); $v--); if($v < 0) $v = 0; $_tm = $cur_tm-($dif%$lngh[$v]);
589 |
590 | $no = floor($no); if($no <> 1) $pds[$v] .='s'; $x=sprintf("%d %s ",$no,$pds[$v]);
591 | if(($rcs == 1)&&($v >= 1)&&(($cur_tm-$_tm) > 0)) $x .= time_ago($_tm);
592 | return $x;
593 | }
594 |
595 | function snmptable($host, $community, $oid) {
596 | # This handy function was bought to you by scot at indievisible dot org
597 | # Found on the PHP.net documentation page for snmprealwalk.
598 |
599 | # The important thing about this function is that it fills in the blanks.
600 | # Regular SNMP walks leave out items so you can't blindly prod things into arrays any more.
601 |
602 | snmp_set_oid_numeric_print(TRUE);
603 | snmp_set_quick_print(TRUE);
604 | snmp_set_enum_print(TRUE);
605 | snmp_set_valueretrieval(SNMP_VALUE_PLAIN );
606 | snmp_set_oid_output_format(SNMP_OID_OUTPUT_NUMERIC);
607 |
608 | $retval = array();
609 | if(!$raw = snmp2_real_walk($host, $community, $oid)) {
610 | return false;
611 | }
612 | if (count($raw) == 0) return false; // no data
613 |
614 | $prefix_length = 0;
615 | $largest = 0;
616 | foreach ($raw as $key => $value) {
617 | if ($prefix_length == 0) {
618 | // don't just use $oid's length since it may be non-numeric
619 | $prefix_elements = count(explode('.',$oid));
620 | $tmp = '.' . strtok($key, '.');
621 | while ($prefix_elements > 1) {
622 | $tmp .= '.' . strtok('.');
623 | $prefix_elements--;
624 | }
625 | $tmp .= '.';
626 | $prefix_length = strlen($tmp);
627 | }
628 | $key = substr($key, $prefix_length);
629 | $index = explode('.', $key, 2);
630 | isset($retval[$index[1]]) or $retval[$index[1]] = array();
631 | if ($largest < $index[0]) $largest = $index[0];
632 | $retval[$index[1]][$index[0]] = $value;
633 | }
634 |
635 | if (count($retval) == 0) return false; // no data
636 |
637 | // fill in holes and blanks the agent may "give" you
638 | foreach($retval as $k => $x) {
639 | for ($i = 1; $i <= $largest; $i++) {
640 | if (! isset($retval[$k][$i])) {
641 | $retval[$k][$i] = '';
642 | }
643 | }
644 | ksort($retval[$k]);
645 | }
646 | return($retval);
647 | }
648 |
649 |
--------------------------------------------------------------------------------
/graph.php:
--------------------------------------------------------------------------------
1 | 1) {
61 | $graphs_array = getAggregateGraphsArrayFromRequest($_GET);
62 | $stack = !isset($_GET['stack']) || $_GET['stack'] != 'false';
63 | $rrdtoolcmd = getStackedGraphsCmd($graphs_array, $type, $stack, $start, $end, $height, $width, $friendlytitle);
64 | } else {
65 | $rrdname = $_GET['rrdname'];
66 | $rrdfolder = $_GET['host'];
67 | $rrdtoolcmd = getGraphCmd($rrdname, $rrdfolder, $type, $start, $end, $height, $width, $friendlytitle);
68 | }
69 | }
70 |
71 |
72 | # Anti-caching techniques courtesy of Ganglia
73 |
74 | header ("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); // Date in the past
75 | header ("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT"); // always modified
76 | header ("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1
77 | header ("Pragma: no-cache"); // HTTP/1.0
78 |
79 | header ("Content-type: image/png");
80 |
81 | if ($debug >= 2) {
82 | header ("Content-type: text/html");
83 | echo $rrdtoolcmd;
84 | }
85 | else {
86 | if ($debug >= 1) {
87 | error_log("rrdtool graph command:" . $rrdtoolcmd);
88 | }
89 | passthru($rrdtoolcmd);
90 | }
91 |
92 |
--------------------------------------------------------------------------------
/header.php:
--------------------------------------------------------------------------------
1 |
86 |
--------------------------------------------------------------------------------
/index.php:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
8 | FITB - Welcome
9 |
10 |
11 |
12 |
13 |
14 | for the header ?>
15 |
16 | for the side bar ?>
17 |
18 |
Welcome to FITB
19 |
FITB is a automatic, RRDTool based graphing product that leaves no port untouched.
20 |
Select a host from the left.
21 |
It looks like you don\'t have PHP MySQL libraries installed. FITB will require these to connect to the
24 | database ';
25 | }
26 | if (!$link=connectToDB()) {
27 | echo '
FITB is having trouble connecting to your database. Have you set up MySQL with the FITB database and specified
28 | the correct connection parameters in config.php?
';
29 | }
30 |
31 | ?>
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/poller.php:
--------------------------------------------------------------------------------
1 | > {$cwd}/poller.log 2>&1 & echo $!";
25 | $pid = shell_exec($command);
26 | logline("MASTER: Child spawned, PID is {$pid}", 1, $verbose);
27 | } else {
28 | logline("MASTER: Not spawning polling child for {$thishost['prettyname']}, is disabled in config.php", 1, $verbose);
29 | }
30 | }
31 |
32 | logline("MASTER: Done spawning polling children", 0, $verbose);
33 | logline("MASTER: Writing last poll time", 2, $verbose);
34 | if (!file_put_contents($path_rrd . "lastpolltime.txt", time())) {
35 | logline("MASTER: Warning! Could not write last poll time, check directory perms", 0, $verbose);
36 | }
37 |
38 | # Execute cleanup job to purge any ports and RRD files older than $purgeage in the config
39 | purgeOld($verbose);
40 |
41 | logline("MASTER: Going away now. Be back later. ", 2, $verbose);
42 |
--------------------------------------------------------------------------------
/poller_child.php:
--------------------------------------------------------------------------------
1 | $thisint) {
53 | logline("{$pollprettyhost} - Starting interface loop for interface index {$intid} ({$thisint[2]})", 1, $verbose);
54 |
55 | # Check if the interface is up. No point graphing down interfaces.
56 | if (($thisint[7] == "1") && ($thisint['8'] == "1")) {
57 |
58 | # Assign the values to an array with names for easier referencing
59 | $thisint['inoctets'] = $ifXEntry[$intid][6];
60 | $thisint['outoctets'] = $ifXEntry[$intid][10];
61 | $thisint['inucastpkts'] = $ifXEntry[$intid][7];
62 | $thisint['outucastpkts'] = $ifXEntry[$intid][11];
63 | $thisint['indiscards'] = $thisint[13];
64 | $thisint['outdiscards'] = $thisint[19];
65 | $thisint['inerrors'] = $thisint[14];
66 | $thisint['outerrors'] = $thisint[20];
67 | $thisint['inmulticast'] = $ifXEntry[$intid][8];
68 | $thisint['outmulticast'] = $ifXEntry[$intid][12];
69 | $thisint['inbroadcast'] = $ifXEntry[$intid][9];
70 | $thisint['outbroadcast'] = $ifXEntry[$intid][13];
71 | $thisint['alias'] = $ifXEntry[$intid][18];
72 |
73 | # Sanitise the name
74 | $intname = str_replace("/", "-", $thisint[2]);
75 | $intname = str_replace(" ", "-", $intname);
76 | $intname = str_replace(":", "-", $intname);
77 | $intname = str_replace('"', "", $intname);
78 | $thisint['name'] = $intname;
79 |
80 | # Sanitise the alias
81 | $thisint['alias'] = str_replace('"', "", $thisint['alias']);
82 |
83 |
84 | logline("{$pollprettyhost} - {$intname} - Description for {$intname} is {$thisint['alias']}.", 2, $verbose);
85 |
86 | # Send data to carbon.
87 | if ($carbon) {
88 | foreach($graphite_metrics as $metric) {
89 | if ($thisint[$metric]) {
90 | fwrite($carbon, "{$graphite_prefix}.{$graphite_datacenter}-{$pollprettyhost}.{$thisint['name']}.{$metric} {$thisint[$metric]} {$timestamp}\n");
91 | }
92 | }
93 | }
94 |
95 | # This loop is going to run a lot. For every interface, create every graph.
96 | foreach($pollgraphs as $thisgraph) {
97 |
98 | logline("{$pollprettyhost} - {$intname} - Starting loop for interface {$intname} and graph type {$thisgraph}", 2, $verbose);
99 | $thisgraphdef = getGraphDefinition($thisgraph);
100 |
101 | $genrrdname = "{$pollprettyhost}-{$intname}_{$thisgraphdef['filesuffix']}.rrd";
102 |
103 | logline("{$pollprettyhost} - {$intname} - Starting find or create RRD for graphtype {$thisgraph} and interface {$thisint['name']}... ", 2, $verbose);
104 | if (!findOrCreateRRD($genrrdname, $pollprettyhost, $thisgraphdef['rrddef'])) {
105 | logline("{$pollprettyhost} - {$intname} - findOrCreateRRD returned false! Could not find or create the RRD file, check your permissions", 0, $verbose);
106 | return false;
107 | }
108 | logline("{$pollprettyhost} - {$intname} - Find or create rrd done", 2, $verbose);
109 |
110 | # This ugly little loop is neccesery to collect up all the data sources, get the right numbers and add a colon in the middle only.
111 | $insertvalues = "";
112 | $i = 0;
113 | $numberofds = count($thisgraphdef['datasources']);
114 | foreach($thisgraphdef['datasources'] as $thisds) {
115 | $insertvalues .= $thisint[$thisds];
116 | if($i < $numberofds - 1) {
117 | $insertvalues .= ":";
118 | }
119 | $i++;
120 | }
121 |
122 | logline("{$pollprettyhost} - {$intname} - Going to update RRD {$genrrdname} with data {$insertvalues}", 2, $verbose);
123 | updateRRD($genrrdname, $pollprettyhost, $timestamp, $insertvalues);
124 | logline("{$pollprettyhost} - {$intname} - Update RRD done", 1, $verbose);
125 |
126 | logline("{$pollprettyhost} - {$intname} - Updating database", 2, $verbose);
127 | # Insert the details of this graph/port into the database for future reference
128 | $link=connectToDB();
129 | # first, delete the previous row if it exists
130 | mysqli_query($link, 'DELETE FROM ports where host="' . $pollprettyhost . '" AND safename="' . $thisint['name']. '" AND graphtype="' . $thisgraph . '"');
131 | # Now insert the values
132 | mysqli_query($link, 'INSERT INTO ports (host, name, safename, filename, alias, graphtype, lastpoll)
133 | VALUES ("'.$pollprettyhost.'", "'.$thisint[2].'", "'.$thisint['name'].'", "'.$genrrdname.'", "'.$thisint['alias'].'", "'.$thisgraph.'", "'. $timestamp .'")');
134 |
135 | logline("{$pollprettyhost} - {$intname} - Done Updating database", 2, $verbose);
136 | logline("{$pollprettyhost} - {$intname} - Done loop for interface {$thisint['name']} and graph type {$thisgraph}", 2, $verbose);
137 | }
138 | } else {
139 | logline("{$pollprettyhost} - Interface {$thisint['name']} was either admin down or oper down, not polling this run", 1, $verbose);
140 | }
141 | logline("{$pollprettyhost} - {$intname} - Loop for interface {$thisint['name']} complete", 1, $verbose);
142 | }
143 |
144 | logline("{$pollprettyhost} - Poller has completed it's run for this host. ", 0, $verbose);
145 |
146 | # Disconnect carbon.
147 | if ($carbon) {
148 | fclose($carbon);
149 | }
150 |
--------------------------------------------------------------------------------
/rrds/.empty_dir:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lozzd/FITB/3f9f4b7129e0c1afe81031f239d88d2714ef48b5/rrds/.empty_dir
--------------------------------------------------------------------------------
/search.php:
--------------------------------------------------------------------------------
1 |
18 |
19 |
20 |
21 |
22 |
FITB - Search Results for
23 |
24 |
25 |
26 |
27 |
28 | for the header ?>
29 |
30 | for the side bar ?>
31 |
32 | Search Results for \"{$searchquery}\" on host {$host}";
35 | } elseif (isset($type)) {
36 | echo "
Search Results for \"{$searchquery}\", type {$type} ";
37 | } else {
38 | echo "
Search Results for \"{$searchquery}\" ";
39 | }
40 | ?>
41 | 0) {
59 | $numresults = mysqli_num_rows($result);
60 | echo "
{$numresults} results found
";
61 | while ($row = mysqli_fetch_assoc($result)) {
62 | $staletag = "";
63 | if ((time() - $row['lastpoll']) > $staleage) {
64 | $staletag = "STALE: ";
65 | }
66 | $friendlytitle = urlencode("{$staletag}{$row['host']} - {$row['name']} ({$row['alias']})");
67 | $basegraphurl = "graph.php?host={$row['host']}&rrdname={$row['host']}-{$row['safename']}&type={$row['graphtype']}{$start}&friendlytitle={$friendlytitle}";
68 | echo '
';
69 | echo ' ';
70 | }
71 | } else {
72 | echo "No results :( ";
73 | }
74 | ?>
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
--------------------------------------------------------------------------------
/side.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/viewaggregate.php:
--------------------------------------------------------------------------------
1 |
23 |
24 |
25 |
26 |
27 |
FITB
28 |
29 |
30 |
31 |
32 |
33 |
34 | for the header ?>
35 |
36 | for the side bar ?>
37 |
38 |
View aggregate
39 | ';
44 | foreach ($graphs_array as $g) {
45 | $host = $g['rrdfolder'];
46 | $port = str_replace($host.'-', '', $g['rrdname']);
47 | echo "
Host/Port: {$host} /{$port}
";
48 | }
49 | echo '
Delete this aggregate graph
';
50 | }
51 | echo '
* Shift-click a graph to start building a new aggregate.
';
52 | ?>
53 |
54 |
55 |
56 |
70 |
71 |
72 |
73 |
--------------------------------------------------------------------------------
/viewgraph.php:
--------------------------------------------------------------------------------
1 |
18 |
19 |
20 |
21 |
22 |
FITB - View graph -
23 |
24 |
25 |
26 |
27 |
28 |
29 | for the header ?>
30 |
31 | for the side bar ?>
32 |
33 |
View graph -
34 | 0) {
39 | $row = mysqli_fetch_assoc($result);
40 | echo "
";
41 | echo "
Port Name: {$row['name']}
";
42 | echo "
Port Alias:
{$row['alias']} ";
43 | echo '
On host: ' . $row['host'] . ' ';
44 | echo "
Status: ";
45 | if ((time() - $row['lastpoll']) > $staleage) {
46 | echo 'STALE
';
47 | } else {
48 | echo '
OK ';
49 | }
50 | echo "
Last polled: " . _ago($row['lastpoll']) . " ago
";
51 |
52 | # Insert the graph
53 | $friendlytitle = urlencode("{$viewhost} - {$row['name']} ({$row['alias']})");
54 | $basegraphurl = "graph.php?host={$viewhost}&rrdname={$viewhost}-{$row['safename']}&type={$row['graphtype']}{$start}&friendlytitle={$friendlytitle}";
55 | echo '
';
56 | } else {
57 | echo "Port/host/type combination not found! Was it polled yet?";
58 | }
59 | ?>
60 |
61 |
62 |
63 |
64 |
65 |
66 |
73 |
74 |
75 |
76 |
--------------------------------------------------------------------------------
/viewhost.php:
--------------------------------------------------------------------------------
1 |
15 |
16 |
17 |
18 |
19 |
FITB -
20 |
21 |
22 |
23 |
24 |
25 |
26 | for the header ?>
27 |
28 | for the side bar ?>
29 |
30 |
31 | 0) {
38 | echo '
';
39 | while ($row = mysqli_fetch_assoc($result)) {
40 | $staletag = "";
41 | if ((time() - $row['lastpoll']) > $staleage) {
42 | $staletag = "STALE: ";
43 | }
44 | $friendlytitle = urlencode("{$staletag}{$viewhost} - {$row['name']} ({$row['alias']})");
45 | $basegraphurl = "graph.php?host={$viewhost}&rrdname={$viewhost}-{$row['safename']}&type={$row['graphtype']}{$start}";
46 | echo '';
47 | echo '';
48 | echo ' ';
49 | echo ' ';
50 | }
51 | echo ' ';
52 | echo '
';
53 | } else {
54 | echo "This host does not exist, or it has not been polled yet. ";
55 | }
56 | } else {
57 | echo "
Connection to FITB database failed, have you set up the database and specified the correct connection parameters in config.php?";
58 | }
59 | ?>
60 |
61 |
62 |
63 |
64 |
65 |
66 |
82 |
83 |
84 |
85 |
86 |
--------------------------------------------------------------------------------
/viewport.php:
--------------------------------------------------------------------------------
1 |
13 |
14 |
15 |
16 |
17 |
FITB - View port -
18 |
19 |
20 |
21 |
22 |
23 | for the header ?>
24 |
25 | for the side bar ?>
26 |
27 |
View port -
28 | 0) {
33 |
34 | while ($row = mysqli_fetch_assoc($result)) {
35 | $staletag = "";
36 | if ((time() - $row['lastpoll']) > $staleage) {
37 | $staletag = "STALE: ";
38 | }
39 | $friendlytitle = urlencode("{$staletag}{$viewhost} - {$row['name']} ({$row['alias']})");
40 | $basegraphurl = "graph.php?host={$viewhost}&rrdname={$viewhost}-{$row['safename']}&type={$row['graphtype']}{$start}&friendlytitle={$friendlytitle}";
41 | echo '
';
42 | echo ' ';
43 | }
44 | } else {
45 | echo "This host does not exist, or it has not been polled yet. ";
46 | }
47 | ?>
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
--------------------------------------------------------------------------------