mirror of
https://github.com/netbirdio/plugins.git
synced 2026-05-22 18:44:07 -07:00
net/frr - cleanup unused endpoints for 23.7 and align with FRR8 for OPNsense 23.7 (#3498)
This commit is contained in:
+63
-88
@@ -1,6 +1,7 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Copyright (C) 2023 Deciso B.V.
|
||||
* Copyright (C) 2017 Frank Wall
|
||||
* Copyright (C) 2017 Michael Muenz <m.muenz@gmail.com>
|
||||
*
|
||||
@@ -56,42 +57,21 @@ class DiagnosticsController extends ApiControllerBase
|
||||
return !empty($this->allifnames[$ifname]) ? $this->allifnames[$ifname] : '';
|
||||
}
|
||||
|
||||
private function getInformation(string $daemon, string $name, string $format): array
|
||||
private function configdJson($daemon, $name)
|
||||
{
|
||||
$response = (new Backend())->configdRun(
|
||||
"quagga diagnostics " . $daemon . "_" . $name . ($format === "json" ? "_json" : "")
|
||||
);
|
||||
return ["response" => ($format === "json" ? json_decode($response ?? '', true) : $response)];
|
||||
$response = (new Backend())->configdpRun('quagga', ['diagnostics', $daemon . "_" . $name, 'json']);
|
||||
return json_decode($response ?? '', true) ?? [];
|
||||
}
|
||||
|
||||
public function generalrunningconfigAction(): array
|
||||
{
|
||||
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'];
|
||||
$routes6 = $this->getInformation("general", "route6", $format)['response'];
|
||||
if ($format === "json") {
|
||||
return array("response" => array("ipv4" => $routes4, "ipv6" => $routes6));
|
||||
} else {
|
||||
return array("response" => $routes4 . $routes6);
|
||||
}
|
||||
}
|
||||
|
||||
public function generalroute4Action($format = "json"): array
|
||||
{
|
||||
return $this->getInformation("general", "route4", $format);
|
||||
return ["response" => (new Backend())->configdpRun('quagga diagnostics general_running-config')];
|
||||
}
|
||||
|
||||
public function searchGeneralroute4Action(): array
|
||||
{
|
||||
$records = [];
|
||||
foreach ($this->getInformation('general', 'route4', 'json')['response'] as $routes) {
|
||||
foreach ($this->configdJson('general', 'route4') as $routes) {
|
||||
foreach ($routes as $route) {
|
||||
foreach ($route['nexthops'] as $nexthop) {
|
||||
$nexthop = array_merge($route, $nexthop);
|
||||
@@ -105,15 +85,10 @@ class DiagnosticsController extends ApiControllerBase
|
||||
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 ($this->configdJson('general', 'route6') as $routes) {
|
||||
foreach ($routes as $route) {
|
||||
foreach ($route['nexthops'] as $nexthop) {
|
||||
$nexthop = array_merge($route, $nexthop);
|
||||
@@ -127,20 +102,10 @@ class DiagnosticsController extends ApiControllerBase
|
||||
return $this->searchRecordsetBase($records);
|
||||
}
|
||||
|
||||
public function bgprouteAction($format = "json"): array
|
||||
{
|
||||
return $this->getInformation("bgp", "route", $format);
|
||||
}
|
||||
|
||||
public function bgproute4Action($format = "json"): array
|
||||
{
|
||||
return $this->getInformation("bgp", "route4", $format);
|
||||
}
|
||||
|
||||
public function searchBgproute4Action(): array
|
||||
{
|
||||
$records = [];
|
||||
$payload = $this->getInformation("bgp", "route4", "json")['response'];
|
||||
$payload = $this->configdJson("bgp", "route4");
|
||||
$baserecord = [];
|
||||
foreach ($payload as $key => $value) {
|
||||
if (!is_array($value)) {
|
||||
@@ -173,15 +138,10 @@ class DiagnosticsController extends ApiControllerBase
|
||||
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'];
|
||||
$payload = $this->configdJson("bgp", "route6");
|
||||
$baserecord = [];
|
||||
foreach ($payload as $key => $value) {
|
||||
if (!is_array($value)) {
|
||||
@@ -214,30 +174,25 @@ class DiagnosticsController extends ApiControllerBase
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function bgpsummaryAction($format = "json"): array
|
||||
public function bgpsummaryAction(): array
|
||||
{
|
||||
return $this->getInformation("bgp", "summary", $format);
|
||||
return ['response' => $this->configdJson("bgp", "summary")];
|
||||
}
|
||||
|
||||
public function bgpneighborsAction($format = "json"): array
|
||||
public function bgpneighborsAction(): array
|
||||
{
|
||||
return $this->getInformation("bgp", "neighbors", $format);
|
||||
return ['response' => $this->configdJson("bgp", "neighbors")];
|
||||
}
|
||||
|
||||
public function ospfoverviewAction($format = "json"): array
|
||||
public function ospfoverviewAction(): array
|
||||
{
|
||||
return $this->getInformation("ospf", "overview", $format);
|
||||
}
|
||||
|
||||
public function ospfneighborAction($format = "json"): array
|
||||
{
|
||||
return $this->getInformation("ospf", "neighbor", $format);
|
||||
return ['response' => $this->configdJson("ospf", "overview")];
|
||||
}
|
||||
|
||||
public function searchOspfneighborAction(): array
|
||||
{
|
||||
$records = [];
|
||||
$payload = $this->getInformation("ospf", "neighbor", "json");
|
||||
$payload = $this->configdJson("ospf", "neighbor");
|
||||
if (!empty($payload['neighbors'])) {
|
||||
foreach ($payload['neighbors'] as $neighborid => $neighbor) {
|
||||
foreach ($neighbor as $item) {
|
||||
@@ -249,15 +204,10 @@ class DiagnosticsController extends ApiControllerBase
|
||||
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'];
|
||||
$payload = $this->configdJson('ospf', 'route');
|
||||
foreach ($payload as $net => $network) {
|
||||
if (empty($network['nexthops'])) {
|
||||
continue;
|
||||
@@ -269,8 +219,8 @@ class DiagnosticsController extends ApiControllerBase
|
||||
'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']),
|
||||
'viainterface' => !empty($nexthop['via']) ? $nexthop['via'] : $nexthop['directlyAttachedTo'],
|
||||
'viainterfaceDescr' => $this->getIfDesc($nexthop['via'] ?? $nexthop['directlyAttachedTo']),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -278,45 +228,69 @@ class DiagnosticsController extends ApiControllerBase
|
||||
return $this->searchRecordsetBase($records);
|
||||
}
|
||||
|
||||
public function ospfdatabaseAction($format = "json"): array
|
||||
public function ospfdatabaseAction(): array
|
||||
{
|
||||
return $this->getInformation("ospf", "database", $format);
|
||||
return ['response' => $this->configdJson("ospf", "database")];
|
||||
}
|
||||
|
||||
public function ospfinterfaceAction($format = "json"): array
|
||||
public function ospfinterfaceAction(): array
|
||||
{
|
||||
return $this->getInformation("ospf", "interface", $format);
|
||||
return ['response' => $this->configdJson("ospf", "interface")];
|
||||
}
|
||||
|
||||
public function ospfv3overviewAction($format = "json"): array
|
||||
public function ospfv3interfaceAction(): array
|
||||
{
|
||||
return $this->getInformation("ospfv3", "overview", $format);
|
||||
return ['response' => $this->configdJson("ospfv3", "interface")];
|
||||
}
|
||||
|
||||
public function ospfv3neighborAction($format = "json"): array
|
||||
public function ospfv3overviewAction(): array
|
||||
{
|
||||
return $this->getInformation("ospfv3", "neighbor", $format);
|
||||
return ['response' => $this->configdJson("ospfv3", "overview")];
|
||||
}
|
||||
|
||||
public function ospfv3routeAction($format = "json"): array
|
||||
public function searchOspfv3routeAction($format = "json"): array
|
||||
{
|
||||
return $this->getInformation("ospfv3", "route", $format);
|
||||
$records = [];
|
||||
$payload = $this->configdJson("ospfv3", "route");
|
||||
if (!empty($payload['routes'])) {
|
||||
foreach ($payload['routes'] as $net => $route) {
|
||||
if (!empty($route['nextHops'])) {
|
||||
foreach ($route['nextHops'] as $nexthop) {
|
||||
$record = array_merge($route, $nexthop);
|
||||
$record['network'] = $net;
|
||||
$record['interfaceDescr'] = $this->getIfDesc($record['interfaceName']);
|
||||
$records[] = $record;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $this->searchRecordsetBase($records);
|
||||
}
|
||||
|
||||
public function ospfv3databaseAction($format = "json"): array
|
||||
public function searchOspfv3databaseAction(): array
|
||||
{
|
||||
return $this->getInformation("ospfv3", "database", $format);
|
||||
}
|
||||
|
||||
public function ospfv3interfaceAction($format = "json"): array
|
||||
{
|
||||
return $this->getInformation("ospfv3", "interface", $format);
|
||||
$records = [];
|
||||
$payload = $this->configdJson("ospfv3", "database") ;
|
||||
foreach ($payload as $dbname => $database) {
|
||||
foreach ($database as $topic) {
|
||||
if (!empty($topic['lsa'])) {
|
||||
foreach ($topic['lsa'] as $record) {
|
||||
$record['dbname'] = $dbname;
|
||||
$record['interface'] = $topic['interface'] ?? '';
|
||||
$record['interfaceDescr'] = $this->getIfDesc($topic['interface'] ?? null);
|
||||
$record['areaId'] = $topic['areaId'] ?? '';
|
||||
$records[] = $record;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $this->searchRecordsetBase($records);
|
||||
}
|
||||
|
||||
private function bfdTreeFetch($topic)
|
||||
{
|
||||
$records = [];
|
||||
$payload = $this->getInformation("bfd", $topic, "json")['response'];
|
||||
$payload = $this->configdJson("bfd", $topic);
|
||||
if (!empty($payload)) {
|
||||
foreach ($payload as $peer) {
|
||||
$peerid = $peer['peer'];
|
||||
@@ -329,7 +303,8 @@ class DiagnosticsController extends ApiControllerBase
|
||||
public function bfdsummaryAction(): array
|
||||
{
|
||||
$records = [];
|
||||
foreach (explode("\n", $this->getInformation("bfd", "summary", "plain")['response']) as $line) {
|
||||
$payload = (new Backend())->configdpRun('quagga diagnostics bfd_summary');
|
||||
foreach (explode("\n", $payload) as $line) {
|
||||
$parts = preg_split('/\s+/', trim($line));
|
||||
if (count($parts) == 4 && filter_var($parts[0], FILTER_VALIDATE_INT) !== false) {
|
||||
$records[] = [
|
||||
|
||||
@@ -75,9 +75,9 @@ class DiagnosticsController extends \OPNsense\Base\IndexController
|
||||
],
|
||||
[
|
||||
'name' => 'database',
|
||||
'endpoint' => '/api/quagga/diagnostics/ospfdatabase/plain',
|
||||
'endpoint' => '/api/quagga/diagnostics/ospfdatabase',
|
||||
'tabhead' => gettext('Database'),
|
||||
'type' => 'text'
|
||||
'type' => 'tree'
|
||||
],
|
||||
[
|
||||
'name' => 'neighbors',
|
||||
@@ -116,13 +116,41 @@ class DiagnosticsController extends \OPNsense\Base\IndexController
|
||||
'tabhead' => gettext('Counters'),
|
||||
'type' => 'tree'
|
||||
]
|
||||
];
|
||||
];
|
||||
$this->view->default_tab = 'summary';
|
||||
$this->view->pick('OPNsense/Quagga/diagnostics');
|
||||
}
|
||||
public function ospfv3Action()
|
||||
{
|
||||
$this->view->pick('OPNsense/Quagga/diagnosticsospfv3');
|
||||
$this->view->tabs = [
|
||||
[
|
||||
'name' => 'overview',
|
||||
'endpoint' => '/api/quagga/diagnostics/ospfv3overview',
|
||||
'tabhead' => gettext('Overview'),
|
||||
'type' => 'tree'
|
||||
|
||||
],
|
||||
[
|
||||
'name' => 'routing',
|
||||
'endpoint' => '/api/quagga/diagnostics/search_ospfv3route',
|
||||
'tabhead' => gettext('Routing'),
|
||||
'type' => 'ospfv3routetable'
|
||||
],
|
||||
[
|
||||
'name' => 'database',
|
||||
'endpoint' => '/api/quagga/diagnostics/search_ospfv3database',
|
||||
'tabhead' => gettext('Database'),
|
||||
'type' => 'ospfv3databasetable'
|
||||
],
|
||||
[
|
||||
'name' => 'interface',
|
||||
'endpoint' => '/api/quagga/diagnostics/ospfv3interface',
|
||||
'tabhead' => gettext('Interface'),
|
||||
'type' => 'tree'
|
||||
]
|
||||
];
|
||||
$this->view->default_tab = 'overview';
|
||||
$this->view->pick('OPNsense/Quagga/diagnostics');
|
||||
}
|
||||
public function generalAction()
|
||||
{
|
||||
|
||||
@@ -75,7 +75,6 @@ POSSIBILITY OF SUCH DAMAGE.
|
||||
$(".tree_search").keyup(treeSearchKeyUp);
|
||||
|
||||
let all_grids = [];
|
||||
|
||||
{% for tab in tabs %}
|
||||
/**
|
||||
* register refresh event handler for {{ tab['tabhead'] }}
|
||||
@@ -85,13 +84,15 @@ POSSIBILITY OF SUCH DAMAGE.
|
||||
{% case 'generalroutetable' %}
|
||||
{% case 'bgproutetable' %}
|
||||
{% case 'ospfroutetable' %}
|
||||
{% case 'ospfv3routetable' %}
|
||||
{% case 'ospfv3databasetable' %}
|
||||
{% case 'ospfneighbors' %}
|
||||
{% case 'bfdsummary' %}
|
||||
if (all_grids["{{ tab['name'] }}"] === undefined) {
|
||||
/**
|
||||
* initialize bootgrid table for {{ tab['tabhead'] }}
|
||||
*/
|
||||
gridopt = {
|
||||
let gridopt = {
|
||||
search: "{{ tab['endpoint'] }}",
|
||||
options:{
|
||||
formatters : {
|
||||
@@ -116,6 +117,61 @@ POSSIBILITY OF SUCH DAMAGE.
|
||||
}
|
||||
return field.html();
|
||||
},
|
||||
ospf_route_type: function(column, row) {
|
||||
let ospf_route_types = {
|
||||
'N': "{{ lang._('Network') }}",
|
||||
'IA': "{{ lang._('OSPF inter area') }}",
|
||||
'N1': "{{ lang._('OSPF NSSA external type 1') }}",
|
||||
'N2': "{{ lang._('OSPF NSSA external type 2') }}",
|
||||
'E1': "{{ lang._('OSPF external type 1') }}",
|
||||
'E2': "{{ lang._('OSPF external type 2') }}",
|
||||
};
|
||||
let field = $("<div/>");
|
||||
row[column.id].split(' ').forEach(function(routeType) {
|
||||
if (ospf_route_types[routeType] !== undefined) {
|
||||
field.append($("<abbr>").text(routeType).attr('title', ospf_route_types[routeType]));
|
||||
} else {
|
||||
field.append($("<abbr>").text(routeType));
|
||||
}
|
||||
field.append(' ');
|
||||
});
|
||||
|
||||
return field.html();
|
||||
},
|
||||
ospf6_dest_type: function(column, row){
|
||||
let ospf6_dest_types = {
|
||||
'?': "{{ lang._('Unknown') }}",
|
||||
'R': "{{ lang._('Router') }}",
|
||||
'N': "{{ lang._('Network') }}",
|
||||
'D': "{{ lang._('Discard') }}",
|
||||
'L': "{{ lang._('Linkstate') }}",
|
||||
'A': "{{ lang._('AddressRange') }}",
|
||||
};
|
||||
let field = $("<div/>");
|
||||
if (ospf6_dest_types[row[column.id]] !== undefined) {
|
||||
field.append($("<abbr>").text(row[column.id]).attr('title', ospf6_dest_types[row[column.id]]));
|
||||
} else {
|
||||
field.append($("<abbr>").text(row[column.id]));
|
||||
}
|
||||
|
||||
return field.html();
|
||||
},
|
||||
ospf6_path_type: function(column, row){
|
||||
let ospf6_path_types = {
|
||||
'??': "{{ lang._('UnknownRoute') }}",
|
||||
'IA': "{{ lang._('IntraArea') }}",
|
||||
'IE': "{{ lang._('InterArea') }}",
|
||||
'E1': "{{ lang._('External1') }}",
|
||||
'E2': "{{ lang._('External2') }}",
|
||||
};
|
||||
let field = $("<div/>");
|
||||
if (ospf6_path_types[row[column.id]] !== undefined) {
|
||||
field.append($("<abbr>").text(row[column.id]).attr('title', ospf6_path_types[row[column.id]]));
|
||||
} else {
|
||||
field.append($("<abbr>").text(row[column.id]));
|
||||
}
|
||||
return field.html();
|
||||
},
|
||||
boolean: function (column, row) {
|
||||
if (row[column.id]) {
|
||||
return "<span class=\"fa fa-check\" data-value=\"1\" data-row-id=\"" + row.uuid + "\"></span>";
|
||||
@@ -133,7 +189,6 @@ POSSIBILITY OF SUCH DAMAGE.
|
||||
$("#page_frr_subtitle").append('<i class="fa fa-fw fa-chevron-right" aria-hidden="true"></i>');
|
||||
$("#page_frr_subtitle").append($("<span/>").text(data.subtitle));
|
||||
}
|
||||
console.log(data);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
@@ -300,6 +355,26 @@ POSSIBILITY OF SUCH DAMAGE.
|
||||
</table>
|
||||
</div>
|
||||
{% break %}
|
||||
{% case 'ospfv3routetable' %}
|
||||
<div class="col-sm-12">
|
||||
<table id="grid-{{ tab['name'] }}" class="table table-condensed table-hover table-striped table-responsive">
|
||||
<thead>
|
||||
<tr>
|
||||
<th data-column-id="destinationType" data-width="5em" data-formatter="ospf6_dest_type" data-sortable="false">{{ lang._('Type') }}</th>
|
||||
<th data-column-id="isBestRoute" data-width="5em" data-formatter="boolean" data-searchable="false" data-sortable="false">{{ lang._('Best') }}</th>
|
||||
<th data-column-id="pathType" data-formatter="ospf6_path_type">{{ lang._('Path type') }}</th>
|
||||
<th data-column-id="network">{{ lang._('Network') }}</th>
|
||||
<th data-column-id="nextHop" data-type="string">{{ lang._('nextHop') }}</th>
|
||||
<th data-column-id="interfaceName" data-type="string">{{ lang._('Interface') }}</th>
|
||||
<th data-column-id="interfaceDescr">{{ lang._('interface name') }}</th>
|
||||
<th data-column-id="duration" data-type="string">{{ lang._('Time') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% break %}
|
||||
{% case 'ospfneighbors' %}
|
||||
<div class="col-sm-12">
|
||||
<table id="grid-{{ tab['name'] }}" class="table table-condensed table-hover table-striped table-responsive">
|
||||
@@ -321,6 +396,28 @@ POSSIBILITY OF SUCH DAMAGE.
|
||||
</table>
|
||||
</div>
|
||||
{% break %}
|
||||
{% case 'ospfv3databasetable' %}
|
||||
<div class="col-sm-12">
|
||||
<table id="grid-{{ tab['name'] }}" class="table table-condensed table-hover table-striped table-responsive">
|
||||
<thead>
|
||||
<tr>
|
||||
<th data-column-id="dbname">{{ lang._('Database') }}</th>
|
||||
<th data-column-id="areaId">{{ lang._('Area') }}</th>
|
||||
<th data-column-id="interface">{{ lang._('Interface') }}</th>
|
||||
<th data-column-id="interfaceDescr">{{ lang._('interface name') }}</th>
|
||||
<th data-column-id="type">{{ lang._('Type') }}</th>
|
||||
<th data-column-id="lsId">{{ lang._('LS ID') }}</th>
|
||||
<th data-column-id="advRouter">{{ lang._('Advertising Router') }}</th>
|
||||
<th data-column-id="age">{{ lang._('Age') }}</th>
|
||||
<th data-column-id="seqNum">{{ lang._('Sequence Number') }}</th>
|
||||
<th data-column-id="payload">{{ lang._('Payload') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% break %}
|
||||
{% case 'bfdsummary' %}
|
||||
<div class="col-sm-12">
|
||||
<table id="grid-{{ tab['name'] }}" class="table table-condensed table-hover table-striped table-responsive">
|
||||
|
||||
@@ -1,382 +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.
|
||||
|
||||
#}
|
||||
|
||||
<script type="text/x-template" id="overviewtpl">
|
||||
<h2>{{ lang._('General') }}</h2>
|
||||
<table class="table table-striped">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>{{ lang._('Router ID') }}</td>
|
||||
<td><%= ospfv3_overview['router_id'] %></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ lang._('Routing Process') }}</td>
|
||||
<td><%= ospfv3_overview['routing_process'] %></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ lang._('Running Time') }}</td>
|
||||
<td><%= ospfv3_overview['running_time'] %></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ lang._('Initial SPF scheduling delay') }}</td>
|
||||
<td><%= ospfv3_overview['intital_spf_scheduling_delay'] %></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ lang._('Hold Time') }}</td>
|
||||
<td>
|
||||
{{ lang._('Minimum Hold Time') }} <%= ospfv3_overview['hold_time']['min'] %><br/>
|
||||
{{ lang._('Maximum Hold Time:') }} <%= ospfv3_overview['hold_time']['max'] %>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ lang._('SPF timer') }}</td>
|
||||
<td><%= ospfv3_overview['spf_timer'] %></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ lang._('Number Of Scoped AS') }}</td>
|
||||
<td><%= ospfv3_overview['number_as_scoped'] %></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ lang._('Number Of Areas') }}</td>
|
||||
<td><%= ospfv3_overview['number_of_areas'] %></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h2>{{ lang._('Areas') }}</h2>
|
||||
<% if (ospfv3_overview['areas']) { %>
|
||||
<% areas = ospfv3_overview['areas'] %>
|
||||
<% _.each(_.keys(areas), function(areaname) { %>
|
||||
<% area = areas[areaname] %>
|
||||
<h3><%= areaname %></h3>
|
||||
<table class="table table-striped">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>{{ lang._('Number Of LSAs') }}</td>
|
||||
<td><%= area['number_lsas'] %></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ lang._('Interfaces') }}</td>
|
||||
<td><%= _.join(area['interfaces'], ", ") %></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<% }) %>
|
||||
<% } %>
|
||||
</script>
|
||||
|
||||
<script type="text/x-template" id="routestpl">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th data-column-id="flags1" data-type="string">{{ lang._('Flags 1') }}</th>
|
||||
<th data-column-id="flags2" data-type="string">{{ lang._('Flags 2') }}</th>
|
||||
<th data-column-id="network" data-type="string">{{ lang._('Network') }}</th>
|
||||
<th data-column-id="gateway" data-type="string">{{ lang._('Gateway') }}</th>
|
||||
<th data-column-id="interface" data-type="string">{{ lang._('Interface') }}</th>
|
||||
<th data-column-id="time" data-type="string">{{ lang._('Time') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<% _.each(ospfv3_route, function (route) { %>
|
||||
<tr>
|
||||
<td><%= route['f1'] %></td>
|
||||
<td><%= route['f2'] %></td>
|
||||
<td><%= route['network'] %></td>
|
||||
<td><%= route['gateway'] %></td>
|
||||
<td><%= route['interface'] %></td>
|
||||
<td><%= route['time'] %></td>
|
||||
</tr>
|
||||
<% }) %>
|
||||
</tbody>
|
||||
</table>
|
||||
</script>
|
||||
|
||||
<script type="text/x-template" id="databasetpl">
|
||||
<% if(ospfv3_database['scoped_link_db']) { %>
|
||||
<h2>{{ lang._('Scoped Link Database') }}</h2>
|
||||
<% _.each(_.keys(ospfv3_database['scoped_link_db']), function(areaname) { %>
|
||||
<% area = ospfv3_database['scoped_link_db'][areaname] %>
|
||||
<h3><% areaname %></h3>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th data-column-id="type" data-type="string">{{ lang._('Type') }}</th>
|
||||
<th data-column-id="lsid" data-type="string">{{ lang._('LS ID') }}</th>
|
||||
<th data-column-id="advrouter" data-type="string">{{ lang._('Advertising Router') }}</th>
|
||||
<th data-column-id="age" data-type="numeric">{{ lang._('Age') }}</th>
|
||||
<th data-column-id="seqnr" data-type="string">{{ lang._('Sequence Number') }}</th>
|
||||
<th data-column-id="payload" data-type="string">{{ lang._('Payload') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<% _.each(area, function(entry) { %>
|
||||
<tr>
|
||||
<td><%= entry['Type'] %></td>
|
||||
<td><%= entry['LSId'] %></td>
|
||||
<td><%= entry['AdvRouter'] %></td>
|
||||
<td><%= entry['Age'] %></td>
|
||||
<td><%= entry['SeqNum'] %></td>
|
||||
<td><%= entry['Payload'] %></td>
|
||||
</tr>
|
||||
<% }) %>
|
||||
</tbody>
|
||||
</table>
|
||||
<% }) %>
|
||||
<% } %>
|
||||
<% if(ospfv3_database['if_scoped_link_state']) { %>
|
||||
<h2>{{ lang._('Interface Scoped Link Database') }}</h2>
|
||||
<% _.each(_.keys(ospfv3_database['if_scoped_link_state']), function(intfname) { %>
|
||||
<% intf = ospfv3_database['if_scoped_link_state'][intfname] %>
|
||||
<% _.each(_.keys(intf), function(areaname) { %>
|
||||
<% area = intf[areaname] %>
|
||||
<h3><%= intfname %> / <%= areaname %></h3>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th data-column-id="type" data-type="string">{{ lang._('Type') }}</th>
|
||||
<th data-column-id="lsid" data-type="string">{{ lang._('LS ID') }}</th>
|
||||
<th data-column-id="advrouter" data-type="string">{{ lang._('Advertising Router') }}</th>
|
||||
<th data-column-id="age" data-type="numeric">{{ lang._('Age') }}</th>
|
||||
<th data-column-id="seqnr" data-type="string">{{ lang._('Sequence Number') }}</th>
|
||||
<th data-column-id="payload" data-type="string">{{ lang._('Payload') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<% _.each(area, function(entry) { %>
|
||||
<tr>
|
||||
<td><%= entry['Type'] %></td>
|
||||
<td><%= entry['LSId'] %></td>
|
||||
<td><%= entry['AdvRouter'] %></td>
|
||||
<td><%= entry['Age'] %></td>
|
||||
<td><%= entry['SeqNum'] %></td>
|
||||
<td><%= entry['Payload'] %></td>
|
||||
</tr>
|
||||
<% }) %>
|
||||
</tbody>
|
||||
</table>
|
||||
<% }) %>
|
||||
<% }) %>
|
||||
<% } %>
|
||||
<% if (ospfv3_database['as_scoped']) { %>
|
||||
<h2>{{ lang._('AS Scoped') }}</h2>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th data-column-id="type" data-type="string">{{ lang._('Type') }}</th>
|
||||
<th data-column-id="lsid" data-type="string">{{ lang._('LS ID') }}</th>
|
||||
<th data-column-id="advrouter" data-type="string">{{ lang._('Advertising Router') }}</th>
|
||||
<th data-column-id="age" data-type="numeric">{{ lang._('Age') }}</th>
|
||||
<th data-column-id="seqnr" data-type="string">{{ lang._('Sequence Number') }}</th>
|
||||
<th data-column-id="payload" data-type="string">{{ lang._('Payload') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<% _.each(ospfv3_database['as_scoped'], function(entry) { %>
|
||||
<tr>
|
||||
<td><%= entry['Type'] %></td>
|
||||
<td><%= entry['LSId'] %></td>
|
||||
<td><%= entry['AdvRouter'] %></td>
|
||||
<td><%= entry['Age'] %></td>
|
||||
<td><%= entry['SeqNum'] %></td>
|
||||
<td><%= entry['Payload'] %></td>
|
||||
</tr>
|
||||
<% }) %>
|
||||
</tbody>
|
||||
</table>
|
||||
<% } %>
|
||||
</script>
|
||||
|
||||
<script type="text/x-template" id="interfacetpl">
|
||||
<% _.each(_.keys(ospfv3_interface), function(interfacename) { %>
|
||||
<% int = ospfv3_interface[interfacename] %>
|
||||
<h2><%= interfacename %></h2>
|
||||
<table class="table table-striped">
|
||||
<tbody>
|
||||
<% _.each(_.keys(int), function(propertyname) { %>
|
||||
<% if (propertyname != 'pending_lsas' ) { %>
|
||||
<tr>
|
||||
<td><%= translate(propertyname) %></td>
|
||||
<td>
|
||||
<% if (int[propertyname] === false || int[propertyname] === true) { %>
|
||||
<%= checkmark(int[propertyname]) %>
|
||||
<% } else if (propertyname == 'timers') { %>
|
||||
{{ lang._('Hello Timer:') }} <%= int[propertyname]['hello'] %><br />
|
||||
{{ lang._('Dead Timer:') }} <%= int[propertyname]['dead'] %><br />
|
||||
{{ lang._('Wait Timer:') }} <%= int[propertyname]['wait'] %><br />
|
||||
{{ lang._('Retransmit Timer:') }} <%= int[propertyname]['retransmit'] %>
|
||||
<% } else if (propertyname == 'IPv6' || propertyname == 'IPv4') { %>
|
||||
<%= _.join(int[propertyname],'<br />') %><br />
|
||||
<% } else if (propertyname == 'area_cost') { %>
|
||||
<% _.each(int[propertyname], function (ac) { %>
|
||||
<%= ac['area'] %>: <%= ac['cost'] %><br />
|
||||
<% }) %>
|
||||
<% } else { %>
|
||||
<%= translate(int[propertyname]) %>
|
||||
<% } %>
|
||||
</td>
|
||||
</tr>
|
||||
<% } else { %>
|
||||
<% _.each(_.keys(int[propertyname]), function(lsa) { %>
|
||||
<% mylsa = int[propertyname][lsa] %>
|
||||
<tr>
|
||||
<td><%= lsa %> {{ lang._('Time') }}</td>
|
||||
<td><%= mylsa['time'] %> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><%= lsa %> {{ lang._('Count') }}</td>
|
||||
<td><%= mylsa['Count'] %> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><%= lsa %> {{ lang._('Flags') }}</td>
|
||||
<td><%= mylsa['flags'] %> </td>
|
||||
</tr>
|
||||
<% }) %>
|
||||
<% } %>
|
||||
<% }); %>
|
||||
</tbody>
|
||||
</table>
|
||||
<% }) %>
|
||||
</script>
|
||||
<script src="/ui/js/quagga/lodash.js"></script>
|
||||
<script>
|
||||
|
||||
function translate(data)
|
||||
{
|
||||
tr = []
|
||||
tr['count'] = '{{ lang._('Count') }}'
|
||||
tr['router'] = '{{ lang._('Router') }}'
|
||||
tr['network'] = '{{ lang._('Network') }}'
|
||||
tr['summary'] = '{{ lang._('Summary') }}'
|
||||
tr['ASBR summary'] = '{{ lang._('ASBR summary') }}'
|
||||
tr['NSSA'] = '{{ lang._('NSSA') }}'
|
||||
tr['directly attached'] = '{{ lang._('Directly Attached') }}'
|
||||
tr['Full/DR'] = '{{ lang._('Full (Designated Router)') }}'
|
||||
tr['enabled'] = '{{ lang._('Enabled') }}'
|
||||
tr['cost'] = '{{ lang._('Cost') }}'
|
||||
tr['priority'] = '{{ lang._('Priority') }}'
|
||||
tr['router_id'] = '{{ lang._('Router ID') }}'
|
||||
tr['network_type'] = '{{ lang._('Network Type') }}'
|
||||
tr['area'] = '{{ lang._('Area') }}'
|
||||
tr['transmit_delay'] = '{{ lang._('Transmit Delay') }}'
|
||||
tr['state'] = '{{ lang._('State') }}'
|
||||
tr['broadcast'] = '{{ lang._('Broadcast') }}'
|
||||
tr['mtu_mismatch_detection'] = '{{ lang._('MTU Mismatch Detection') }}'
|
||||
tr['address'] = '{{ lang._('Address') }}'
|
||||
tr['designated_router'] = '{{ lang._('Designated Router') }}'
|
||||
tr['designated_router_interface_address'] = '{{ lang._('Designated Router Interface Address') }}'
|
||||
tr['backup_designated_router'] = '{{ lang._('Backup Designated Router') }}'
|
||||
tr['multicast_group_memberships'] = '{{ lang._('Multicast Group Memberships') }}'
|
||||
tr['intervals'] = '{{ lang._('Intervals') }}'
|
||||
tr['hello_due_in'] = '{{ lang._('Hello Due In') }}'
|
||||
tr['neighbor_count'] = '{{ lang._('Neighbor Count') }}'
|
||||
tr['adjacent_neighbor_count'] = '{{ lang._('Adjacent Neighbor Count') }}'
|
||||
tr['timers'] = '{{ lang._('Timers') }}'
|
||||
tr['number_if_scoped_lsas'] = '{{ lang._('Number Of Interface Scoped LSAs') }}'
|
||||
tr['pending_lsas'] = '{{ lang._('Pending LSAs') }}'
|
||||
tr['area_cost'] = '{{ lang._('Area Cost') }}'
|
||||
tr['instance_id'] = '{{ lang._('Instance ID') }}'
|
||||
tr['interface_mtu'] = '{{ lang._('Interface MTU') }}'
|
||||
tr['type'] = '{{ lang._('Type') }}'
|
||||
tr['id'] = '{{ lang._('ID') }}'
|
||||
tr['up'] = '{{ lang._('Up') }}'
|
||||
tr['DR'] = '{{ lang._('Designated Router') }}'
|
||||
tr['BDR'] = '{{ lang._('Backup Designated Router') }}'
|
||||
tr['BROADCAST'] = '{{ lang._('Broadcast') }}'
|
||||
tr['UNKNOWN'] = '{{ lang._('Unknown') }}'
|
||||
tr['POINTOPOINT'] = '{{ lang._('Point to Point') }}'
|
||||
tr['LOOPBACK'] = '{{ lang._('Loopback') }}'
|
||||
return _.has(tr,data) ? tr[data] : data
|
||||
}
|
||||
|
||||
function checkmark(bin)
|
||||
{
|
||||
return "<i class=\"fa " + (bin ? "fa-check-square" : "fa-square") + " text-muted\"></i>";
|
||||
}
|
||||
|
||||
dataconverters = {
|
||||
boolean: {
|
||||
from: function (value) { return (value == 'true') || (value == true); },
|
||||
to: function (value) { return checkmark(value) }
|
||||
},
|
||||
raw: {
|
||||
from: function (value) { return value },
|
||||
to: function (value) { return value }
|
||||
}
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
updateServiceControlUI('quagga');
|
||||
|
||||
ajaxCall(url="/api/quagga/diagnostics/ospfv3overview", sendData={}, callback=function(data,status) {
|
||||
content = _.template($('#overviewtpl').html())(data['response'])
|
||||
$('#overview').html(content)
|
||||
});
|
||||
ajaxCall(url="/api/quagga/diagnostics/ospfv3database", sendData={}, callback=function(data,status) {
|
||||
content = _.template($('#databasetpl').html())(data['response'])
|
||||
$('#database').html(content)
|
||||
$('#database table').bootgrid()
|
||||
});
|
||||
ajaxCall(url="/api/quagga/diagnostics/ospfv3route", sendData={}, callback=function(data,status) {
|
||||
content = _.template($('#routestpl').html())(data['response'])
|
||||
$('#routing').html(content)
|
||||
$('#routing table').bootgrid()
|
||||
});
|
||||
/*ajaxCall(url="/api/quagga/diagnostics/ospfv3neighbor", sendData={}, callback=function(data,status) {
|
||||
content = _.template($('#neighbortpl').html())(data['response'])
|
||||
$('#neighbor').html(content)
|
||||
});*/
|
||||
ajaxCall(url="/api/quagga/diagnostics/ospfv3interface", sendData={}, callback=function(data,status) {
|
||||
content = _.template($('#interfacetpl').html())(data['response'])
|
||||
$('#interface').html(content)
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
</script>
|
||||
|
||||
<!-- Navigation bar -->
|
||||
<ul class="nav nav-tabs" data-tabs="tabs" id="maintabs">
|
||||
<li class="active"><a data-toggle="tab" href="#overview">{{ lang._('Overview') }}</a></li>
|
||||
<li><a data-toggle="tab" href="#routing">{{ lang._('Routing Table') }}</a></li>
|
||||
<li><a data-toggle="tab" href="#database">{{ lang._('Database') }}</a></li>
|
||||
<!--<li><a data-toggle="tab" href="#neighbor">{{ lang._('Neighbor') }}</a></li>-->
|
||||
<li><a data-toggle="tab" href="#interface">{{ lang._('Interface') }}</a></li>
|
||||
</ul>
|
||||
<div class="tab-content content-box tab-content">
|
||||
<div id="overview" class="tab-pane fade in active">
|
||||
</div>
|
||||
<div id="routing" class="tab-pane fade in">
|
||||
</div>
|
||||
<div id="database" class="tab-pane fade in">
|
||||
</div>
|
||||
<div id="neighbor" class="tab-pane fade in">
|
||||
</div>
|
||||
<div id="interface" class="tab-pane fade in">
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,446 +0,0 @@
|
||||
#!/usr/local/bin/python3
|
||||
"""
|
||||
Copyright (c) 2020 Marc Leuser
|
||||
Copyright (c) 2020 Ad Schellevis <ad@opnsense.org>
|
||||
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.
|
||||
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import ujson
|
||||
import re
|
||||
from typing import List, Dict
|
||||
from lib import VtySH
|
||||
|
||||
|
||||
class Re:
|
||||
""" Custom regex helper class (source https://stackoverflow.com/a/4980181)
|
||||
Use to conveniently build switch-case like constructs using if/elif
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.last_match = None
|
||||
|
||||
def match(self, pattern, text):
|
||||
self.last_match = re.match(pattern, text)
|
||||
return self.last_match
|
||||
|
||||
def search(self, pattern, text):
|
||||
self.last_match = re.search(pattern, text)
|
||||
return self.last_match
|
||||
|
||||
|
||||
class FRRTableReader:
|
||||
def __init__(self, titles: List[str] = None):
|
||||
self.titles = titles if titles is not None else list()
|
||||
self.columns = list()
|
||||
|
||||
def read_header(self, line: str, start_without_title: bool = False, start_without_title_name: str = 'status'):
|
||||
# we're going to create a list of columns. each entry contains title, start index and end index for easy parsing
|
||||
self.columns = []
|
||||
|
||||
# the first column's title may sometimes be empty in FRR's output. we'll have to give it a name though
|
||||
if start_without_title:
|
||||
self.columns.append({
|
||||
'title': start_without_title_name,
|
||||
'start_index': 0,
|
||||
'end_index': None
|
||||
})
|
||||
|
||||
# fill the list with subsequent columns
|
||||
for title in self.titles:
|
||||
try:
|
||||
# find the start index of the current title in the header
|
||||
start_index = line.index(title)
|
||||
except ValueError:
|
||||
# just skip the column if it can't be found
|
||||
continue
|
||||
|
||||
# last column's end index is this column's start index
|
||||
if self.columns:
|
||||
self.columns[-1]['end_index'] = start_index
|
||||
|
||||
# add the current column to the list
|
||||
self.columns.append({
|
||||
'title': title,
|
||||
'start_index': start_index,
|
||||
'end_index': None
|
||||
})
|
||||
|
||||
def read_line(self, line: str) -> Dict[str, str]:
|
||||
result = {}
|
||||
|
||||
# sanity check: the line has to be long enough to contain all columns,
|
||||
# so its length must be greater than the last column's start index
|
||||
if len(line) > self.columns[-1]['start_index']:
|
||||
for column in self.columns:
|
||||
# use the column's name as dict key and just extract the data from start to end index
|
||||
result[column['title'].strip()] = line[column['start_index']:column['end_index']].strip()
|
||||
|
||||
return result
|
||||
|
||||
|
||||
class DaemonError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class Daemon:
|
||||
def __init__(self, vtysh: VtySH):
|
||||
self.vtysh = vtysh
|
||||
self.myre = Re()
|
||||
|
||||
def _show(self, suffix: str):
|
||||
# execute the command and filter out empty lines so subsequent iterations don't have to deal with them
|
||||
return list(filter(None, self.vtysh.execute(command='show ' + suffix, translate=bytes.decode).split('\n')))
|
||||
|
||||
|
||||
class OSPF(Daemon):
|
||||
def _show(self, suffix: str):
|
||||
return super()._show('ip ospf ' + suffix)
|
||||
|
||||
def database(self):
|
||||
db = {}
|
||||
# table reader for Route Link States
|
||||
rltr = FRRTableReader(titles=['Link ID', 'ADV Router', 'Age', 'Seq#', 'CkSum', 'Link count'])
|
||||
# table reader for Net Link States
|
||||
nltr = FRRTableReader(titles=['Link ID', 'ADV Router', 'Age', 'Seq#', 'CkSum'])
|
||||
# table reader for Summary Link States
|
||||
sltr = FRRTableReader(titles=['Link ID', 'ADV Router', 'Age', 'Seq#', 'CkSum', 'Route\n'])
|
||||
# table reader for AS External Link States (columns are identical to Summary Link States)
|
||||
eltr = sltr
|
||||
|
||||
# get the FRR output
|
||||
lines = self._show('database')
|
||||
|
||||
# placeholder for the active table reader for the coming line
|
||||
tr = None
|
||||
# whether or not the header for the current section has already been parsed
|
||||
header_parsed = False
|
||||
# the current router
|
||||
router = None
|
||||
# the current area
|
||||
area = None
|
||||
# the current mode
|
||||
mode = None
|
||||
|
||||
for line in lines:
|
||||
if line.startswith(' '):
|
||||
# this is a heading
|
||||
heading = line.strip()
|
||||
header_parsed = False
|
||||
|
||||
# this is going to be dirty
|
||||
if self.myre.search(r'OSPF Router with ID \(([\.\d]+)\)', heading):
|
||||
router = self.myre.last_match.group(1)
|
||||
if router not in db:
|
||||
db[router] = {}
|
||||
mode = 'router'
|
||||
elif self.myre.search(r'Router Link States \(Area ([\.\d]+)\)', heading):
|
||||
mode = 'router_link_state_area'
|
||||
area = self.myre.last_match.group(1)
|
||||
if mode not in db[router]:
|
||||
db[router][mode] = {}
|
||||
if area not in db[router][mode]:
|
||||
db[router][mode][area] = []
|
||||
tr = rltr
|
||||
elif self.myre.search(r'Net Link States \(Area ([\.\d]+)\)', heading):
|
||||
mode = 'net_link_state_area'
|
||||
area = self.myre.last_match.group(1)
|
||||
if mode not in db[router]:
|
||||
db[router][mode] = {}
|
||||
if area not in db[router][mode]:
|
||||
db[router][mode][area] = []
|
||||
tr = nltr
|
||||
elif self.myre.search(r'Summary Link States \(Area ([\.\d]+)\)', heading):
|
||||
mode = 'summary_link_state_area'
|
||||
area = self.myre.last_match.group(1)
|
||||
if mode not in db[router]:
|
||||
db[router][mode] = {}
|
||||
if area not in db[router][mode]:
|
||||
db[router][mode][area] = []
|
||||
tr = sltr
|
||||
elif heading == 'AS External Link States':
|
||||
mode = 'external_states'
|
||||
if mode not in db[router]:
|
||||
db[router][mode] = []
|
||||
tr = eltr
|
||||
else:
|
||||
raise DaemonError('failed to parse heading: ' + heading)
|
||||
# told you.
|
||||
else:
|
||||
if not header_parsed:
|
||||
if mode in ['summary_link_state_area', 'external_states']:
|
||||
# workaround because "Route" matches on "Router" and breaks the offset parsing logic
|
||||
# stay consistent with the previous script, add a trailing newline
|
||||
line += '\n'
|
||||
tr.read_header(line)
|
||||
header_parsed = True
|
||||
else:
|
||||
if mode == 'router':
|
||||
raise DaemonError(
|
||||
'attempting to parse a table row but the mode is \'router\'. '
|
||||
'please debug the FRR output:\n\n\n' + line
|
||||
)
|
||||
elif mode == 'external_states':
|
||||
db[router][mode].append(tr.read_line(line))
|
||||
else:
|
||||
db[router][mode][area].append(tr.read_line(line))
|
||||
|
||||
return db
|
||||
|
||||
|
||||
class OSPFv3(Daemon):
|
||||
def _show(self, suffix: str):
|
||||
return super()._show('ipv6 ospf6 ' + suffix)
|
||||
|
||||
def database(self):
|
||||
database = {}
|
||||
lines = map(str.strip, self._show('database'))
|
||||
|
||||
# table reader
|
||||
tr = FRRTableReader(titles=["Type", "LSId", "AdvRouter", " Age", " SeqNum", " Payload"])
|
||||
header_parsed = False
|
||||
interface = None
|
||||
area = None
|
||||
mode = None
|
||||
|
||||
for line in lines:
|
||||
if self.myre.search(r'Area Scoped Link State Database \(Area (.*)\)', line):
|
||||
header_parsed = False
|
||||
mode = 'scoped_link_db'
|
||||
area = self.myre.last_match.group(1)
|
||||
if mode not in database:
|
||||
database[mode] = {}
|
||||
if area not in database[mode]:
|
||||
database[mode][area] = []
|
||||
elif self.myre.search(r'I\/F Scoped Link State Database \(I\/F (\S+) in Area (.*)\)', line):
|
||||
header_parsed = False
|
||||
mode = 'if_scoped_link_state'
|
||||
interface = self.myre.last_match.group(1)
|
||||
area = self.myre.last_match.group(2)
|
||||
if mode not in database:
|
||||
database[mode] = {}
|
||||
if interface not in database[mode]:
|
||||
database[mode][interface] = {}
|
||||
if area not in database[mode][interface]:
|
||||
database[mode][interface][area] = []
|
||||
elif line == 'AS Scoped Link State Database':
|
||||
header_parsed = False
|
||||
mode = 'as_scoped'
|
||||
if mode not in database:
|
||||
database[mode] = []
|
||||
else:
|
||||
if not header_parsed:
|
||||
tr.read_header(line)
|
||||
header_parsed = True
|
||||
else:
|
||||
if mode == 'scoped_link_db':
|
||||
database[mode][area].append(tr.read_line(line))
|
||||
elif mode == 'if_scoped_link_state':
|
||||
database[mode][interface][area].append(tr.read_line(line))
|
||||
elif mode == 'as_scoped':
|
||||
database[mode].append(tr.read_line(line))
|
||||
else:
|
||||
raise DaemonError('invalid mode, failed to parse line: ' + line)
|
||||
|
||||
return database
|
||||
|
||||
def route(self):
|
||||
route = []
|
||||
lines = map(str.strip, self._show('route'))
|
||||
|
||||
for line in lines:
|
||||
columns = re.split(r'\s+', line)
|
||||
route.append({
|
||||
'f1': columns[0],
|
||||
'f2': columns[1],
|
||||
'network': columns[2],
|
||||
'gateway': columns[3],
|
||||
'interface': columns[4],
|
||||
'time': columns[5],
|
||||
})
|
||||
|
||||
return route
|
||||
|
||||
def interface(self):
|
||||
interface = {}
|
||||
lines = map(str.strip, self._show('interface'))
|
||||
|
||||
current_if = None
|
||||
for line in lines:
|
||||
if self.myre.search(r'(\S+) is (down|up), type ([A-Z]+)', line):
|
||||
current_if = self.myre.last_match.group(1)
|
||||
interface[current_if] = {
|
||||
'up': True if self.myre.last_match.group(2) == 'up' else False,
|
||||
'type': self.myre.last_match.group(3),
|
||||
'enabled': True
|
||||
}
|
||||
elif self.myre.search(r'Interface ID: (\d+)', line):
|
||||
interface[current_if]['id'] = self.myre.last_match.group(1)
|
||||
elif self.myre.search(r'OSPF not enabled on this interface', line):
|
||||
interface[current_if]['enabled'] = False
|
||||
elif self.myre.search(r'Instance ID (\d+), Interface MTU (\d+) \(autodetect: (\d+)\)', line):
|
||||
interface[current_if]['instance_id'] = int(self.myre.last_match.group(1))
|
||||
interface[current_if]['interface_mtu'] = int(self.myre.last_match.group(2))
|
||||
interface[current_if]['interface_mtu_autodetect'] = int(self.myre.last_match.group(3))
|
||||
elif self.myre.search(r'(inet |inet6): (\S+)', line):
|
||||
family = 'IPv6' if self.myre.last_match.group(1) == 'inet6' else 'IPv4'
|
||||
if family not in interface[current_if]:
|
||||
interface[current_if][family] = []
|
||||
interface[current_if][family].append(self.myre.last_match.group(2))
|
||||
elif self.myre.search(r'MTU mismatch detection: (en|dis)abled', line):
|
||||
interface[current_if]['mtu_mismatch_detection'] = True if self.myre.last_match.group(
|
||||
1) == 'en' else False
|
||||
elif self.myre.search(r'DR: (\S+) BDR: (\S+)', line):
|
||||
interface[current_if]['designated_router'] = self.myre.last_match.group(1)
|
||||
interface[current_if]['backup_designated_router'] = self.myre.last_match.group(2)
|
||||
elif self.myre.search(r'State (\S+), Transmit Delay (\d+) sec, Priority (\d+)', line):
|
||||
interface[current_if]['state'] = self.myre.last_match.group(1)
|
||||
interface[current_if]['transmit_delay'] = int(self.myre.last_match.group(2))
|
||||
interface[current_if]['priority'] = int(self.myre.last_match.group(3))
|
||||
elif self.myre.search(r'Number of I\/F scoped LSAs is (\d+)', line):
|
||||
interface[current_if]['number_if_scoped_lsas'] = int(self.myre.last_match.group(1))
|
||||
elif self.myre.search(r'(\d+) Pending LSAs for (\S+) in Time ([\d:]+)(?: (.*))', line):
|
||||
if 'pending_lsas' not in interface[current_if]:
|
||||
interface[current_if]['pending_lsas'] = {}
|
||||
interface[current_if]['pending_lsas'][self.myre.last_match.group(2)] = {
|
||||
'time': self.myre.last_match.group(3),
|
||||
'count': self.myre.last_match.group(1),
|
||||
'flags': self.myre.last_match.group(4)
|
||||
}
|
||||
elif self.myre.search(r'Hello (\d+), Dead (\d+), Retransmit (\d+)', line):
|
||||
interface[current_if]['timers'] = {
|
||||
'hello': int(self.myre.last_match.group(1)),
|
||||
'dead': int(self.myre.last_match.group(2)),
|
||||
'retransmit': int(self.myre.last_match.group(3))
|
||||
}
|
||||
elif self.myre.search(r'Area ID (\S+), Cost (\d+)', line):
|
||||
if 'area_cost' not in interface[current_if]:
|
||||
interface[current_if]['area_cost'] = []
|
||||
interface[current_if]['area_cost'].append({
|
||||
'area': self.myre.last_match.group(1),
|
||||
'cost': int(self.myre.last_match.group(2))
|
||||
})
|
||||
elif line in ['Internet Address:', 'Timer intervals configured:']:
|
||||
# ignore these strings
|
||||
pass
|
||||
else:
|
||||
raise DaemonError('failed to parse line: ' + line)
|
||||
|
||||
return interface
|
||||
|
||||
def neighbor(self):
|
||||
neighbors = []
|
||||
lines = self._show('neighbor')
|
||||
|
||||
tr = FRRTableReader(titles=['Neighbor ID', 'Pri', 'DeadTime', 'State/IfState', 'Duration I/F[State]'])
|
||||
ll = lines.pop(0)
|
||||
tr.read_header(ll)
|
||||
|
||||
for line in lines:
|
||||
neighbor = tr.read_line(line)
|
||||
neighbor['Pri'] = int(neighbor['Pri'])
|
||||
neighbors.append(neighbor)
|
||||
|
||||
return neighbors
|
||||
|
||||
def overview(self):
|
||||
overview = {'areas': {}}
|
||||
lines = map(str.strip, self._show(''))
|
||||
|
||||
current_area = None
|
||||
|
||||
for line in lines:
|
||||
if self.myre.search(r'OSPFv3 Routing Process \((\d+)\) with Router-ID ([\d\.]+)', line):
|
||||
overview['router_id'] = self.myre.last_match.group(2)
|
||||
overview['routing_process'] = int(self.myre.last_match.group(1))
|
||||
elif self.myre.search(r'Initial SPF scheduling delay (\d+) millisec\(s\)', line):
|
||||
overview['initial_spf_scheduling_delay'] = int(self.myre.last_match.group(1))
|
||||
elif self.myre.search(r'(Min|Max)imum hold time between consecutive SPFs (\d+) milli?second\(s\)', line):
|
||||
if 'hold_time' not in overview:
|
||||
overview['hold_time'] = {}
|
||||
overview['hold_time'][self.myre.last_match.group(1).lower()] = int(self.myre.last_match.group(2))
|
||||
elif line == 'This router is an ASBR (injecting external routing information)':
|
||||
overview['asbr'] = True
|
||||
elif self.myre.search(r'SPF timer is (.*)', line):
|
||||
overview['spf_timer'] = self.myre.last_match.group(1)
|
||||
elif self.myre.search(r'Running (.*)', line):
|
||||
overview['running_time'] = self.myre.last_match.group(1)
|
||||
elif self.myre.search(r'Number of AS scoped LSAs is (\d+)', line):
|
||||
overview['number_as_scoped'] = int(self.myre.last_match.group(1))
|
||||
elif self.myre.search(r'Hold time multiplier is currently (\d+)', line):
|
||||
overview['current_hold_time_multipier'] = int(self.myre.last_match.group(1))
|
||||
elif self.myre.search(r'Number of areas in this router is (\d+)', line):
|
||||
overview['number_of_areas'] = int(self.myre.last_match.group(1))
|
||||
elif self.myre.search(r'^Area ([\d\.]*)', line):
|
||||
current_area = self.myre.last_match.group(1)
|
||||
overview['areas'][current_area] = {}
|
||||
elif self.myre.search(r'Interface attached to this area: (.*)', line):
|
||||
overview['areas'][current_area]['interfaces'] = self.myre.last_match.group(1).split(' ')
|
||||
elif self.myre.search(r'Number of Area scoped LSAs is (.*)', line):
|
||||
overview['areas'][current_area]['number_lsas'] = int(self.myre.last_match.group(1))
|
||||
elif self.myre.search(r'LSA minimum arrival (.*)', line) or \
|
||||
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) or \
|
||||
self.myre.search(r'Adjacency changes are logged', line):
|
||||
# skip these lines
|
||||
pass
|
||||
else:
|
||||
raise DaemonError('failed to parse line: ' + line)
|
||||
|
||||
return overview
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(description='Python port of the OPNsense FRR output parser.')
|
||||
parser.add_argument('-d', '--ospf-database', help='Prints the OSPF Database', action='store_true')
|
||||
parser.add_argument('-D', '--ospfv3-database', help='Prints the OSPFv3 Database', action='store_true')
|
||||
parser.add_argument('-t', '--ospfv3-route', help='Prints the OSPFv3 routing table', action='store_true')
|
||||
parser.add_argument('-I', '--ospfv3-interface', help='Prints OSPFv3 interface information', action='store_true')
|
||||
parser.add_argument('-N', '--ospfv3-neighbor', help='Prints OSPFv3 neighbor information', action='store_true')
|
||||
parser.add_argument('-O', '--ospfv3-overview', help='Prints an OSPFv3 Summary', action='store_true')
|
||||
args = parser.parse_args()
|
||||
|
||||
# initialize VtySH and parser objects
|
||||
main_vtysh = VtySH()
|
||||
ospf = OSPF(main_vtysh)
|
||||
ospfv3 = OSPFv3(main_vtysh)
|
||||
|
||||
main_result = {}
|
||||
if args.ospf_database:
|
||||
main_result['ospf_database'] = ospf.database()
|
||||
elif args.ospfv3_database:
|
||||
main_result['ospfv3_database'] = ospfv3.database()
|
||||
elif args.ospfv3_route:
|
||||
main_result['ospfv3_route'] = ospfv3.route()
|
||||
elif args.ospfv3_interface:
|
||||
main_result['ospfv3_interface'] = ospfv3.interface()
|
||||
elif args.ospfv3_neighbor:
|
||||
main_result['ospfv3_neighbors'] = ospfv3.neighbor()
|
||||
elif args.ospfv3_overview:
|
||||
main_result['ospfv3_overview'] = ospfv3.overview()
|
||||
|
||||
print(ujson.dumps(main_result, escape_forward_slashes=False))
|
||||
@@ -30,271 +30,134 @@ type:script_output
|
||||
message:FRR diagnosticts "show running-config"
|
||||
|
||||
[diagnostics.general_route4]
|
||||
command:/usr/local/bin/vtysh -c "show ip route"
|
||||
parameters:
|
||||
command:/usr/local/bin/vtysh
|
||||
parameters: -c 'show ip route %s'
|
||||
type:script_output
|
||||
message:FRR diagnosticts "show ip route"
|
||||
|
||||
[diagnostics.general_route4_json]
|
||||
command:/usr/local/bin/vtysh -c "show ip route json"
|
||||
parameters:
|
||||
type:script_output
|
||||
message:FRR diagnosticts "show ip route json"
|
||||
|
||||
[diagnostics.general_route6]
|
||||
command:/usr/local/bin/vtysh -c "show ipv6 route"
|
||||
parameters:
|
||||
command:/usr/local/bin/vtysh
|
||||
parameters: -c 'show ipv6 route %s'
|
||||
type:script_output
|
||||
message:FRR diagnosticts "show ipv6 route"
|
||||
|
||||
[diagnostics.general_route6_json]
|
||||
command:/usr/local/bin/vtysh -c "show ipv6 route json"
|
||||
parameters:
|
||||
type:script_output
|
||||
message:FRR diagnosticts "show ipv6 route json"
|
||||
|
||||
[diagnostics.bgp_route]
|
||||
command:exit 1
|
||||
parameters:
|
||||
type:script_output
|
||||
message:FRR diagnostics "show bgp all" (not implemented)
|
||||
|
||||
[diagnostics.bgp_route_json]
|
||||
command:/usr/local/bin/vtysh -c "show bgp all json"
|
||||
parameters:
|
||||
type:script_output
|
||||
message:FRR diagnostics "show bgp all json" (not implemented)
|
||||
|
||||
[diagnostics.bgp_route4]
|
||||
command:/usr/local/bin/vtysh -c "show bgp ipv4"
|
||||
parameters:
|
||||
command:/usr/local/bin/vtysh
|
||||
parameters: -c 'show bgp ipv4 %s'
|
||||
type:script_output
|
||||
message:FRR diagnostics "show bgp ipv4"
|
||||
|
||||
[diagnostics.bgp_route4_json]
|
||||
command:/usr/local/bin/vtysh -c "show bgp ipv4 json"
|
||||
parameters:
|
||||
type:script_output
|
||||
message:FRR diagnostics "show bgp ipv4 json"
|
||||
message:FRR diagnostics "show bgp ipv4 %s"
|
||||
|
||||
[diagnostics.bgp_route6]
|
||||
command:/usr/local/bin/vtysh -c "show bgp ipv6"
|
||||
parameters:
|
||||
command:/usr/local/bin/vtysh
|
||||
parameters:-c 'show bgp ipv6 %s'
|
||||
type:script_output
|
||||
message:FRR diagnostics "show bgp ipv6"
|
||||
|
||||
[diagnostics.bgp_route6_json]
|
||||
command:/usr/local/bin/vtysh -c "show bgp ipv6 json"
|
||||
parameters:
|
||||
type:script_output
|
||||
message:FRR diagnostics "show bgp ipv6 json"
|
||||
message:FRR diagnostics "show bgp ipv6 %s"
|
||||
|
||||
[diagnostics.bgp_summary]
|
||||
command:/usr/local/bin/vtysh -c "show bgp summary"
|
||||
parameters:
|
||||
command:/usr/local/bin/vtysh
|
||||
parameters: -c 'show bgp summary %s'
|
||||
type:script_output
|
||||
message:FRR diagnostics "show bgp summary"
|
||||
|
||||
[diagnostics.bgp_summary_json]
|
||||
command:/usr/local/bin/vtysh -c "show bgp summary json"
|
||||
parameters:
|
||||
type:script_output
|
||||
message:FRR diagnostics "show bgp summary json"
|
||||
message:FRR diagnostics "show bgp summary %s"
|
||||
|
||||
[diagnostics.bgp_summary4]
|
||||
command:/usr/local/bin/vtysh -c "show bgp ipv4 summary"
|
||||
parameters:
|
||||
command:/usr/local/bin/vtysh
|
||||
parameters: -c 'show bgp ipv4 summary %s'
|
||||
type:script_output
|
||||
message:FRR diagnostics "show bgp ipv4 summary"
|
||||
|
||||
[diagnostics.bgp_summary4_json]
|
||||
command:/usr/local/bin/vtysh -c "show bgp ipv4 summary json"
|
||||
parameters:
|
||||
type:script_output
|
||||
message:FRR diagnostics "show bgp ipv4 summary json"
|
||||
message:FRR diagnostics "show bgp ipv4 summary %s"
|
||||
|
||||
[diagnostics.bgp_summary6]
|
||||
command:/usr/local/bin/vtysh -c "show bgp ipv6 summary"
|
||||
parameters:
|
||||
command:/usr/local/bin/vtysh
|
||||
parameters: -c 'show bgp ipv6 summary %s'
|
||||
type:script_output
|
||||
message:FRR diagnostics "show bgp ipv6 summary"
|
||||
|
||||
[diagnostics.bgp_summary6_json]
|
||||
command:/usr/local/bin/vtysh -c "show bgp ipv6 summary json"
|
||||
parameters:
|
||||
type:script_output
|
||||
message:FRR diagnostics "show bgp ipv6 summary json"
|
||||
message:FRR diagnostics "show bgp ipv6 summary %s"
|
||||
|
||||
[diagnostics.bgp_neighbors]
|
||||
command:/usr/local/bin/vtysh -c "show bgp neighbors"
|
||||
parameters:
|
||||
command:/usr/local/bin/vtysh
|
||||
parameters: -c 'show bgp neighbors %s'
|
||||
type:script_output
|
||||
message:FRR diagnostics "show bgp neighbors"
|
||||
|
||||
[diagnostics.bgp_neighbors_json]
|
||||
command:/usr/local/bin/vtysh -c "show bgp neighbors json"
|
||||
parameters:
|
||||
type:script_output
|
||||
message:FRR diagnostics "show bgp neighbors json"
|
||||
message:FRR diagnostics "show bgp neighbors %s"
|
||||
|
||||
[diagnostics.bgp_neighbors4]
|
||||
command:/usr/local/bin/vtysh -c "show bgp ipv4 neighbors"
|
||||
parameters:
|
||||
command:/usr/local/bin/vtysh
|
||||
parameters: -c 'show bgp ipv4 neighbors %s'
|
||||
type:script_output
|
||||
message:FRR diagnostics "show bgp ipv4 neighbors"
|
||||
|
||||
[diagnostics.bgp_neighbors4_json]
|
||||
command:/usr/local/bin/vtysh -c "show bgp ipv4 neighbors json"
|
||||
parameters:
|
||||
type:script_output
|
||||
message:FRR diagnostics "show bgp ipv4 neighbors json"
|
||||
message:FRR diagnostics "show bgp ipv4 neighbors %s"
|
||||
|
||||
[diagnostics.bgp_neighbors6]
|
||||
command:/usr/local/bin/vtysh -c "show bgp ipv6 neighbors"
|
||||
parameters:
|
||||
command:/usr/local/bin/vtysh
|
||||
parameters: -c 'show bgp ipv6 neighbors %s'
|
||||
type:script_output
|
||||
message:FRR diagnostics "show bgp ipv6 neighbors"
|
||||
|
||||
[diagnostics.bgp_neighbors6_json]
|
||||
command:/usr/local/bin/vtysh -c "show bgp ipv6 neighbors json"
|
||||
parameters:
|
||||
type:script_output
|
||||
message:FRR diagnostics "show bgp ipv6 neighbors json"
|
||||
message:FRR diagnostics "show bgp ipv6 neighbors %s"
|
||||
|
||||
[diagnostics.ospf_overview]
|
||||
command:/usr/local/bin/vtysh -c "show ip ospf"
|
||||
parameters:
|
||||
command:/usr/local/bin/vtysh
|
||||
parameters: -c 'show ip ospf %s'
|
||||
type:script_output
|
||||
message:FRR diagnostics "show ip ospf"
|
||||
|
||||
[diagnostics.ospf_overview_json]
|
||||
command:/usr/local/bin/vtysh -c "show ip ospf json"
|
||||
parameters:
|
||||
type:script_output
|
||||
message:FRR diagnostics "show ip ospf json"
|
||||
message:FRR diagnostics "show ip ospf %s"
|
||||
|
||||
[diagnostics.ospf_neighbor]
|
||||
command:/usr/local/bin/vtysh -c "show ip ospf neighbor"
|
||||
parameters:
|
||||
command:/usr/local/bin/vtysh
|
||||
parameters: -c 'show ip ospf neighbor %s'
|
||||
type:script_output
|
||||
message:FRR diagnostics "show ip ospf neighbor"
|
||||
|
||||
[diagnostics.ospf_neighbor_json]
|
||||
command:/usr/local/bin/vtysh -c "show ip ospf neighbor json"
|
||||
parameters:
|
||||
type:script_output
|
||||
message:FRR diagnostics "show ip ospf neighbor json"
|
||||
message:FRR diagnostics "show ip ospf neighbor %s"
|
||||
|
||||
[diagnostics.ospf_route]
|
||||
command:/usr/local/bin/vtysh -c "show ip ospf route"
|
||||
parameters:
|
||||
command:/usr/local/bin/vtysh
|
||||
parameters: -c 'show ip ospf route %s'
|
||||
type:script_output
|
||||
message:FRR diagnostics "show ip ospf route"
|
||||
message:FRR diagnostics "show ip ospf route %s"
|
||||
|
||||
[diagnostics.ospf_route_json]
|
||||
command:/usr/local/bin/vtysh -c "show ip ospf route json"
|
||||
parameters:
|
||||
[diagnostics.ospf_interface]
|
||||
command:/usr/local/bin/vtysh
|
||||
parameters: -c 'show ip ospf interface %s'
|
||||
type:script_output
|
||||
message:FRR diagnostics "show ip ospf route json"
|
||||
message:FRR diagnostics "show ip ospf interface %s"
|
||||
|
||||
[diagnostics.bfd_neighbors]
|
||||
command:/usr/local/bin/vtysh
|
||||
parameters: -c 'show bfd peers %s'
|
||||
type:script_output
|
||||
message:FRR diagnostics "show bfd peers json %s"
|
||||
|
||||
[diagnostics.bfd_summary]
|
||||
command:/usr/local/bin/vtysh
|
||||
parameters: -c 'show bfd peers brief %s'
|
||||
type:script_output
|
||||
message:FRR diagnostics "show bfd peers brief %s"
|
||||
|
||||
[diagnostics.bfd_counters]
|
||||
command:/usr/local/bin/vtysh
|
||||
parameters: -c 'show bfd peers counters %s'
|
||||
type:script_output
|
||||
message:FRR diagnostics "show bfd peers counters %s"
|
||||
|
||||
[diagnostics.ospf_database]
|
||||
command:/usr/local/bin/vtysh -c "show ip ospf database"
|
||||
parameters:
|
||||
command:/usr/local/bin/vtysh
|
||||
parameters: -c 'show ip ospf database %s'
|
||||
type:script_output
|
||||
message:FRR diagnostics "show ip ospf database"
|
||||
|
||||
[diagnostics.ospf_database_json]
|
||||
command:/usr/local/opnsense/scripts/frr/legacy-diagnostics.py --ospf-database
|
||||
parameters:
|
||||
type:script_output
|
||||
message:FRR diagnostics "show ip ospf database json"
|
||||
|
||||
[diagnostics.ospf_interface]
|
||||
command:/usr/local/bin/vtysh -c "show ip ospf interface"
|
||||
parameters:
|
||||
type:script_output
|
||||
message:FRR diagnostics "show ip ospf interface"
|
||||
|
||||
[diagnostics.ospf_interface_json]
|
||||
command:/usr/local/bin/vtysh -c "show ip ospf interface json"
|
||||
parameters:
|
||||
type:script_output
|
||||
message:FRR diagnostics "show ip ospf interface json"
|
||||
|
||||
[diagnostics.bfd_neighbors_json]
|
||||
command:/usr/local/bin/vtysh -c "show bfd peers json"
|
||||
parameters:
|
||||
type:script_output
|
||||
message:FRR diagnostics "show bfd peers json"
|
||||
|
||||
[diagnostics.bfd_summary]
|
||||
command:/usr/local/bin/vtysh -c "show bfd peers brief"
|
||||
parameters:
|
||||
type:script_output
|
||||
message:FRR diagnostics "show bfd peers brief"
|
||||
|
||||
[diagnostics.bfd_counters_json]
|
||||
command:/usr/local/bin/vtysh -c "show bfd peers counters json"
|
||||
parameters:
|
||||
type:script_output
|
||||
message:FRR diagnostics "show bfd peers counters json"
|
||||
|
||||
[diagnostics.ospfv3_overview]
|
||||
command:/usr/local/bin/vtysh -c "show ipv6 ospf6"
|
||||
parameters:
|
||||
command:/usr/local/bin/vtysh
|
||||
parameters: -c 'show ipv6 ospf6 %s'
|
||||
type:script_output
|
||||
message:FRR diagnostics "show ipv6 ospf6"
|
||||
|
||||
[diagnostics.ospfv3_overview_json]
|
||||
command:/usr/local/opnsense/scripts/frr/legacy-diagnostics.py --ospfv3-overview
|
||||
parameters:
|
||||
type:script_output
|
||||
message:FRR diagnostics "show ipv6 ospf6 json"
|
||||
|
||||
[diagnostics.ospfv3_neighbor]
|
||||
command:/usr/local/bin/vtysh -c "show ipv6 ospf6 neighbor"
|
||||
parameters:
|
||||
type:script_output
|
||||
message:FRR diagnostics "show ipv6 ospf6 neighbor"
|
||||
|
||||
[diagnostics.ospfv3_neighbor_json]
|
||||
command:/usr/local/opnsense/scripts/frr/legacy-diagnostics.py --ospfv3-neighbor
|
||||
parameters:
|
||||
type:script_output
|
||||
message:FRR diagnostics "show ipv6 ospf6 neighbor json"
|
||||
message:FRR diagnostics "show ipv6 ospf6 %s"
|
||||
|
||||
[diagnostics.ospfv3_route]
|
||||
command:/usr/local/bin/vtysh -c "show ipv6 ospf6 route"
|
||||
parameters:
|
||||
command:/usr/local/bin/vtysh
|
||||
parameters: -c 'show ipv6 ospf6 route %s'
|
||||
type:script_output
|
||||
message:FRR diagnostics "show ipv6 ospf6 route"
|
||||
|
||||
[diagnostics.ospfv3_route_json]
|
||||
command:/usr/local/opnsense/scripts/frr/legacy-diagnostics.py --ospfv3-route
|
||||
parameters:
|
||||
type:script_output
|
||||
message:FRR diagnostics "show ipv6 ospf6 route json"
|
||||
message:FRR diagnostics "show ipv6 ospf6 route %s"
|
||||
|
||||
[diagnostics.ospfv3_database]
|
||||
command:/usr/local/bin/vtysh -c "show ipv6 ospf6 database"
|
||||
parameters:
|
||||
type:script_output
|
||||
message:FRR diagnostics "show ipv6 ospf6 database"
|
||||
|
||||
[diagnostics.ospfv3_database_json]
|
||||
command:/usr/local/opnsense/scripts/frr/legacy-diagnostics.py --ospfv3-database
|
||||
parameters:
|
||||
command:/usr/local/bin/vtysh
|
||||
parameters: -c 'show ipv6 ospf6 database %s'
|
||||
type:script_output
|
||||
message:FRR diagnostics "show ipv6 ospf6 database json"
|
||||
|
||||
[diagnostics.ospfv3_interface]
|
||||
command:/usr/local/bin/vtysh -c "show ipv6 ospf6 interface"
|
||||
parameters:
|
||||
command:/usr/local/bin/vtysh
|
||||
parameters: -c 'show ipv6 ospf6 interface %s'
|
||||
type:script_output
|
||||
message:FRR diagnostics "show ipv6 ospf6 interface"
|
||||
message:FRR diagnostics "show ipv6 ospf6 interface %s"
|
||||
|
||||
[diagnostics.ospfv3_interface_json]
|
||||
command:/usr/local/opnsense/scripts/frr/legacy-diagnostics.py --ospfv3-interface
|
||||
parameters:
|
||||
type:script_output
|
||||
message:FRR diagnostics "show ipv6 ospf6 interface json"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2015-2022 Deciso B.V.
|
||||
* Copyright (C) 2015-2023 Deciso B.V.
|
||||
* Copyright (C) 2023 Marc Bartelt
|
||||
* All rights reserved.
|
||||
*
|
||||
@@ -27,85 +27,6 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* shared options for bootgrid tables
|
||||
*/
|
||||
let gridopt = {
|
||||
formatters: {
|
||||
boolean: function(column, row) {
|
||||
if (row[column.id]) {
|
||||
return '<span class="fa fa-fw fa-check" data-value="1" data-row-id="' + row.uuid + '"></span>';
|
||||
} 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 += '<abbr title="' + translatedRouteType + '">' + routeType + '</abbr>';
|
||||
}
|
||||
|
||||
result += ' ';
|
||||
});
|
||||
|
||||
return result;
|
||||
},
|
||||
general_route_code: function(column, row) {
|
||||
let result = row.code;
|
||||
let protocol = translateZebraCode(row.code);
|
||||
|
||||
if(typeof(protocol) !== 'string') result = '<abbr title="' + protocol['long'] + '">' + protocol['short'] + '</abbr>';
|
||||
if(row.selected) result += ' <abbr title="Selected">></abbr>';
|
||||
if(row.installed) result += ' <abbr title="FIB">*</abbr>';
|
||||
|
||||
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
|
||||
*/
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user