net/frr - cleanup unused endpoints for 23.7 and align with FRR8 for OPNsense 23.7 (#3498)

This commit is contained in:
Ad Schellevis
2023-07-10 19:56:23 +02:00
committed by GitHub
parent 5c3d27cc51
commit 13797b7c17
8 changed files with 268 additions and 1349 deletions
@@ -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('&nbsp;');
});
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">&gt;</abbr>';
if(row.installed) result += ' <abbr title="FIB">&ast;</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