From c8e607a8a9f690128950fdba01507e6a400a6e4c Mon Sep 17 00:00:00 2001 From: Ad Schellevis Date: Sat, 21 Mar 2020 12:19:16 +0100 Subject: [PATCH] Shaper: add statiscs endpoint /api/trafficshaper/service/statistics for https://github.com/opnsense/core/issues/3994 --- .../TrafficShaper/Api/ServiceController.php | 98 ++++++++++++++++++- src/opnsense/scripts/shaper/lib/__init__.py | 1 + 2 files changed, 98 insertions(+), 1 deletion(-) diff --git a/src/opnsense/mvc/app/controllers/OPNsense/TrafficShaper/Api/ServiceController.php b/src/opnsense/mvc/app/controllers/OPNsense/TrafficShaper/Api/ServiceController.php index dfc7c6976..7e7a4495b 100644 --- a/src/opnsense/mvc/app/controllers/OPNsense/TrafficShaper/Api/ServiceController.php +++ b/src/opnsense/mvc/app/controllers/OPNsense/TrafficShaper/Api/ServiceController.php @@ -1,7 +1,7 @@ "failed"); } } + + /** + * fetch current statistics + */ + public function statisticsAction() + { + $result = array("status" => "failed"); + if ($this->request->isGet()) { + // close session for long running action + $this->sessionClose(); + $ipfwstats = json_decode((new Backend())->configdRun("ipfw stats"), true); + if ($ipfwstats != null) { + // ipfw stats are stuctured as they would be using the various ipfw commands, let's reformat + // into something easier to handle from the UI and attach model data. + $result['status'] = "ok"; + $result['items'] = array(); + $pipenrs = array(); + if (!empty($ipfwstats['pipes'])) { + $shaperModel = new TrafficShaper(); + // traverse pipes + foreach ($ipfwstats['pipes'] as $pipeid => $pipe) { + $pipenrs[] = $pipeid; + $item = $pipe; + $item['type'] = "pipe"; + $item['id'] = $pipeid; + $result['items'][] = $item; + foreach ($ipfwstats['queues'] as $queueid => $queue) { + if ($queue['sched_nr'] == $pipeid) { + // XXX: sched_nr seems to be the linking pin to pipe + $item = $queue; + $item['type'] = "queue"; + $item['id'] = $pipeid . "." . $queueid; + $result['items'][] = $item; + } + } + } + // XXX: If not directly connected, we better still list the queues so we know what we miss. + // current assumption is this doesn't happen on our setups, should be removed in the future + $stray_queues = false; + foreach ($ipfwstats['queues'] as $queueid => $queue) { + if (!in_array($queue['sched_nr'], $pipenrs)) { + if (!$stray_queues) { + $result['items'][] = [ + "type" => "unknown", + "id" => "XXXXX" + ]; + $stray_queues = true; + } + $item = $queue; + $item['type'] = "queue"; + $item['id'] = "XXXXX." . $queueid; + $result['items'][] = $item; + } + } + // collect model properties + foreach ($result['items'] as &$item) { + $idfield = $item['type'] == 'queue' ? "flow_set_nr" : "pipe"; + // link pipe and queue descriptions + $item['description'] = ""; + if (in_array($item['type'], ['queue', 'pipe'])) { + if ($item['type'] == 'pipe') { + $root = $shaperModel->pipes->pipe; + } else { + $root = $shaperModel->queues->queue; + } + foreach ($root->iterateItems() as $node) { + if ((string)$node->number == $item[$idfield]) { + $item['description'] = (string)$node->description; + $item['uuid'] = (string)$node->getAttribute('uuid'); + break; + } + } + } + // link rules (with statistics) + $item['rules'] = []; + if (!empty($ipfwstats['rules']["{$item['type']}s"])) { + foreach ($ipfwstats['rules']["{$item['type']}s"] as $rule) { + if ($item[$idfield] == $rule['attached_to']) { + $rule['description'] = ""; + if ($rule['rule_uuid'] != null) { + $node = $shaperModel->getNodeByReference("rules.rule.{$rule['rule_uuid']}"); + if ($node != null) { + $rule['description'] = (string)$node->description; + } + } + $item['rules'][] = $rule; + } + } + } + } + } + } + } + return $result ; + } } diff --git a/src/opnsense/scripts/shaper/lib/__init__.py b/src/opnsense/scripts/shaper/lib/__init__.py index 6a53cc44d..cd74863be 100755 --- a/src/opnsense/scripts/shaper/lib/__init__.py +++ b/src/opnsense/scripts/shaper/lib/__init__.py @@ -185,6 +185,7 @@ def parse_ipfw_rules(): 'pkts': parts[1], 'bytes': parts[2], 'accessed': parts[3], + 'attached_to': parts[5], 'rule_uuid': None } if line.find('//') > -1: