From 0e2bed223959d8c639b3d9bacb1b03a87a067c3c Mon Sep 17 00:00:00 2001 From: PiBa-NL Date: Mon, 31 Jul 2017 01:33:26 +0200 Subject: [PATCH] status_queues, provide 'realtime' statistics -retrieve 'current' numbers from pfSense not using qstats provides the following advantages: -no long lag which requires 18 updates to get 90% accurate values on screen -showing queue's in 2 browsers does not show half the value status_queues, altq codel queue does not show queues, and when cbq without options doesn't tell its 'type' on the queue line then still parse its borrows and suspends status_queues, move function get_queue_stats() to shaper.inc --- src/etc/inc/shaper.inc | 78 +++++++ src/usr/local/www/getqueuestats.php | 40 ++++ src/usr/local/www/status_queues.php | 320 ++++++++++++++++++---------- 3 files changed, 330 insertions(+), 108 deletions(-) create mode 100644 src/usr/local/www/getqueuestats.php diff --git a/src/etc/inc/shaper.inc b/src/etc/inc/shaper.inc index f9388349d1..2546fa0bf3 100644 --- a/src/etc/inc/shaper.inc +++ b/src/etc/inc/shaper.inc @@ -29,6 +29,84 @@ require_once("globals.inc"); require_once("functions.inc"); require_once("util.inc"); require_once("notices.inc"); +include_once("interfaces.inc");//required for convert_real_interface_to_friendly_interface_name() in get_queue_stats() to get altq interface name. + +function get_queue_stats() { + // due to sysutils\qstats not providing accurate stats with 'currently' valid numbers + // in its current implementation this php function does the job for now.. + $result = array(); + $result['timestamp'] = gettimeofday(true); + $r = exec("/sbin/pfctl -s queue -v", $output, $ret); + $interfacestats = array(); + foreach($output as $line) { + $partial = explode('{', $line); + $items = explode(" ", $partial[0]); + if (isset($partial[1])) { + $contains = explode(", ", substr($partial[1], 0, strlen($partial[1]) - 1)); + } else { + unset($contains); + } + if ($items[0] == "queue") { + $level = 1; + while (empty($items[$level])) { + $level++; + } + unset($newqueue); + $newqueue = array(); + $currentqueuename = $items[$level]; + $currentqueueif = $items[$level+2]; + $newqueue['name'] = $currentqueuename; + $newqueue['interface'] = $items[$level+2]; + + if ($items[$level+3] == "bandwidth") { + $level += 2; + } + if ($items[$level+3] == "priority") { + $level += 2; + } + if ($items[$level+3] == "qlimit") { + $level += 2; + } + $tmp = $items[$level+3]; + if (substr($tmp,strlen($tmp)-1) == "(") { + $newqueue['shapertype'] = substr($tmp, 0, strlen($tmp)-1); + } + + $interfacestats[$currentqueueif][$currentqueuename] = &$newqueue; + if (is_array($contains)) { + $newqueue['contains'] = $contains; + } + } elseif ($items[0] == "altq") { + unset($newqueue); + $newqueue = array(); + $currentqueuename = convert_real_interface_to_friendly_interface_name($items[2]); + $currentqueueif = $items[2]; + $newqueue['name'] = $currentqueuename; + $newqueue['interface'] = $items[2]; + $interfacestats[$currentqueueif][$currentqueuename] = &$newqueue; + } else { + if ($items[3] == "pkts:") { + $newqueue["pkts"] = trim(substr($line, 10, 10)); + $newqueue["bytes"] = trim(substr($line, 29, 10)); + $newqueue["droppedpkts"] = trim(substr($line, 55, 6)); + $newqueue["droppedbytes"] = trim(substr($line, 69, 6)); + } + if ($items[3] == "qlength:") { + $subitems = explode("/", substr($line, 13, 8)); + $newqueue["qlengthitems"] = trim($subitems[0]); + $newqueue["qlengthsize"] = trim($subitems[1]); + if (substr($line, 22, 8) == "borrows:") { + $newqueue["borrows"] = trim(substr($line, 31, 7)); + } + if (substr($line, 39, 9) == "suspends:") { + $newqueue["suspends"] = trim(substr($line, 49, 7)); + } + } + } + } + $result['interfacestats'] = $interfacestats; + return $result; +} /* * I admit :) this is derived from xmlparse.inc StartElement() diff --git a/src/usr/local/www/getqueuestats.php b/src/usr/local/www/getqueuestats.php new file mode 100644 index 0000000000..ce4938ceb0 --- /dev/null +++ b/src/usr/local/www/getqueuestats.php @@ -0,0 +1,40 @@ +pps) { - $bigger_stat = $q->pps; - } - } - } else { - foreach ($statistics as $q) { - if ($bigger_stat < $q->bandwidth) { - $bigger_stat = $q->bandwidth; - } - } - } - $finscript = ""; - foreach ($statistics as $q) { - if ($stat_type == "0" && $bigger_stat != "0") { - $packet_s = round(100 * ($q->pps / $bigger_stat), 0); - } else if ($bigger_stat != "0") { - $packet_s = round(100 * ($q->bandwidth / $bigger_stat), 0); - } else { - $packet_s = 0; - } - $finscript .= "$('#queue{$q->queuename}width').css('width','{$packet_s}%');"; - $finscript .= "$('#queue{$q->queuename}pps').val('" . number_format($q->pps, 1) . "');"; - $finscript .= "$('#queue{$q->queuename}bps').val('" . format_bits($q->bandwidth) . "');"; - $finscript .= "$('#queue{$q->queuename}borrows').val('{$q->borrows}');"; - $finscript .= "$('#queue{$q->queuename}suspends').val('{$q->suspends}');"; - $finscript .= "$('#queue{$q->queuename}drops').val('{$q->drops}');"; - $finscript .= "$('#queue{$q->queuename}length').val('{$q->queuelength}');"; - } - unset($statistics, $altqstats); - header("Content-type: text/javascript"); - echo $finscript; - exit; -} + +include_once("shaper.inc"); + +$stats = get_queue_stats(); + $pgtitle = array(gettext("Status"), gettext("Queues")); $shortcut_section = "trafficshaper"; include("head.inc"); @@ -121,11 +61,20 @@ if (!$error): ?>
+

+
+ +
+ +
+

@@ -174,7 +256,7 @@ else: ?> @@ -182,7 +264,7 @@ else: ?>
@@ -206,59 +288,81 @@ else: ?> include("foot.inc"); -function processQueues($altqstats, $level, $parent_name) { +function processInterfaceQueues($altqstats, $parent_name) { global $g; global $if_queue_list; - + $parent_name = $parent_name . " queuerow" . $altqstats['name'] . convert_real_interface_to_friendly_interface_name($altqstats['interface']); $prev_if = $altqstats['interface']; - foreach ($altqstats['queue'] as $q) { + if (!is_array($altqstats['interfacestats'])) { + print(""); + return; + } + foreach ($altqstats['interfacestats'] as $if => $ifq) { + $parents = array(); + echo "\n"; $if_name = ""; foreach ($if_queue_list as $oif => $real_name) { - if ($oif == $q['interface']) { + if ($oif == $if) { $if_name = $real_name; break; } } if ($prev_if != $q['interface']) { - echo "\n"; $prev_if = $q['interface']; } - $qfinterface = convert_real_interface_to_friendly_interface_name($q['interface']); - $qname = str_replace($q['interface'], $qfinterface, $q['name']); - -?> - - -'); - print('
'); - print('
'); - print('
'); - print(''); - print(''); - print(''); - print(''); - print(''); - print(''); - print(''); + } + $find = $qkey; + $level = 0; + while(isset($parents[$find])) { + $find = $parents[$find]; + $level++; + $parent_name = $parent_name . " queuerow" . $find . $q['interface']; + } + $qfinterface = convert_real_interface_to_friendly_interface_name($q['interface']); + $qname = str_replace($q['interface'], $qfinterface, $q['name']); ?> - + + '); + print('
'); + print('
'); + print('
'); + print(''); + print(''); + print(''); + print(''); + print(''); + print(''); + print(''); +?> + +
"); + print("No Queue data available"); + print("
Interface " . htmlspecialchars(convert_real_interface_to_friendly_descr($if)) . "
Interface " . htmlspecialchars(convert_real_interface_to_friendly_descr($q['interface'])) . "
- +/-"; + foreach($ifq as $qkey => $q) { + $parent_name = $if . " queuerow" . $altqstats['name'] . convert_real_interface_to_friendly_interface_name($altqstats['interface']); + if (isset($q['contains'])) { + foreach($q['contains'] as $child) { + $parents[$child] = $qkey; } - if (strstr($qname, "root_")) { - echo "Root queue"; - } else { - echo "" . htmlspecialchars($qname) . ""; - } - ?> -
+ +/-"; + } + if (strstr($qname, "root_")) { + echo "Root queue"; + } else { + echo "" . htmlspecialchars($qname) . ""; + } + ?> +