Project

General

Profile

Download (12.5 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-2024 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");
34
*/
35

    
36
require_once("guiconfig.inc");
37
include_once("shaper.inc");
38

    
39
$stats = get_queue_stats();
40

    
41
$pgtitle = array(gettext("Status"), gettext("Queues"));
42
$shortcut_section = "trafficshaper";
43
include("head.inc");
44

    
45
if (count(config_get_path('shaper/queue', [])) < 1) {
46
	print_info_box(gettext("Traffic shaping is not configured."));
47
	include("foot.inc");
48
	exit;
49
}
50

    
51
if (!$error): ?>
52
<form action="status_queues.php" method="post">
53
<script type="text/javascript">
54
//<![CDATA[
55
var refreshrate = 1000;
56
var queuestathistorylength = 0;
57
var queuestathistory = [];
58
var queuestatprevious = [];
59
var timestampprevious;
60
var graphstatmax = 0;
61
events.push(function() {
62
	$('#updatespeed').on('change', function() {
63
		refreshrate = $("#updatespeed").val();
64
	});
65

    
66
	function getqueueactivity() {
67
		var url = "/getqueuestats.php";
68
		var pars = "format=json";
69
		$.ajax(
70
			url,
71
			{
72
				type: 'post',
73
				data: pars,
74
				complete: activitycallback
75
			});
76
	}
77

    
78
	function escapeStr(str)
79
	{
80
		if (str)
81
			return str.replace(/([ #;?%&,.+*~\':"!^$[\]()=>|\/@])/g,'\\$1');
82
		return str;
83
	}
84

    
85
	function activitycallback(transport) {
86
		setTimeout(getqueueactivity, refreshrate);
87
		json = transport.responseJSON;
88
		if (!json) {
89
			return;
90
		}
91
		timestamp = json.timestamp;
92
		timestampdiff = timestamp - timestampprevious;
93
		$stattype = $('#selStatistic').val();
94

    
95
		interfacename_stats = [];
96
		for (interfacename in json.interfacestats) {
97
			var queueparents = [];
98
			interface = json.interfacestats[interfacename];
99
			interfacename_stats[interfacename] = [];
100
			for (queuename in interface) {
101
				queue = interface[queuename];
102
				statqname = queue['name'] + queue['interface'];
103

    
104
				for(childnr in queue['contains']) {
105
					child = queue['contains'][childnr];
106
					if (!queueparents[child]) {
107
						queueparents[child] = [];
108
					}
109
					queueparents[child] = queuename;
110
				}
111

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

    
191
	$(document).ready(function() {
192
		setTimeout(getqueueactivity, 150);
193
	});
194
});
195

    
196
function formatSpeedBits(speed) {
197
	// format speed in bits/sec, input: bytes/sec
198
	if (speed < 125000) {
199
		return Math.round(speed / 125) + " <?=gettext("Kbps"); ?>";
200
	}
201
	if (speed < 125000000) {
202
		return Math.round(speed / 1250)/100 + " <?=gettext("Mbps"); ?>";
203
	}
204
	// else
205
	return Math.round(speed / 1250000)/100 + " <?=gettext("Gbps"); ?>";  /* wow! */
206
}
207
//]]>
208
</script>
209
<?php endif;
210

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

    
267
<script type="text/javascript">
268
//<![CDATA[
269
	function StatsShowHide(classname) {
270
		var firstrow = $("." + classname).first();
271
		if (firstrow.is(':visible')) {
272
			$("." + classname).hide();
273
		} else {
274
			$("." + classname).show();
275
		}
276
	}
277
//]]>
278
</script>
279
</form>
280
<?php
281

    
282
include("foot.inc");
283

    
284
function processInterfaceQueues($altqstats, $parent_name) {
285
	global $g;
286
	global $if_queue_list;
287

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