Compare commits
10 Commits
d428b82220
...
1d5e5ad8cb
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1d5e5ad8cb | ||
|
|
12c4b22ea2 | ||
|
|
ccd2de0651 | ||
|
|
9128c0d50d | ||
|
|
bc47b54f67 | ||
|
|
b85ca1140f | ||
|
|
a91e518f16 | ||
|
|
a068960b51 | ||
|
|
8397eabe50 | ||
|
|
2afa56dde9 |
1
Build.PL
1
Build.PL
@@ -79,6 +79,7 @@ Module::Build->new(
|
||||
'Plack::Middleware::ReverseProxy' => '0.15',
|
||||
'Pod::Usage' => 0,
|
||||
'Regexp::Common' => 2017060201,
|
||||
'Regexp::Common::net::CIDR' => 0,
|
||||
'Role::Tiny' => '1.002005',
|
||||
'Scope::Guard' => 0,
|
||||
'Sereal' => '0',
|
||||
|
||||
32
Changes
32
Changes
@@ -1,3 +1,35 @@
|
||||
2.071001 - 2023-12-07
|
||||
|
||||
[BUG FIXES]
|
||||
|
||||
* update vlansneverconfigured report to skip vlan 1
|
||||
* better approach to HTML entity encoding in custom report searchable fields
|
||||
|
||||
2.071000 - 2023-12-07
|
||||
|
||||
[NEW FEATURES]
|
||||
|
||||
* device port external links support
|
||||
* new preset fields and custom fields support for device external links
|
||||
* custom reports returning array columns will be split over lines
|
||||
* default database in tenancies can have a friendly name
|
||||
* #1133 improve searchable generic report fields
|
||||
|
||||
[ENHANCEMENTS]
|
||||
|
||||
* tidied up the report menus and report names
|
||||
* #830 subnets report can show all subnets to start with
|
||||
* #920 unused VLANs report
|
||||
* #999 device vlan count report
|
||||
* #1018 VLANs with Multiple Names report
|
||||
* #1022 VLANs Known but Not Configured report
|
||||
* #1023 ports with most vlans report
|
||||
* #1052 duplicate private networks report
|
||||
|
||||
[BUG FIXES]
|
||||
|
||||
* do not reverse the external links for IPs and Devices
|
||||
|
||||
2.070003 - 2023-11-24
|
||||
|
||||
[BUG FIXES]
|
||||
|
||||
2
MANIFEST
2
MANIFEST
@@ -578,6 +578,8 @@ share/views/ajax/report/subnets.tt
|
||||
share/views/ajax/report/subnets_csv.tt
|
||||
share/views/ajax/report/vlaninventory.tt
|
||||
share/views/ajax/report/vlaninventory_csv.tt
|
||||
share/views/ajax/report/vlanmultiplenames.tt
|
||||
share/views/ajax/report/vlanmultiplenames_csv.tt
|
||||
share/views/ajax/search/device.tt
|
||||
share/views/ajax/search/device_csv.tt
|
||||
share/views/ajax/search/node_by_ip.tt
|
||||
|
||||
@@ -93,6 +93,7 @@
|
||||
"Plack::Middleware::ReverseProxy" : "0.15",
|
||||
"Pod::Usage" : "0",
|
||||
"Regexp::Common" : "2017060201",
|
||||
"Regexp::Common::net::CIDR" : "0",
|
||||
"Role::Tiny" : "1.002005",
|
||||
"SNMP::Info" : "3.95",
|
||||
"SQL::Abstract" : "1.85",
|
||||
@@ -138,7 +139,7 @@
|
||||
"provides" : {
|
||||
"App::Netdisco" : {
|
||||
"file" : "lib/App/Netdisco.pm",
|
||||
"version" : "2.070003"
|
||||
"version" : "2.071001"
|
||||
},
|
||||
"App::Netdisco::AnyEvent::Nbtstat" : {
|
||||
"file" : "lib/App/Netdisco/AnyEvent/Nbtstat.pm"
|
||||
@@ -961,6 +962,6 @@
|
||||
"x_IRC" : "irc://irc.libera.chat/#netdisco",
|
||||
"x_MailingList" : "https://lists.sourceforge.net/lists/listinfo/netdisco-users"
|
||||
},
|
||||
"version" : "2.070003",
|
||||
"version" : "2.071001",
|
||||
"x_serialization_backend" : "JSON::PP version 4.16"
|
||||
}
|
||||
|
||||
5
META.yml
5
META.yml
@@ -23,7 +23,7 @@ name: App-Netdisco
|
||||
provides:
|
||||
App::Netdisco:
|
||||
file: lib/App/Netdisco.pm
|
||||
version: '2.070003'
|
||||
version: '2.071001'
|
||||
App::Netdisco::AnyEvent::Nbtstat:
|
||||
file: lib/App/Netdisco/AnyEvent/Nbtstat.pm
|
||||
App::Netdisco::Backend::Job:
|
||||
@@ -625,6 +625,7 @@ requires:
|
||||
Plack::Middleware::ReverseProxy: '0.15'
|
||||
Pod::Usage: '0'
|
||||
Regexp::Common: '2017060201'
|
||||
Regexp::Common::net::CIDR: '0'
|
||||
Role::Tiny: '1.002005'
|
||||
SNMP::Info: '3.95'
|
||||
SQL::Abstract: '1.85'
|
||||
@@ -663,5 +664,5 @@ resources:
|
||||
homepage: http://netdisco.org/
|
||||
license: http://opensource.org/licenses/BSD-3-Clause
|
||||
repository: https://github.com/netdisco/netdisco
|
||||
version: '2.070003'
|
||||
version: '2.071001'
|
||||
x_serialization_backend: 'CPAN::Meta::YAML version 0.018'
|
||||
|
||||
@@ -4,7 +4,7 @@ use strict;
|
||||
use warnings;
|
||||
use 5.010_000;
|
||||
|
||||
our $VERSION = '2.070003';
|
||||
our $VERSION = '2.071001';
|
||||
use App::Netdisco::Configuration;
|
||||
|
||||
=head1 NAME
|
||||
|
||||
@@ -247,7 +247,7 @@ hook after_error_render => sub { setting('layout' => 'main') };
|
||||
tag => $_->{'tag'},
|
||||
path => config->{'url_base'}->with("/t/$_->{tag}")->path } ) }
|
||||
@{ setting('tenant_databases') },
|
||||
{ tag => 'netdisco', displayname => 'Default' }
|
||||
{ tag => 'netdisco', displayname => (setting('database')->{displayname} || 'Default') }
|
||||
});
|
||||
config->{'tenant_data'}->{'netdisco'}->{'path'}
|
||||
= URI::Based->new((config->{path} eq '/') ? '' : config->{path})->path;
|
||||
|
||||
@@ -9,6 +9,10 @@ use Path::Class 'file';
|
||||
use Storable 'dclone';
|
||||
use Safe;
|
||||
|
||||
use HTML::Entities 'encode_entities';
|
||||
use Regexp::Common qw( RE_net_IPv4 RE_net_IPv6 RE_net_MAC RE_net_domain );
|
||||
use Regexp::Common::net::CIDR ();
|
||||
|
||||
our ($config, @data);
|
||||
|
||||
foreach my $report (@{setting('reports')}) {
|
||||
@@ -72,6 +76,42 @@ foreach my $report (@{setting('reports')}) {
|
||||
my @results = ((-f $munger) ? $compartment->rdo( $munger ) : @data);
|
||||
return if $@ or (0 == scalar @results);
|
||||
|
||||
# searchable field support..
|
||||
|
||||
my $recidr4 = $RE{net}{CIDR}{IPv4}{-keep}; #RE_net_CIDR_IPv4(-keep);
|
||||
my $rev4 = RE_net_IPv4(-keep);
|
||||
my $rev6 = RE_net_IPv6(-keep);
|
||||
my $remac = RE_net_MAC(-keep);
|
||||
|
||||
foreach my $row (@results) {
|
||||
foreach my $col (@column_order) {
|
||||
next unless $column_config{$col}->{_searchable};
|
||||
my $fields = (ref $row->{$col} ? $row->{$col} : [$row->{$col}]);
|
||||
|
||||
foreach my $f (@$fields) {
|
||||
|
||||
encode_entities($f);
|
||||
|
||||
$f =~ s!\b${recidr4}\b!'<a href="'.
|
||||
uri_for('/search', {q => "$1/$2"})->path_query
|
||||
.qq{">$1/$2</a>}!gex;
|
||||
|
||||
if (not $1 and not $2) {
|
||||
$f =~ s!\b${rev4}\b!'<a href="'.
|
||||
uri_for('/search', {q => $1})->path_query .qq{">$1</a>}!gex;
|
||||
}
|
||||
|
||||
$f =~ s!\b${rev6}\b!'<a href="'.
|
||||
uri_for('/search', {q => $1})->path_query .qq{">$1</a>}!gex;
|
||||
|
||||
$f =~ s!\b${remac}\b!'<a href="'.
|
||||
uri_for('/search', {q => $1})->path_query .qq{">$1</a>}!gex;
|
||||
|
||||
$row->{$col} = $f if not ref $row->{$col};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (request->is_ajax) {
|
||||
template 'ajax/report/generic_report.tt',
|
||||
{ results => \@results,
|
||||
|
||||
149
share/config.yml
149
share/config.yml
@@ -16,6 +16,7 @@ logger_format: '[%P] %U %L %m'
|
||||
include_paths: []
|
||||
template_paths: []
|
||||
site_local_files: false
|
||||
database: {}
|
||||
external_databases: []
|
||||
tenant_databases: []
|
||||
|
||||
@@ -126,21 +127,22 @@ sidebar_defaults:
|
||||
c_name: { label: 'Name', default: checked, idx: 7 }
|
||||
c_tags: { label: 'Tags', default: null, idx: 8 }
|
||||
c_speed_admin: { label: 'Speed (configured)', default: null, idx: 9 }
|
||||
c_speed: { label: 'Speed (running)', default: null, idx: 10 }
|
||||
c_duplex_admin: { label: 'Duplex (configured)', default: null, idx: 11 }
|
||||
c_duplex: { label: 'Duplex (running)', default: null, idx: 12 }
|
||||
c_error: { label: 'Error Message', default: null, idx: 13 }
|
||||
c_mac: { label: 'Port MAC', default: null, idx: 14 }
|
||||
c_mtu: { label: 'MTU', default: null, idx: 15 }
|
||||
c_pvid: { label: 'Native VLAN', default: checked, idx: 16 }
|
||||
c_vmember: { label: 'VLAN Membership', default: checked, idx: 17 }
|
||||
c_power: { label: 'PoE', default: null, idx: 18 }
|
||||
c_ssid: { label: 'SSID', default: null, idx: 19 }
|
||||
c_nac_summary: { label: 'NAC/802.1X Status', default: null, idx: 20 }
|
||||
c_nodes: { label: 'Connected Nodes', default: null, idx: 21 }
|
||||
c_neighbors: { label: 'Connected Devices', default: checked, idx: 22 }
|
||||
c_stp: { label: 'Spanning Tree', default: null, idx: 23 }
|
||||
c_up: { label: 'Up/Down Status', default: null, idx: 24 }
|
||||
c_links: { label: 'External Links', default: checked, idx: 10 }
|
||||
c_speed: { label: 'Speed (running)', default: null, idx: 11 }
|
||||
c_duplex_admin: { label: 'Duplex (configured)', default: null, idx: 12 }
|
||||
c_duplex: { label: 'Duplex (running)', default: null, idx: 13 }
|
||||
c_error: { label: 'Error Message', default: null, idx: 14 }
|
||||
c_mac: { label: 'Port MAC', default: null, idx: 15 }
|
||||
c_mtu: { label: 'MTU', default: null, idx: 16 }
|
||||
c_pvid: { label: 'Native VLAN', default: checked, idx: 17 }
|
||||
c_vmember: { label: 'VLAN Membership', default: checked, idx: 18 }
|
||||
c_power: { label: 'PoE', default: null, idx: 19 }
|
||||
c_ssid: { label: 'SSID', default: null, idx: 20 }
|
||||
c_nac_summary: { label: 'NAC/802.1X Status', default: null, idx: 21 }
|
||||
c_nodes: { label: 'Connected Nodes', default: null, idx: 22 }
|
||||
c_neighbors: { label: 'Connected Devices', default: checked, idx: 23 }
|
||||
c_stp: { label: 'Spanning Tree', default: null, idx: 24 }
|
||||
c_up: { label: 'Up/Down Status', default: null, idx: 25 }
|
||||
mac_format: { default: IEEE }
|
||||
n_inventory: { label: 'Remote Inventory', default: checked, idx: 0 }
|
||||
n_detailed_inventory: { label: 'Remote Advertisement', default: null, idx: 1 }
|
||||
@@ -171,8 +173,8 @@ sidebar_defaults:
|
||||
age_num: { default: 3 }
|
||||
age_unit: { default: months }
|
||||
device_port_col_idx_left: 8
|
||||
device_port_col_idx_mid: 20
|
||||
device_port_col_idx_right: 24
|
||||
device_port_col_idx_mid: 21
|
||||
device_port_col_idx_right: 25
|
||||
jobqueue_refresh: 5
|
||||
safe_password_store: true
|
||||
reports: []
|
||||
@@ -181,7 +183,7 @@ system_reports:
|
||||
label: 'Blocked - Error-Disabled'
|
||||
category: Port
|
||||
columns:
|
||||
- { ip: Device }
|
||||
- { ip: Device, _searchable: true }
|
||||
- { dns: DNS }
|
||||
- { port: Port }
|
||||
- { name: Description }
|
||||
@@ -257,14 +259,14 @@ system_reports:
|
||||
HAVING count(vlan) > COALESCE(NULLIF(?,''), '1') ::integer
|
||||
ORDER BY vlans DESC, ip ASC, port ASC
|
||||
- tag: duplicateprivatenetworks
|
||||
category: Port
|
||||
category: IP
|
||||
label: 'Duplicate Private Networks'
|
||||
columns:
|
||||
- { subnet: 'Subnet', _searchable: true }
|
||||
- { count: 'Instances' }
|
||||
- { seen: 'Where Seen' }
|
||||
- { seen: 'Where Seen', _searchable: true }
|
||||
query: |
|
||||
SELECT subnet, count(subnet), array_to_string(array_agg(host(alias)::text || ' on ' || host(ip)::text), ', ') AS seen
|
||||
SELECT subnet, count(subnet), array_agg(host(alias)::text || ' on ' || host(ip)::text) AS seen
|
||||
FROM device_ip
|
||||
WHERE ip <> alias
|
||||
AND (masklen(subnet) <> 32 AND masklen(subnet) <> 128)
|
||||
@@ -282,64 +284,85 @@ system_reports:
|
||||
columns:
|
||||
- { ip: 'Device IP', _searchable: true }
|
||||
- { vlans: 'VLAN List' }
|
||||
bind_params: ['chunk_size']
|
||||
query: |
|
||||
SELECT ip, array_to_string(array_agg(DISTINCT vlan::integer ORDER BY vlan::integer ASC), ', ') AS vlans
|
||||
FROM device_port_vlan dpv
|
||||
WHERE native IS false
|
||||
AND vlan <> 1
|
||||
AND (
|
||||
SELECT count(*) FROM device_port_vlan dpv2
|
||||
WHERE dpv2.ip = dpv.ip
|
||||
AND dpv2.vlan = dpv.vlan
|
||||
AND native IS true
|
||||
) = 0
|
||||
GROUP BY ip
|
||||
ORDER BY ip
|
||||
SELECT ip, array_agg(vlans) AS vlans FROM (
|
||||
SELECT ip, array_to_string(array_agg(vlan), ', ') AS vlans, (x / COALESCE(NULLIF(?,''), '20') ::integer) AS chunk FROM (
|
||||
SELECT *, (row_number() over (partition by ip)) AS x FROM (
|
||||
|
||||
SELECT DISTINCT ip, vlan
|
||||
FROM device_port_vlan dpv
|
||||
WHERE native IS false
|
||||
AND vlan <> 1
|
||||
AND (
|
||||
SELECT count(*) FROM device_port_vlan dpv2
|
||||
WHERE dpv2.ip = dpv.ip
|
||||
AND dpv2.vlan = dpv.vlan
|
||||
AND native IS true
|
||||
) = 0
|
||||
ORDER BY ip, vlan)
|
||||
|
||||
) GROUP BY ip, chunk
|
||||
) GROUP BY ip ORDER BY ip
|
||||
- tag: vlansneverconfigured
|
||||
category: VLAN
|
||||
label: 'VLANs Known but Not Configured'
|
||||
columns:
|
||||
- { ip: 'Device IP', _searchable: true }
|
||||
- { vlans: 'VLAN List' }
|
||||
bind_params: ['chunk_size']
|
||||
query: |
|
||||
SELECT ip, array_to_string(array_agg(DISTINCT dv.vlan::integer ORDER BY dv.vlan::integer ASC), ', ') AS vlans
|
||||
FROM device_vlan dv
|
||||
WHERE NOT EXISTS (
|
||||
SELECT FROM device_port_vlan dpv
|
||||
WHERE dpv.ip = dv.ip
|
||||
AND dpv.vlan = dv.vlan
|
||||
)
|
||||
AND vlan NOT IN (1002, 1003, 1004, 1005)
|
||||
GROUP BY ip
|
||||
ORDER BY ip
|
||||
SELECT ip, array_agg(vlans) AS vlans FROM (
|
||||
SELECT ip, array_to_string(array_agg(vlan), ', ') AS vlans, (x / COALESCE(NULLIF(?,''), '20') ::integer) AS chunk FROM (
|
||||
SELECT *, (row_number() over (partition by ip)) AS x FROM (
|
||||
|
||||
SELECT DISTINCT ip, vlan
|
||||
FROM device_vlan dv
|
||||
WHERE vlan <> 1
|
||||
AND NOT EXISTS (
|
||||
SELECT FROM device_port_vlan dpv
|
||||
WHERE dpv.ip = dv.ip
|
||||
AND dpv.vlan = dv.vlan
|
||||
)
|
||||
AND vlan NOT IN (1002, 1003, 1004, 1005)
|
||||
ORDER BY ip, vlan)
|
||||
|
||||
) GROUP BY ip, chunk
|
||||
) GROUP BY ip ORDER BY ip
|
||||
- tag: vlansunused
|
||||
category: VLAN
|
||||
label: 'VLANs No Longer Used'
|
||||
columns:
|
||||
- { ip: 'Device IP', _searchable: true }
|
||||
- { vlans: 'VLAN List' }
|
||||
bind_params: ['free']
|
||||
bind_params: ['chunk_size', 'free']
|
||||
query: |
|
||||
SELECT dpv.ip, array_to_string(array_agg(DISTINCT dpv.vlan::integer ORDER BY dpv.vlan::integer ASC), ', ') AS vlans
|
||||
FROM device_port_vlan dpv
|
||||
WHERE dpv.native IS false
|
||||
AND dpv.vlan <> 1
|
||||
AND (
|
||||
SELECT count(*) FROM device_port_vlan dpv2
|
||||
LEFT JOIN device_port dp USING (ip, port)
|
||||
LEFT JOIN device d USING (ip)
|
||||
WHERE dpv2.ip = dpv.ip
|
||||
AND dpv2.vlan = dpv.vlan
|
||||
AND native IS true
|
||||
SELECT ip, array_agg(vlans) AS vlans FROM (
|
||||
SELECT ip, array_to_string(array_agg(vlan), ', ') AS vlans, (x / COALESCE(NULLIF(?,''), '20') ::integer) AS chunk FROM (
|
||||
SELECT *, (row_number() over (partition by ip)) AS x FROM (
|
||||
|
||||
SELECT DISTINCT ip, vlan
|
||||
FROM device_port_vlan dpv
|
||||
WHERE dpv.native IS false
|
||||
AND dpv.vlan <> 1
|
||||
AND (
|
||||
dp.up_admin = 'up'
|
||||
OR age( LOCALTIMESTAMP,
|
||||
to_timestamp( extract( epoch FROM d.last_discover ) - ( d.uptime - dp.lastchange ) /100 ) ::timestamp )
|
||||
< COALESCE(NULLIF(?,''), '3 months') ::interval
|
||||
)
|
||||
) = 0
|
||||
GROUP BY dpv.ip
|
||||
ORDER BY dpv.ip
|
||||
SELECT count(*) FROM device_port_vlan dpv2
|
||||
LEFT JOIN device_port dp USING (ip, port)
|
||||
LEFT JOIN device d USING (ip)
|
||||
WHERE dpv2.ip = dpv.ip
|
||||
AND dpv2.vlan = dpv.vlan
|
||||
AND native IS true
|
||||
AND (
|
||||
dp.up_admin = 'up'
|
||||
OR age( LOCALTIMESTAMP,
|
||||
to_timestamp( extract( epoch FROM d.last_discover ) - ( d.uptime - dp.lastchange ) /100 ) ::timestamp )
|
||||
< COALESCE(NULLIF(?,''), '3 months') ::interval
|
||||
)
|
||||
) = 0
|
||||
ORDER BY dpv.ip, dpv.vlan)
|
||||
|
||||
) GROUP BY ip, chunk
|
||||
) GROUP BY ip ORDER BY ip
|
||||
- tag: devicevlancount
|
||||
category: VLAN
|
||||
label: 'VLAN Count per Device'
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
[% PROCESS 'externallinks.tt' -%]
|
||||
[% SET user_can_port_control = user_has_role('port_control', device) %]
|
||||
<table id="dp-data-table" class="table table-bordered table-striped" width="100%" cellspacing="0">
|
||||
<thead>
|
||||
@@ -5,6 +6,7 @@
|
||||
<th></th>
|
||||
[% FOREACH item IN settings.port_columns %]
|
||||
[% NEXT IF item.name == 'c_admin' %]
|
||||
[% NEXT IF item.name == 'c_links' AND settings.external_links.device_port.empty %]
|
||||
[% NEXT IF item.name == 'c_nodes' AND params.c_nodes AND params.c_neighbors %]
|
||||
[% NEXT UNLESS params.${item.name} %]
|
||||
[% SET th_class = '' %]
|
||||
@@ -177,6 +179,15 @@
|
||||
|
||||
[% END %]
|
||||
|
||||
[% IF params.c_links AND settings.external_links.device_port.size > 0 %]
|
||||
<td>
|
||||
[% INCLUDE external_device_port_links
|
||||
item = row
|
||||
d = device
|
||||
%]
|
||||
</td>
|
||||
[% END %]
|
||||
|
||||
[% IF params.c_speed_admin %]
|
||||
<td>[% row.speed_admin | html_entity %]</td>
|
||||
[% END %]
|
||||
|
||||
@@ -10,13 +10,16 @@
|
||||
[% FOREACH row IN results %]
|
||||
<tr>
|
||||
[% FOREACH col IN columns %]
|
||||
[% IF column_options.$col._searchable %]
|
||||
<td>
|
||||
<a href="[% uri_for('/search') | none %]?q=[% row.item(col) | uri %]">[% row.item(col) | html_entity %]</a>
|
||||
</td>
|
||||
[% ELSE %]
|
||||
<td>[% row.item(col) | html_entity %]</td>
|
||||
<td>
|
||||
[% FOREACH record IN row.item(col) %]
|
||||
[% IF column_options.$col._searchable %]
|
||||
[% record | none %]
|
||||
[% ELSE %]
|
||||
[% record | html_entity %]
|
||||
[% END %]
|
||||
[% '<br />' IF loop.size > 1 %]
|
||||
[% END %]
|
||||
</td>
|
||||
[% END %]
|
||||
</tr>
|
||||
[% END %]
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
<a href="[% link.url | evaltt %]" target="_blank">
|
||||
<span class="label label-default"><i class="icon-external-link"></i> [% link.displayname | html_entity %]</span></a>
|
||||
[% END %]
|
||||
|
||||
[% BLOCK external_mac_links %]
|
||||
[% FOREACH link IN settings.external_links.node.reverse %]
|
||||
[% NEXT UNLESS link.for_mac %]
|
||||
@@ -11,6 +12,7 @@
|
||||
<span class="nd_node-ext-link"> </span>
|
||||
[% END %]
|
||||
[% END %]
|
||||
|
||||
[% BLOCK external_ip_links %]
|
||||
[% FOREACH link IN settings.external_links.node %]
|
||||
[% NEXT UNLESS link.for_ip %]
|
||||
@@ -18,6 +20,7 @@
|
||||
[% PROCESS external_link %]
|
||||
[% END %]
|
||||
[% END %]
|
||||
|
||||
[% BLOCK external_device_links %]
|
||||
[% FOREACH link IN settings.external_links.device %]
|
||||
[% device = item.ip FILTER uri %]
|
||||
@@ -33,3 +36,20 @@
|
||||
[% PROCESS external_link %]
|
||||
[% END %]
|
||||
[% END %]
|
||||
|
||||
[% BLOCK external_device_port_links %]
|
||||
[% FOREACH link IN settings.external_links.device_port %]
|
||||
[% device = d.ip FILTER uri %]
|
||||
[% fqdn = d.dns FILTER uri %]
|
||||
[% sysname = d.name FILTER uri %]
|
||||
[% hostname = d.dns.remove(settings.domain_suffix) FILTER uri %]
|
||||
[% port = item.port FILTER uri %]
|
||||
[% mac = item.mac FILTER uri %]
|
||||
[% ifindex = item.ifindex FILTER uri %]
|
||||
[% FOREACH config IN settings._extra_device_port_cols %]
|
||||
[% NEXT UNLESS config.field.match('^cf_') %]
|
||||
[% ${config.field} = item.get_column(${config.field}) FILTER uri %]
|
||||
[% END %]
|
||||
[% PROCESS external_link %]
|
||||
[% END %]
|
||||
[% END %]
|
||||
|
||||
@@ -176,7 +176,7 @@
|
||||
<li class="dropdown">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
|
||||
<i class="icon-home text-success"></i>
|
||||
[% settings.tenant_data.$tenant.displayname || 'Default' | html_entity %]
|
||||
[% (settings.tenant_data.$tenant.displayname || settings.database.displayname || 'Default') | html_entity %]
|
||||
<b class="caret"></b>
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
|
||||
Reference in New Issue
Block a user