Project

General

Profile

Download (13.8 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/*
3
 * status_queues.php
4
 *
5
 * part of pfSense (https://www.pfsense.org)
6
 * Copyright (c) 2004-2013 BSD Perimeter
7
 * Copyright (c) 2013-2016 Electric Sheep Fencing
8
 * Copyright (c) 2014-2020 Rubicon Communications, LLC (Netgate)
9
 * All rights reserved.
10
 *
11
 * Licensed under the Apache License, Version 2.0 (the "License");
12
 * you may not use this file except in compliance with the License.
13
 * You may obtain a copy of the License at
14
 *
15
 * http://www.apache.org/licenses/LICENSE-2.0
16
 *
17
 * Unless required by applicable law or agreed to in writing, software
18
 * distributed under the License is distributed on an "AS IS" BASIS,
19
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20
 * See the License for the specific language governing permissions and
21
 * limitations under the License.
22
 */
23

    
24
##|+PRIV
25
##|*IDENT=page-status-trafficshaper-queues
26
##|*NAME=Status: Traffic Shaper: Queues
27
##|*DESCR=Allow access to the 'Status: Traffic Shaper: Queues' page.
28
##|*MATCH=status_queues.php*
29
##|-PRIV
30
/*
31
header("Last-Modified: " . gmdate("D, j M Y H:i:s") . " GMT");
32
header("Expires: " . gmdate("D, j M Y H:i:s", time()) . " GMT");
33
header("Cache-Control: no-cache, no-store, must-revalidate"); // HTTP/1.1
34
header("Pragma: no-cache"); // HTTP/1.0
35
*/
36

    
37
require_once("guiconfig.inc");
38
class QueueStats {
39
	public $queuename;
40
	public $queuelength;
41
	public $pps;
42
	public $bandwidth;
43
	public $borrows;
44
	public $suspends;
45
	public $drops;
46
}
47

    
48
include_once("shaper.inc");
49

    
50
$stats = get_queue_stats();
51

    
52
$pgtitle = array(gettext("Status"), gettext("Queues"));
53
$shortcut_section = "trafficshaper";
54
include("head.inc");
55

    
56
if (!isset($config['shaper']['queue']) || !is_array($config['shaper']['queue']) || count($config['shaper']['queue']) < 1) {
57
	print_info_box(gettext("Traffic shaping is not configured."));
58
	include("foot.inc");
59
	exit;
60
}
61

    
62
if (!$error): ?>
63
<form action="status_queues.php" method="post">
64
<script type="text/javascript">
65
//<![CDATA[
66
var refreshrate = 1000;
67
var queuestathistorylength = 0;
68
var queuestathistory = [];
69
var queuestatprevious = [];
70
var timestampprevious;
71
var graphstatmax = 0;
72
events.push(function() {
73
	$('#updatespeed').on('change', function() {
74
		refreshrate = $("#updatespeed").val();
75
	});
76

    
77
	function getqueueactivity() {
78
		var url = "/getqueuestats.php";
79
		var pars = "format=json";
80
		$.ajax(
81
			url,
82
			{
83
				type: 'post',
84
				data: pars,
85
				complete: activitycallback
86
			});
87
	}
88

    
89
	function escapeStr(str)
90
	{
91
		if (str)
92
			return str.replace(/([ #;?%&,.+*~\':"!^$[\]()=>|\/@])/g,'\\$1');
93
		return str;
94
	}
95

    
96
	function activitycallback(transport) {
97
		setTimeout(getqueueactivity, refreshrate);
98
		json = transport.responseJSON;
99
		if (!json) {
100
			return;
101
		}
102
		timestamp = json.timestamp;
103
		timestampdiff = timestamp - timestampprevious;
104
		$stattype = $('#selStatistic').val();
105

    
106
		interfacename_stats = [];
107
		for (interfacename in json.interfacestats) {
108
			var queueparents = [];
109
			interface = json.interfacestats[interfacename];
110
			interfacename_stats[interfacename] = [];
111
			for (queuename in interface) {
112
				queue = interface[queuename];
113
				statqname = queue['name'] + queue['interface'];
114

    
115
				for(childnr in queue['contains']) {
116
					child = queue['contains'][childnr];
117
					if (!queueparents[child]) {
118
						queueparents[child] = [];
119
					}
120
					queueparents[child] = queuename;
121
				}
122

    
123
				if (queuestatprevious[statqname]) {
124
					interfacename_stats[interfacename][statqname] = [];
125
					pkts_ps = (queue['pkts'] - queuestatprevious[statqname]['pkts']) / timestampdiff
126
					bytes_ps = (queue['bytes'] - queuestatprevious[statqname]['bytes']) / timestampdiff
127
					droppedpkts = parseFloat(queue['droppedpkts']);
128
					borrows = parseFloat(queue['borrows']);
129
					suspends = parseFloat(queue['suspends']);
130
					interfacename_stats[interfacename][statqname]['pkts_ps'] = pkts_ps;
131
					interfacename_stats[interfacename][statqname]['bytes_ps'] = bytes_ps;
132
					interfacename_stats[interfacename][statqname]['borrows'] = borrows;
133
					interfacename_stats[interfacename][statqname]['suspends'] = suspends;
134
					interfacename_stats[interfacename][statqname]['droppedpkts'] = droppedpkts;
135
					interfacename_stats[interfacename][statqname]['qlengthitems'] = queue['qlengthitems'];
136
					interfacename_stats[interfacename][statqname]['qlengthsize'] = queue['qlengthsize'];
137
					find = queuename;
138
					while(queueparents[find]) {
139
						// add diff values also to parent queues
140
						parentname = queueparents[find];
141
						parentqueuename = parentname+interfacename;
142
						interfacename_stats[interfacename][parentqueuename]['pkts_ps'] += pkts_ps;
143
						interfacename_stats[interfacename][parentqueuename]['bytes_ps'] += bytes_ps;
144
						interfacename_stats[interfacename][parentqueuename]['borrows'] += borrows;
145
						interfacename_stats[interfacename][parentqueuename]['suspends'] += suspends;
146
						interfacename_stats[interfacename][parentqueuename]['droppedpkts'] += droppedpkts;
147
						find = parentname;
148
					}
149
				}
150
				queuestatprevious[statqname] = queue;
151
			}
152
		}
153
		// Find max pps/bps needed for any scale bar
154
		statmax = 0;
155
		for (interfacename in interfacename_stats) {
156
			interface = interfacename_stats[interfacename];
157
			for (queuename in interface) {
158
				queue = interface[queuename];
159
				if ($stattype == "0") {
160
					if (statmax < queue['pkts_ps']) {
161
						statmax = queue['pkts_ps'];
162
					}
163
				} else {
164
					if (statmax < queue['bytes_ps']) {
165
						statmax = queue['bytes_ps'];
166
					}
167
				}
168
			}
169
		}
170
		// use a slowly sliding max scale value but do make sure its always large enough to accommodate the largest value..
171
		if (graphstatmax < statmax) {
172
			// peek value + 10% keeps a little room for it to increase
173
			graphstatmax = statmax * 1.1;
174
		} else {
175
			// in general make largest bar fill +- 2/3 of the scale
176
			graphstatmax = (graphstatmax * 20 + statmax * 1.5) / 21;
177
		}
178
		// set values on the objects
179
		for (interfacename in interfacename_stats) {
180
			interface = interfacename_stats[interfacename];
181
			for (queuename in interface) {
182
				queue = interface[queuename];
183
				statqname = escapeStr(queuename);
184
				if ($stattype == "0") {
185
					$('#queue'+statqname+'width').css('width', (queue['pkts_ps']*100/graphstatmax).toFixed(0) + '%');
186
				} else {
187
					$('#queue'+statqname+'width').css('width', (queue['bytes_ps']*100/graphstatmax).toFixed(0) + '%');
188
				}
189
				$('#queue'+statqname+'pps').val(queue['pkts_ps'].toFixed(1));
190
				$('#queue'+statqname+'bps').val(formatSpeedBits(queue['bytes_ps']));
191
				$('#queue'+statqname+'borrows').val(queue['borrows']);
192
				$('#queue'+statqname+'suspends').val(queue['suspends']);
193
				$('#queue'+statqname+'drops').val(queue['droppedpkts']);
194
				$('#queue'+statqname+'length').val(queue['qlengthitems']+'/'+queue['qlengthsize']);
195
			}
196
		}
197
		timestampprevious = timestamp;
198
	}
199

    
200
	$(document).ready(function() {
201
		setTimeout(getqueueactivity, 150);
202
	});
203
});
204

    
205
function formatSpeedBits(speed) {
206
	// format speed in bits/sec, input: bytes/sec
207
	if (speed < 125000) {
208
		return Math.round(speed / 125) + " <?=gettext("Kbps"); ?>";
209
	}
210
	if (speed < 125000000) {
211
		return Math.round(speed / 1250)/100 + " <?=gettext("Mbps"); ?>";
212
	}
213
	// else
214
	return Math.round(speed / 1250000)/100 + " <?=gettext("Gbps"); ?>";  /* wow! */
215
}
216
//]]>
217
</script>
218
<?php endif;
219

    
220
if ($error):
221
	print_info_box($error);
222
else: ?>
223
	<div class="panel panel-default">
224
	<div class="panel-heading"><h2 class="panel-title"><?=gettext("Settings"); ?></h2></div>
225
		<div class="panel-body table-responsive">
226
			<label class="col-sm-2 control-label">
227
				<span>Refresh rate</span>
228
			</label>
229
			<div class="col-sm-10">
230
				<select id="updatespeed" class="form-control">
231
					<option value="500">0.5 <?=gettext("seconds");?></option>
232
					<option value="1000" selected>1 <?=gettext("seconds");?></option>
233
					<option value="2000">2 <?=gettext("seconds");?></option>
234
					<option value="5000">5 <?=gettext("seconds");?></option>
235
				</select>
236
			</div>
237
		</div>
238
	<div class="panel-heading"><h2 class="panel-title"><?=gettext("Status Queues"); ?></h2></div>
239
		<div class="panel-body table-responsive">
240
			<table class="table table-striped table-hover">
241
				<thead>
242
					<tr>
243
						<th><?=gettext("Queue"); ?></th>
244
						<th><?=gettext("Statistics"); ?>
245
							<select id="selStatistic" class="form-control">
246
								<option value="0"><?=gettext("PPS");?></option>
247
								<option value="1"><?=gettext("Bandwidth");?></option>
248
							</select>
249
						</th>
250
						<th><?=gettext("PPS"); ?></th>
251
						<th><?=gettext("Bandwidth"); ?></th>
252
						<th><?=gettext("Borrows"); ?></th>
253
						<th><?=gettext("Suspends"); ?></th>
254
						<th><?=gettext("Drops"); ?></th>
255
						<th><?=gettext("Length"); ?></th>
256
					</tr>
257
				</thead>
258
				<tbody>
259
<?php
260
	$if_queue_list = get_configured_interface_list_by_realif(true);
261
	processInterfaceQueues($stats, 0, "");
262
?>
263
<?php endif; ?>
264
				</tbody>
265
			</table>
266
			<br />
267
			<div class="infoblock blockopen">
268
<?php
269
	print_info_box(gettext("Queue graphs sample data on a regular interval."), 'info', false);
270
?>
271
			</div>
272
		</div>
273
	</div>
274
<br/>
275

    
276
<script type="text/javascript">
277
//<![CDATA[
278
	function StatsShowHide(classname) {
279
		var firstrow = $("." + classname).first();
280
		if (firstrow.is(':visible')) {
281
			$("." + classname).hide();
282
		} else {
283
			$("." + classname).show();
284
		}
285
	}
286
//]]>
287
</script>
288
</form>
289
<?php
290

    
291
include("foot.inc");
292

    
293
function processInterfaceQueues($altqstats, $parent_name) {
294
	global $g;
295
	global $if_queue_list;
296

    
297
	$parent_name = $parent_name . " queuerow" . $altqstats['name'] . convert_real_interface_to_friendly_interface_name($altqstats['interface']);
298
	$prev_if = $altqstats['interface'];
299
	if (!is_array($altqstats['interfacestats'])) {
300
		print("<tr><td>");
301
		print("No Queue data available");
302
		print("</td></tr>");
303
		return;
304
	}
305
	foreach ($altqstats['interfacestats'] as $if => $ifq) {
306
		$parents = array();
307
		echo "<tr><td colspan=\"8\"><b>Interface " . htmlspecialchars(convert_real_interface_to_friendly_descr($if)) . "</b></td></tr>\n";
308
		$if_name = "";
309
		foreach ($if_queue_list as $oif => $real_name) {
310
			if ($oif == $if) {
311
				$if_name = $real_name;
312
				break;
313
			}
314
		}
315
		if ($prev_if != $q['interface']) {
316
			$prev_if = $q['interface'];
317
		}
318
		foreach($ifq as $qkey => $q) {
319
			$parent_name = $if . " queuerow" . $altqstats['name'] . convert_real_interface_to_friendly_interface_name($altqstats['interface']);
320
			if (isset($q['contains'])) {
321
				foreach($q['contains'] as $child) {
322
					$parents[$child] = $qkey;
323
				}
324
			}
325
			$find = $qkey;
326
			$level = 0;
327
			while(isset($parents[$find])) {
328
				$find = $parents[$find];
329
				$level++;
330
				$parent_name = $parent_name . " queuerow" . $find . $q['interface'];
331
			}
332
			$qfinterface = convert_real_interface_to_friendly_interface_name($q['interface']);
333
			$qname = str_replace($q['interface'], $qfinterface, $q['name']);
334
?>
335
			<tr class="<?=$parent_name;?>">
336
				<td class="<?=$row_class?>" style="padding-left:<?=$level * 20?>px;">
337
					<?php
338
					if (is_array($q['contains'])) {
339
						echo "<a href=\"#\" onclick=\"StatsShowHide('queuerow{$qname}{$qfinterface}');return false\">+/-</a>";
340
					}
341
					if (strstr($qname, "root_")) {
342
						echo "<a href=\"firewall_shaper.php?interface={$if_name}&amp;queue={$if_name}&amp;action=show\">Root queue</a>";
343
					} else {
344
						echo "<a href=\"firewall_shaper.php?interface={$if_name}&amp;queue={$qname}&amp;action=show\">" . htmlspecialchars($qname) . "</a>";
345
					}
346
					?>
347
				</td>
348
<?php
349
			$cpuUsage = 0;
350
			$stat_prefix = "queue" . $q['name'] . $q['interface'];
351
			print('<td>');
352
			print('<div class="progress" style="height: 7px;width: 170px;">');
353
			print('		<div class="progress-bar" role="progressbar" id="' . $stat_prefix . 'width" aria-valuenow="70" aria-valuemin="0" aria-valuemax="100" style="width: ' . $cpuUsage*100 . '%;"></div>');
354
			print('	  </div>');
355
			print('</td>');
356
			print('<td><input readonly style="border:0;width:70px;text-align:right;" name="' . $stat_prefix . 'pps"      id="' . $stat_prefix . 'pps"      value="(' . gettext("Loading") . ')" /></td>');
357
			print('<td><input readonly style="border:0;width:80px;text-align:right;" name="' . $stat_prefix . 'bps"      id="' . $stat_prefix . 'bps"      value="" /></td>');
358
			print('<td><input readonly style="border:0;width:70px;text-align:right;" name="' . $stat_prefix . 'borrows"  id="' . $stat_prefix . 'borrows"  value="" /></td>');
359
			print('<td><input readonly style="border:0;width:70px;text-align:right;" name="' . $stat_prefix . 'suspends" id="' . $stat_prefix . 'suspends" value="" /></td>');
360
			print('<td><input readonly style="border:0;width:70px;text-align:right;" name="' . $stat_prefix . 'drops"    id="' . $stat_prefix . 'drops"    value="" /></td>');
361
			print('<td><input readonly style="border:0;width:70px;text-align:right;" name="' . $stat_prefix . 'length"   id="' . $stat_prefix . 'length"   value="" /></td>');
362
?>
363
			</tr>
364
<?php
365
			if (is_array($q['queue'])) {
366
				processInterfaceQueues($q, $level + 1, $parent_name);
367
			}
368
		}
369
	};
370
}
371

    
372
function statsQueues($xml) {
373
	global $statistics;
374

    
375
	$fname = convert_real_interface_to_friendly_interface_name($xml['interface']);
376
	$qname = str_replace($xml['interface'], $fname, $xml['name']);
377

    
378
	$current = new QueueStats();
379
	$child = new QueueStats();
380
	$current->queuename = $qname . $fname;
381
	$current->queuelength = $xml['qlength'];
382
	$current->pps = $xml['measured'];
383
	$current->bandwidth = $xml['measuredspeedint'];
384
	$current->borrows = intval($xml['borrows']);
385
	$current->suspends = intval($xml['suspends']);
386
	$current->drops = intval($xml['droppedpkts']);
387
	if (is_array($xml['queue'])) {
388
		foreach ($xml['queue'] as $q) {
389
			$child = statsQueues($q);
390
			$current->pps += $child->pps;
391
			$current->bandwidth += $child->bandwidth;
392
			$current->borrows += $child->borrows;
393
			$current->suspends += $child->suspends;
394
			$current->drops += $child->drops;
395
		}
396
	}
397
	unset($child);
398
	$statistics[] = $current;
399
	return $current;
400
}
401
function format_bits($bits) {
402
	if ($bits >= 1000000000) {
403
		return sprintf("%.2f Gbps", $bits/1000000000);
404
	} else if ($bits >= 1000000) {
405
		return sprintf("%.2f Mbps", $bits/1000000);
406
	} else if ($bits >= 1000) {
407
		return sprintf("%.2f Kbps", $bits/1000);
408
	} else {
409
		return sprintf("%d bps", $bits);
410
	}
411
}
412
?>
(179-179/227)