From 39bbffb928e2aa7ad582fb3e14a6de5538cb4853 Mon Sep 17 00:00:00 2001 From: Ad Schellevis Date: Mon, 26 Jun 2023 15:55:00 +0200 Subject: [PATCH] net/frr: Diagnostics overhaul (#3484) * net/frr: Diagnostics overhaul [2] - https://github.com/opnsense/plugins/pull/3263 o new extensible template for tabbed views o get rid of diagnostics form - was unused anyway o get rid of lodash dependency o the API output is HTML-encoded, this fixes resulting display issues o move search functionality to controller. o move bgp additional attributes inside the record set so people can show them on request (the tree functions might better to move into a core Javascript file to reduce duplication, but for now this is fine.) o add formatters in bootgrid to mimic the previous behavior o add interface names (descriptions) for easier reading in route endpoints o cleanups, move bgp routerid and localAS to subtitle and offer a "generic" hook for endpoints. remove unused elements from the volt template. --------- Co-authored-by: Marc Leuser --- .../Quagga/Api/DiagnosticsController.php | 170 ++++++- .../OPNsense/Quagga/DiagnosticsController.php | 88 +++- .../OPNsense/Quagga/forms/diagnostics.xml | 8 - .../views/OPNsense/Quagga/diagnostics.volt | 341 +++++++++++++ .../views/OPNsense/Quagga/diagnosticsbgp.volt | 143 ------ .../OPNsense/Quagga/diagnosticsgeneral.volt | 142 ------ .../OPNsense/Quagga/diagnosticsospf.volt | 475 ------------------ .../scripts/frr/legacy-diagnostics.py | 3 +- .../www/js/quagga/diagnostics_utils.js | 207 ++++++++ 9 files changed, 801 insertions(+), 776 deletions(-) delete mode 100644 net/frr/src/opnsense/mvc/app/controllers/OPNsense/Quagga/forms/diagnostics.xml create mode 100644 net/frr/src/opnsense/mvc/app/views/OPNsense/Quagga/diagnostics.volt delete mode 100644 net/frr/src/opnsense/mvc/app/views/OPNsense/Quagga/diagnosticsbgp.volt delete mode 100644 net/frr/src/opnsense/mvc/app/views/OPNsense/Quagga/diagnosticsgeneral.volt delete mode 100644 net/frr/src/opnsense/mvc/app/views/OPNsense/Quagga/diagnosticsospf.volt create mode 100644 net/frr/src/opnsense/www/js/quagga/diagnostics_utils.js diff --git a/net/frr/src/opnsense/mvc/app/controllers/OPNsense/Quagga/Api/DiagnosticsController.php b/net/frr/src/opnsense/mvc/app/controllers/OPNsense/Quagga/Api/DiagnosticsController.php index a92a3d8e0..76f0cf6a2 100644 --- a/net/frr/src/opnsense/mvc/app/controllers/OPNsense/Quagga/Api/DiagnosticsController.php +++ b/net/frr/src/opnsense/mvc/app/controllers/OPNsense/Quagga/Api/DiagnosticsController.php @@ -41,11 +41,27 @@ use OPNsense\Core\Config; */ class DiagnosticsController extends ApiControllerBase { + private $allifnames = []; + + public function initialize() + { + parent::initialize(); + foreach (Config::getInstance()->object()->interfaces->children() as $key => $node) { + $this->allifnames[(string)$node->if] = !empty((string)$node->descr) ? (string)$node->descr : $key; + } + } + + public function getIfDesc($ifname) + { + return !empty($this->allifnames[$ifname]) ? $this->allifnames[$ifname] : ''; + } + private function getInformation(string $daemon, string $name, string $format): array { - $backend = new Backend(); - $response = $backend->configdRun("quagga diagnostics " . $daemon . "_" . $name . ($format === "json" ? "_json" : "")); - return array("response" => ($format === "json" ? json_decode($response) : $response)); + $response = (new Backend())->configdRun( + "quagga diagnostics " . $daemon . "_" . $name . ($format === "json" ? "_json" : "") + ); + return ["response" => ($format === "json" ? json_decode($response ?? '', true) : $response)]; } public function generalrunningconfigAction(): array @@ -53,6 +69,9 @@ class DiagnosticsController extends ApiControllerBase return $this->getInformation("general", "running-config", "plain"); } + /** + * XXX: unused, deprecate in next major? + */ public function generalrouteAction($format = "json"): array { $routes4 = $this->getInformation("general", "route4", $format)['response']; @@ -69,11 +88,45 @@ class DiagnosticsController extends ApiControllerBase return $this->getInformation("general", "route4", $format); } + public function searchGeneralroute4Action(): array + { + $records = []; + foreach($this->getInformation("general", "route4", "json")['response'] as $routes) { + foreach ($routes as $route) { + foreach ($route['nexthops'] as $nexthop) { + $nexthop = array_merge($route, $nexthop); + unset($nexthop['nexthops']); + $nexthop['via'] = !empty($nexthop['ip']) ? $nexthop['ip'] : 'Directly Attached'; + $nexthop['interfaceDescr'] = $this->getIfDesc($nexthop['interfaceName'] ?? ''); + $records[] = $nexthop; + } + } + } + return $this->searchRecordsetBase($records); + } + public function generalroute6Action($format = "json"): array { return $this->getInformation("general", "route6", $format); } + public function searchGeneralroute6Action(): array + { + $records = []; + foreach($this->getInformation("general", "route6", "json")['response'] as $routes) { + foreach ($routes as $route) { + foreach ($route['nexthops'] as $nexthop) { + $nexthop = array_merge($route, $nexthop); + unset($nexthop['nexthops']); + $nexthop['via'] = !empty($nexthop['ip']) ? $nexthop['ip'] : 'Directly Attached'; + $nexthop['interfaceDescr'] = $this->getIfDesc($nexthop['interfaceName'] ?? ''); + $records[] = $nexthop; + } + } + } + return $this->searchRecordsetBase($records); + } + public function bgprouteAction($format = "json"): array { return $this->getInformation("bgp", "route", $format); @@ -84,11 +137,83 @@ class DiagnosticsController extends ApiControllerBase return $this->getInformation("bgp", "route4", $format); } + public function searchBgproute4Action(): array + { + $records = []; + $payload = $this->getInformation("bgp", "route4", "json")['response']; + $baserecord = []; + foreach ($payload as $key => $value) { + if (!is_array($value)) { + $baserecord[$key] = $value; + } + } + if (!empty($payload['routes'])) { + foreach ($payload['routes'] as $routes) { + foreach ($routes as $route) { + foreach ($route['nexthops'] as $nexthop) { + $nexthop = array_merge($route, $nexthop); + unset($nexthop['nexthops']); + $nexthop['internal'] = !empty($nexthop['pathFrom']) && $nexthop['pathFrom'] == 'internal'; + $nexthop['path'] = !empty($nexthop['path']) ? $nexthop['path'] : 'Internal'; + $records[] = array_merge($baserecord, $nexthop); + } + } + } + } + $result = $this->searchRecordsetBase($records); + if (!empty($baserecord)) { + $result['subtitle'] = sprintf( + '%s : %s , %s : %s', + gettext('routerId'), + $baserecord['routerId'], + gettext('localAS'), + $baserecord['localAS'] + ); + } + return $result; + } + public function bgproute6Action($format = "json"): array { return $this->getInformation("bgp", "route6", $format); } + public function searchBgproute6Action(): array + { + $records = []; + $payload = $this->getInformation("bgp", "route6", "json")['response']; + $baserecord = []; + foreach ($payload as $key => $value) { + if (!is_array($value)) { + $baserecord[$key] = $value; + } + } + if (!empty($payload['routes'])) { + foreach ($payload['routes'] as $routes) { + foreach ($routes as $route) { + foreach ($route['nexthops'] as $nexthop) { + $nexthop = array_merge($route, $nexthop); + unset($nexthop['nexthops']); + $nexthop['internal'] = !empty($nexthop['pathFrom']) && $nexthop['pathFrom'] == 'internal'; + $nexthop['path'] = !empty($nexthop['path']) ? $nexthop['path'] : 'Internal'; + $records[] = array_merge($baserecord, $nexthop); + } + } + } + } + $result = $this->searchRecordsetBase($records); + if (!empty($baserecord)) { + $result['subtitle'] = sprintf( + '%s : %s , %s : %s', + gettext('routerId'), + $baserecord['routerId'], + gettext('localAS'), + $baserecord['localAS'] + ); + } + return $result; + } + public function bgpsummaryAction($format = "json"): array { return $this->getInformation("bgp", "summary", $format); @@ -109,11 +234,50 @@ class DiagnosticsController extends ApiControllerBase return $this->getInformation("ospf", "neighbor", $format); } + public function searchOspfneighborAction(): array + { + $records = []; + $payload = $this->getInformation("ospf", "neighbor", "json")['response']; + if (!empty($payload['neighbors'])) { + foreach ($payload['neighbors'] as $neighborid => $neighbor) { + foreach ($neighbor as $item) { + $item['neighborid'] = $neighborid; + $records[] = $item; + } + } + } + return $this->searchRecordsetBase($records); + } + public function ospfrouteAction($format = "json"): array { return $this->getInformation("ospf", "route", $format); } + public function searchOspfrouteAction(): array + { + $records = []; + $payload = $this->getInformation("ospf", "route", "json")['response']; + foreach($payload as $net => $network) { + if (empty($network['nexthops'])) { + continue; + } + foreach ($network['nexthops'] as $nexthop) { + $records[] = [ + 'type' => $network['routeType'], + 'network' => $net, + 'cost' => $network['cost'], + 'area' => $network['area'] ?? '', + 'via' => !empty($nexthop['via']) ? $nexthop['ip'] : 'Directly Attached', + 'viainterface' => !empty($nexthop['via']) ? $nexthop['via'] : $nexthop['directly attached to'], + 'viainterfaceDescr' => $this->getIfDesc($nexthop['via'] ?? $nexthop['directly attached to']), + ]; + } + } + + return $this->searchRecordsetBase($records); + } + public function ospfdatabaseAction($format = "json"): array { return $this->getInformation("ospf", "database", $format); diff --git a/net/frr/src/opnsense/mvc/app/controllers/OPNsense/Quagga/DiagnosticsController.php b/net/frr/src/opnsense/mvc/app/controllers/OPNsense/Quagga/DiagnosticsController.php index 4b7431184..923e0d3ed 100644 --- a/net/frr/src/opnsense/mvc/app/controllers/OPNsense/Quagga/DiagnosticsController.php +++ b/net/frr/src/opnsense/mvc/app/controllers/OPNsense/Quagga/DiagnosticsController.php @@ -29,12 +29,71 @@ class DiagnosticsController extends \OPNsense\Base\IndexController { public function bgpAction() { - $this->view->diagnosticsForm = $this->getForm("diagnostics"); - $this->view->pick('OPNsense/Quagga/diagnosticsbgp'); + $this->view->tabs = [ + [ + 'name' => 'routing4', + 'endpoint' => '/api/quagga/diagnostics/search_bgproute4', + 'tabhead' => "IPv4 " . gettext('Routing Table'), + 'type' => 'bgproutetable' + ], + [ + 'name' => 'routing6', + 'endpoint' => '/api/quagga/diagnostics/search_bgproute6', + 'tabhead' => "IPv6 " . gettext('Routing Table'), + 'type' => 'bgproutetable' + ], + [ + 'name' => 'neighbors', + 'endpoint' => '/api/quagga/diagnostics/bgpneighbors', + 'tabhead' => gettext('Neighbors'), + 'type' => 'tree' + ], + [ + 'name' => 'summary', + 'endpoint' => '/api/quagga/diagnostics/bgpsummary', + 'tabhead' => gettext('Summary'), + 'type' => 'tree' + ] + ]; + $this->view->default_tab = 'routing4'; + $this->view->pick('OPNsense/Quagga/diagnostics'); } public function ospfAction() { - $this->view->pick('OPNsense/Quagga/diagnosticsospf'); + $this->view->tabs = [ + [ + 'name' => 'overview', + 'endpoint' => '/api/quagga/diagnostics/ospfoverview', + 'tabhead' => gettext('Overview'), + 'type' => 'tree' + ], + [ + 'name' => 'routing', + 'endpoint' => '/api/quagga/diagnostics/search_ospfroute', + 'tabhead' => gettext('Routing Table'), + 'type' => 'ospfroutetable' + ], + [ + 'name' => 'database', + 'endpoint' => '/api/quagga/diagnostics/ospfdatabase/plain', + 'tabhead' => gettext('Database'), + 'type' => 'text' + ], + [ + 'name' => 'neighbors', + 'endpoint' => '/api/quagga/diagnostics/search_ospfneighbor', + 'tabhead' => gettext('Neighbors'), + 'type' => 'ospfneighbors' + ], + [ + 'name' => 'interfaces', + 'endpoint' => '/api/quagga/diagnostics/ospfinterface', + 'tabhead' => gettext('Interfaces'), + 'type' => 'tree' + ] + ]; + $this->view->default_tab = 'routing'; + $this->view->pick('OPNsense/Quagga/diagnostics'); } public function ospfv3Action() { @@ -42,6 +101,27 @@ class DiagnosticsController extends \OPNsense\Base\IndexController } public function generalAction() { - $this->view->pick('OPNsense/Quagga/diagnosticsgeneral'); + $this->view->tabs = [ + [ + 'name' => 'routing4', + 'endpoint' => '/api/quagga/diagnostics/search_generalroute4', + 'tabhead' => gettext('IPv4 Routes'), + 'type' => 'generalroutetable' + ], + [ + 'name' => 'routing6', + 'endpoint' => '/api/quagga/diagnostics/search_generalroute6', + 'tabhead' => gettext('IPv6 Routes'), + 'type' => 'generalroutetable' + ], + [ + 'name' => 'runningconfig', + 'endpoint' => '/api/quagga/diagnostics/generalrunningconfig/plain', + 'tabhead' => gettext('Running Configuration'), + 'type' => 'text' + ] + ]; + $this->view->default_tab = 'routing4'; + $this->view->pick('OPNsense/Quagga/diagnostics'); } } diff --git a/net/frr/src/opnsense/mvc/app/controllers/OPNsense/Quagga/forms/diagnostics.xml b/net/frr/src/opnsense/mvc/app/controllers/OPNsense/Quagga/forms/diagnostics.xml deleted file mode 100644 index 0c0fff293..000000000 --- a/net/frr/src/opnsense/mvc/app/controllers/OPNsense/Quagga/forms/diagnostics.xml +++ /dev/null @@ -1,8 +0,0 @@ -
- - diagnostics.bgpneighbor - - text - One of the neighbor IPs - -
diff --git a/net/frr/src/opnsense/mvc/app/views/OPNsense/Quagga/diagnostics.volt b/net/frr/src/opnsense/mvc/app/views/OPNsense/Quagga/diagnostics.volt new file mode 100644 index 000000000..247317359 --- /dev/null +++ b/net/frr/src/opnsense/mvc/app/views/OPNsense/Quagga/diagnostics.volt @@ -0,0 +1,341 @@ +{# + +OPNsense® is Copyright © 2014 – 2023 by Deciso B.V. +Copyright (C) 2023 Marc Bartelt +Copyright (C) 2017 Fabian Franz +Copyright (C) 2017 Michael Muenz +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, +OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +#} + + + + + + + + + + + + +
+ {% for tab in tabs %} +
+ {% switch tab['type'] %} + {% case 'generalroutetable' %} +
+ + + + + + + + + + + + + + + + + +
{{ lang._('Code') }}{{ lang._('Selected') }}{{ lang._('Installed') }}{{ lang._('Network') }}{{ lang._('Administrative Distance') }}{{ lang._('Metric') }}{{ lang._('Interface') }}{{ lang._('Interface name') }}{{ lang._('Via') }}{{ lang._('Time') }}
+
+ {% break %} + {% case 'bgproutetable' %} +
+ + + + + + + + + + + + + + + + + +
{{ lang._('Valid') }}{{ lang._('Best') }}{{ lang._('Internal') }}{{ lang._('Network') }}{{ lang._('Next Hop') }}{{ lang._('Metric') }}{{ lang._('LocPrf') }}{{ lang._('Weight') }}{{ lang._('Path') }}{{ lang._('Origin') }}
+
+ {% break %} + {% case 'ospfroutetable' %} +
+ + + + + + + + + + + + + + +
{{ lang._('Type') }}{{ lang._('Network') }}{{ lang._('Cost') }}{{ lang._('Area') }}{{ lang._('Via') }}{{ lang._('Via interface') }}{{ lang._('Via interface name') }}
+
+ {% break %} + {% case 'ospfneighbors' %} +
+ + + + + + + + + + + + + + + + +
{{ lang._('Neighbor ID') }}{{ lang._('Priority') }}{{ lang._('State') }}{{ lang._('Dead Time') }} [ms]{{ lang._('Address') }}{{ lang._('Interface') }}{{ lang._('Retransmit Counter') }}{{ lang._('Request Counter') }}{{ lang._('DB Summary Counter') }}
+
+ {% break %} + {% case 'tree' %} + +
+ {% break %} + {% case 'text' %} +

+                    {% break %}
+            {% endswitch %}
+        
+ {% endfor %} +
diff --git a/net/frr/src/opnsense/mvc/app/views/OPNsense/Quagga/diagnosticsbgp.volt b/net/frr/src/opnsense/mvc/app/views/OPNsense/Quagga/diagnosticsbgp.volt deleted file mode 100644 index b0e196ce0..000000000 --- a/net/frr/src/opnsense/mvc/app/views/OPNsense/Quagga/diagnosticsbgp.volt +++ /dev/null @@ -1,143 +0,0 @@ -{# - -OPNsense® is Copyright © 2014 – 2017 by Deciso B.V. -Copyright (C) 2017 Fabian Franz -Copyright (C) 2017 Michael Muenz -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, -INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY -AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, -OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. - -#} - -{# -{{ partial("layout_partials/base_form",['fields':diagnosticsForm,'id':'frm_diagnostics_settings'])}} -#} - - - - - - - - - -
-
-
-
-

-  
-
-

-  
-
diff --git a/net/frr/src/opnsense/mvc/app/views/OPNsense/Quagga/diagnosticsgeneral.volt b/net/frr/src/opnsense/mvc/app/views/OPNsense/Quagga/diagnosticsgeneral.volt deleted file mode 100644 index 16c147019..000000000 --- a/net/frr/src/opnsense/mvc/app/views/OPNsense/Quagga/diagnosticsgeneral.volt +++ /dev/null @@ -1,142 +0,0 @@ -{# - -OPNsense® is Copyright © 2014 – 2017 by Deciso B.V. -Copyright (C) 2017 Fabian Franz -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, -INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY -AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, -OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. - -#} - - - - - - - - -
-
-
-
-

-  
-
diff --git a/net/frr/src/opnsense/mvc/app/views/OPNsense/Quagga/diagnosticsospf.volt b/net/frr/src/opnsense/mvc/app/views/OPNsense/Quagga/diagnosticsospf.volt deleted file mode 100644 index 1aa5e0857..000000000 --- a/net/frr/src/opnsense/mvc/app/views/OPNsense/Quagga/diagnosticsospf.volt +++ /dev/null @@ -1,475 +0,0 @@ -{# - -OPNsense® is Copyright © 2014 – 2017 by Deciso B.V. -Copyright (C) 2017 Fabian Franz -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, -INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY -AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, -OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. - -#} - - - - - - - - - - - - -
-
-
-
-
-
-
diff --git a/net/frr/src/opnsense/scripts/frr/legacy-diagnostics.py b/net/frr/src/opnsense/scripts/frr/legacy-diagnostics.py index 73cb70d5e..1567ace98 100755 --- a/net/frr/src/opnsense/scripts/frr/legacy-diagnostics.py +++ b/net/frr/src/opnsense/scripts/frr/legacy-diagnostics.py @@ -404,7 +404,8 @@ class OSPFv3(Daemon): self.myre.search(r'SPF algorithm last executed (.*)', line) or \ self.myre.search(r'Last SPF duration (.*)', line) or \ self.myre.search(r'SPF last executed (.*)', line) or \ - self.myre.search(r'Number of Area scoped LSAs is (.*)', line): + self.myre.search(r'Number of Area scoped LSAs is (.*)', line) or \ + self.myre.search(r'Adjacency changes are logged', line): # skip these lines pass else: diff --git a/net/frr/src/opnsense/www/js/quagga/diagnostics_utils.js b/net/frr/src/opnsense/www/js/quagga/diagnostics_utils.js new file mode 100644 index 000000000..3d69d2cd9 --- /dev/null +++ b/net/frr/src/opnsense/www/js/quagga/diagnostics_utils.js @@ -0,0 +1,207 @@ +/* + * Copyright (C) 2015-2022 Deciso B.V. + * Copyright (C) 2023 Marc Bartelt + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +'use strict'; + +/** + * shared options for bootgrid tables + */ +let gridopt = { + formatters: { + boolean: function(column, row) { + if (row[column.id]) { + return ''; + } else { + return ''; + } + }, + ospf_route_type: function(column, row) { + let result = ''; + + row[column.id].split(' ').forEach(function(routeType) { + let translatedRouteType = translateOSPFTerm(routeType) + + if (translatedRouteType === routeType) { + result += routeType; + } else { + result += '' + routeType + ''; + } + + result += ' '; + }); + + return result; + }, + general_route_code: function(column, row) { + let result = row.code; + let protocol = translateZebraCode(row.code); + + if(typeof(protocol) !== 'string') result = '' + protocol['short'] + ''; + if(row.selected) result += ' >'; + if(row.installed) result += ' *'; + + return result; + } + } +}; + +/** + * zebra route codes - translation table + */ +function translateZebraCode(data) { + let tr = []; + + // routing table tab + tr['kernel'] = {short: 'K', long: 'Kernel'}; + tr['connected'] = {short: 'C', long: 'Connected'}; + tr['bgp'] = {short: 'B', long: 'BGP'}; + tr['ospf'] = {short: 'O', long: 'OSPF'}; + tr['ospf6'] = {short: 'O', long: 'OSPFv3'}; + + return ((data in tr) ? tr[data] : data); +} + + + +/** + * OSPF terms and abbreviations - translation table + **/ +function translateOSPFTerm(data) { + let tr = []; + + // routing table tab + tr['N'] = 'Network'; + tr['R'] = 'Router'; + tr['IA'] = 'OSPF inter area'; + tr['N1'] = 'OSPF NSSA external type 1'; + tr['N2'] = 'OSPF NSSA external type 2'; + tr['E1'] = 'OSPF external type 1'; + tr['E2'] = 'OSPF external type 2'; + + return ((data in tr) ? tr[data] : data); +} + + +/** + * tree view: resize widget on window resize + */ +function resizeTreeWidget() { + let new_height = $(".page-foot").offset().top - + ($(".page-content-head").offset().top + $(".page-content-head").height()) - 160; + $(".treewidget").height(new_height); + $(".treewidget").css('max-height', new_height + 'px'); +} + +/** + * tree view: delayed live-search + */ +let apply_tree_search_timer = null; +function treeSearchKeyUp() { + let sender = $(this); + + clearTimeout(apply_tree_search_timer); + apply_tree_search_timer = setTimeout(function() { + let searchTerm = sender.val().toLowerCase(); + let target = $("#"+sender.attr('for')); + let tree = target.tree("getTree"); + let selected = []; + if (tree !== null) { + tree.iterate((node) => { + let matched = false; + if (searchTerm !== "") { + matched = node.name.toLowerCase().includes(searchTerm); + if (!matched && typeof node.value === 'string') { + matched = node.value.toLowerCase().includes(searchTerm); + } + } + node["selected"] = matched; + + if (matched) { + selected.push(node); + if (node.isFolder()) { + node.is_open = true; + } + let parent = node.parent; + while (parent) { + parent.is_open = true; + parent = parent.parent; + } + } else if (node.isFolder()) { + node.is_open = false; + } + + return true; + }); + target.tree("refresh"); + if (selected.length > 0) { + target.tree('scrollToNode', selected[0]); + } + } + }, 500); +} + +/** + * jqtree expects a list + dict type structure, transform key value store into expected output + * https://mbraak.github.io/jqTree/#general + */ + function dict_to_tree(node, path) { + // some entries are lists, try use a name for the nodes in that case + let node_name_keys = ['name', 'interface-name']; + let result = []; + if ( path === undefined) { + path = ""; + } else { + path = path + "."; + } + for (let key in node) { + if (typeof node[key] === "function") { + continue; + } + let item_path = path + key; + if (node[key] instanceof Object) { + let node_name = key; + for (let idx=0; idx < node_name_keys.length; ++idx) { + if (/^(0|[1-9]\d*)$/.test(node_name) && node[key][node_name_keys[idx]] !== undefined) { + node_name = node[key][node_name_keys[idx]]; + break; + } + } + result.push({ + name: node_name, + id: item_path, + children: dict_to_tree(node[key], item_path) + }); + } else { + result.push({ + name: key, + value: node[key], + id: item_path + }); + } + } + return result; +}