view release on metacpan or search on metacpan
* #1015 API job queue management - get list, submit, delete some or all, get backend names
[BUG FIXES]
* fix cosmetic bug in search text strikethrough when in tenant
2.061000 - 2023-03-29
[NEW FEATURES]
* #29 sidebar support for custom reports with bind params; add show_sidebar setting
* #1001 support for FQDN node search while domain_suffix is set; add fallback to IPv4 host lookup search
* #1002 implement ignore_layers, force_macsuck, force_arpnip config settings
[ENHANCEMENTS]
* allow 3min for port last_change compare to uptime, do not assume wrapped
[BUG FIXES]
* fix nonimpacting error in template html
[BUG FIXES]
* Fix typo in the Device By Location report template
2.029010 - 2014-10-07
[NEW FEATURES]
* Administration (SSH, Telnet, Web) links for devices
* [#143] Pass parameter(s) to custom reports via bind_params config
[BUG FIXES]
* Require old DBIC version to fix deploy problem
2.029009 - 2014-09-27
[ENHANCEMENTS]
* Defaults now exist for all expire tasks
lib/App/Netdisco.pm view on Meta::CPAN
~/bin/netdisco-deploy
# restart web service (if you run it)
~/bin/netdisco-web restart
# restart the backend workers (wherever you run them)
~/bin/netdisco-backend restart
Furthermore, whenever you upgrade your Operating System, you must delete the
C<~/perl5> directory and re-run the following command, to update Netdisco's C
library bindings (and then run all of the above upgrade commands again):
curl -L https://cpanmin.us/ | perl - --notest --local-lib ~/perl5 App::Netdisco
=head1 Tips and Tricks
=head2 Searching
The main black navigation bar has a search box which is smart enough to work
out what you're looking for in most cases. For example device names, node IP
or MAC addresses, VLAN numbers, and so on.
lib/App/Netdisco/Configuration.pm view on Meta::CPAN
if (setting('reports') and ref {} eq ref setting('reports')) {
config->{'reports'} = [ map {{
tag => $_,
%{ setting('reports')->{$_} }
}} keys %{ setting('reports') } ];
}
# add system_reports onto reports
config->{'reports'} = [ @{setting('system_reports')}, @{setting('reports')} ];
#Â upgrade bare bind_params to dict
foreach my $r ( @{setting('reports')} ) {
next unless exists $r->{bind_params};
my $new_bind_params = [ map {ref $_ ? $_ : {param => $_}} @{ $r->{bind_params} } ];
$r->{'bind_params'} = $new_bind_params;
}
# set swagger ui location
#config->{plugins}->{Swagger}->{ui_dir} =
#dir(dist_dir('App-Netdisco'), 'share', 'public', 'swagger-ui')->absolute;
# setup helpers for when request->uri_for() isn't available
# (for example when inside swagger_path())
config->{url_base}
= URI::Based->new((config->{path} eq '/') ? '' : config->{path});
lib/App/Netdisco/DB/ResultSet/Admin.pm view on Meta::CPAN
=cut
sub skipped {
my ($rs, $backend, $max_deferrals, $retry) = @_;
$backend ||= 'fqdn-undefined';
$max_deferrals ||= (2**30); # not really 'disabled'
$retry ||= '100 years'; # not really 'disabled'
return $rs->correlate('device_skips')->search(undef,{
# NOTE: bind param list order is significant
bind => [[deferrals => $max_deferrals], [last_defer => $retry], [backend => $backend]],
});
}
=head2 with_times
This is a modifier for any C<search()> (including the helpers below) which
will add the following additional synthesized columns to the result set:
=over 4
lib/App/Netdisco/DB/ResultSet/Device.pm view on Meta::CPAN
=head2 device_ips_with_address_or_name( $address_or_name )
Returns a correlated subquery for the set of C<device_ip> entries for each
device. The IP alias or dns matches the supplied C<address_or_name>, using
C<ILIKE>.
=cut
sub device_ips_with_address_or_name {
my ($rs, $q, $ipbind) = @_;
$q ||= '255.255.255.255/32';
return $rs->search(undef,{
# NOTE: bind param list order is significant
join => ['device_ips_by_address_or_name'],
bind => [$q, $ipbind, $q],
});
}
=head2 with_module_serials
Adds the C<module_serials.serial> field to the results using
C<module_serials> relation.
=cut
lib/App/Netdisco/DB/ResultSet/Device.pm view on Meta::CPAN
Returns a correlated subquery for the set of C<device_port> entries for each
device. The port MAC address matches the supplied C<mac>, using C<ILIKE>.
=cut
sub ports_with_mac {
my ($rs, $mac) = @_;
$mac ||= '00:00:00:00:00:00';
return $rs->search(undef,{
# NOTE: bind param list order is significant
join => ['ports_by_mac'],
bind => [$mac],
});
}
=head2 with_times
This is a modifier for any C<search()> (including the helpers below) which
will add the following additional synthesized columns to the result set:
=over 4
lib/App/Netdisco/DB/ResultSet/Device.pm view on Meta::CPAN
die "missing param to search_fuzzy\n"
unless $q;
$q = "\%$q\%" if $q !~ m/\%/;
(my $qc = $q) =~ s/\%//g;
# basic IP check is a string match
my $ip_clause = [
'me.ip::text' => { '-ilike' => $q },
'device_ips_by_address_or_name.alias::text' => { '-ilike' => $q },
];
my $ipbind = '255.255.255.255/32';
# but also allow prefix search
if ($qc =~ m{^(?:$RE{net}{IPv4}|$RE{net}{IPv6})(?:/\d+)?$}i
and my $ip = NetAddr::IP::Lite->new($qc)) {
$ip_clause = [
'me.ip' => { '<<=' => $ip->cidr },
'device_ips_by_address_or_name.alias' => { '<<=' => $ip->cidr },
];
$ipbind = $ip->cidr;
}
# get IEEE MAC format
my $mac = NetAddr::MAC->new(mac => ($q || ''));
undef $mac if
($mac and $mac->as_ieee
and (($mac->as_ieee eq '00:00:00:00:00:00')
or ($mac->as_ieee !~ m/^$RE{net}{MAC}$/i)));
$mac = ($mac ? $mac->as_ieee : $q);
return $rs->ports_with_mac($mac)
->device_ips_with_address_or_name($q, $ipbind)
->search(
{
-or => [
'me.contact' => { '-ilike' => $q },
'me.serial' => { '-ilike' => $q },
'me.chassis_id' => { '-ilike' => $q },
'me.location' => { '-ilike' => $q },
'me.name' => { '-ilike' => $q },
'me.description' => { '-ilike' => $q },
'me.ip' => { '-in' =>
lib/App/Netdisco/DB/ResultSet/DeviceBrowser.pm view on Meta::CPAN
Returns a correlated subquery for the set of C<snmp_object> entry for
the walked data row.
=cut
sub with_snmp_object {
my ($rs, $ip) = @_;
$ip ||= '255.255.255.255';
return $rs->search(undef,{
# NOTE: bind param list order is significant
join => ['snmp_object'],
bind => [$ip],
prefetch => 'snmp_object',
});
}
1;
lib/App/Netdisco/DB/SetOperations.pm view on Meta::CPAN
for (@operands) {
$self->throw_exception("ResultClass of ResultSets do not match!")
unless $self->result_class eq $_->result_class;
my $attrs = $_->_resolved_attrs;
$self->throw_exception('ResultSets do not all have the same selected columns!')
unless $self->_compare_arrays($as, $attrs->{as});
my ($sql, @bind) = @{${$_->as_query}};
# $sql =~ s/^\s*\((.*)\)\s*$/$1/;
$sql = q<(> . $sql . q<)>;
push @sql, $sql;
push @params, @bind;
}
my $query = q<(> . join(" $operation ", @sql). q<)>;
my $attrs = $self->_resolved_attrs;
return $self->result_source->resultset->search(undef, {
alias => $self->current_source_alias,
from => [{
$self->current_source_alias => \[ $query, @params ],
-alias => $self->current_source_alias,
lib/App/Netdisco/JobQueue/PostgreSQL.pm view on Meta::CPAN
}
sub jq_getsome {
my $num_slots = shift;
return () unless $num_slots and $num_slots > 0;
my $jobs = schema(vars->{'tenant'})->resultset('Admin');
my @returned = ();
my $tasty = schema(vars->{'tenant'})->resultset('Virtual::TastyJobs')
->search(undef,{ bind => [
setting('workers')->{'BACKEND'}, setting('job_prio')->{'high'},
setting('workers')->{'BACKEND'}, setting('workers')->{'max_deferrals'},
setting('workers')->{'retry_after'}, $num_slots,
]});
while (my $job = $tasty->next) {
if ($job->device
and not scalar grep {$job->action eq $_} @{ setting('job_targets_prefix') }) {
# need to handle device discovered since backend daemon started
lib/App/Netdisco/Util/PortMAC.pm view on Meta::CPAN
addresses supplied as array reference
=cut
sub get_port_macs {
my ($fw_mac_list) = $_[0];
my $port_macs = {};
return {} unless ref [] eq ref $fw_mac_list and @{$fw_mac_list} >= 1;
$fw_mac_list = [ grep {defined and length} @$fw_mac_list ];
my $bindarray = [ { sqlt_datatype => "array" }, $fw_mac_list ];
my $macs
= schema(vars->{'tenant'})->resultset('Virtual::PortMacs')->search({},
{ bind => [$bindarray, $bindarray], select => [ 'mac', 'ip' ], group_by => [ 'mac', 'ip' ] } );
my $cursor = $macs->cursor;
while ( my @vals = $cursor->next ) {
$port_macs->{ $vals[0] } = $vals[1];
}
return $port_macs;
}
1;
lib/App/Netdisco/Web/Auth/Provider/DBIC.pm view on Meta::CPAN
# this method returns a list of current user roles
#Â but for API with trust_remote_user, trust_x_remote_user, and no_auth
# we need to fake that there is a valid API key
my $api_requires_key =
(setting('trust_remote_user') or setting('trust_x_remote_user') or setting('no_auth'))
eq '1' ? 'false' : 'true';
return [ try {
$user->$roles->search({}, { bind => [
$api_requires_key, setting('api_token_lifetime'),
$api_requires_key, setting('api_token_lifetime'),
] })->get_column( $role_column )->all;
} ];
}
sub match_password {
my($self, $password, $user) = @_;
return unless $user;
lib/App/Netdisco/Web/Auth/Provider/DBIC.pm view on Meta::CPAN
sub match_with_ldap {
my($self, $pass, $user) = @_;
return unless setting('ldap') and ref {} eq ref setting('ldap');
my $conf = setting('ldap');
my $ldapuser = $conf->{user_string};
$ldapuser =~ s/\%USER\%?/$user/egi;
# If we can bind as anonymous or proxy user,
# search for user's distinguished name
if ($conf->{proxy_user}) {
my $user = $conf->{proxy_user};
my $pass = $conf->{proxy_pass};
my $attrs = ['distinguishedName'];
my $result = _ldap_search($ldapuser, $attrs, $user, $pass);
$ldapuser = $result->[0] if ($result->[0]);
}
# otherwise, if we can't search and aren't using AD and then construct DN
# by appending base
lib/App/Netdisco/Web/Auth/Provider/DBIC.pm view on Meta::CPAN
foreach my $server (@{$conf->{servers}}) {
my $opts = $conf->{opts} || {};
my $ldap = Net::LDAP->new($server, %$opts) or next;
my $msg = undef;
if ($conf->{tls_opts} ) {
$msg = $ldap->start_tls(%{$conf->{tls_opts}});
}
$msg = $ldap->bind($ldapuser, password => $pass);
$ldap->unbind(); # take down session
return 1 unless $msg->code();
}
return undef;
}
sub _ldap_search {
my ($filter, $attrs, $user, $pass) = @_;
my $conf = setting('ldap');
lib/App/Netdisco/Web/Auth/Provider/DBIC.pm view on Meta::CPAN
foreach my $server (@{$conf->{servers}}) {
my $opts = $conf->{opts} || {};
my $ldap = Net::LDAP->new($server, %$opts) or next;
my $msg = undef;
if ($conf->{tls_opts}) {
$msg = $ldap->start_tls(%{$conf->{tls_opts}});
}
if ( $user and $user ne 'anonymous' ) {
$msg = $ldap->bind($user, password => $pass);
}
else {
$msg = $ldap->bind();
}
$msg = $ldap->search(
base => $conf->{base},
filter => "($filter)",
attrs => $attrs,
);
$ldap->unbind(); # take down session
my $entries = [$msg->entries];
return $entries unless $msg->code();
}
return undef;
}
sub match_with_radius {
my($self, $pass, $user) = @_;
lib/App/Netdisco/Web/Device.pm view on Meta::CPAN
cookie('nd_ports-form' => $uri->query(), expires => '365 days');
};
get '/device' => require_login sub {
my $q = param('q');
my $devices = schema(vars->{'tenant'})->resultset('Device');
# we are passed either dns or ip
my $dev = $devices->search({
-or => [
\[ 'host(me.ip) = ?' => [ bind_value => $q ] ],
'me.dns' => $q,
],
});
if ($dev->count == 0) {
return redirect uri_for('/', {nosuchdevice => 1, device => $q})->path_query;
}
# if passed dns, need to check for duplicates
# and use only ip for q param, if there are duplicates.
lib/App/Netdisco/Web/GenericReport.pm view on Meta::CPAN
foreach my $report (@{setting('reports')}) {
my $r = $report->{tag};
register_report({
tag => $r,
label => $report->{label},
category => ($report->{category} || 'My Reports'),
($report->{hidden} ? (hidden => true) : ()),
provides_csv => true,
api_endpoint => true,
bind_params => [ map {ref $_ ? $_->{param} : $_} @{ $report->{bind_params} } ],
api_parameters => $report->{api_parameters},
});
get "/ajax/content/report/$r" => require_login sub {
# TODO: this should be done by creating a new Virtual Result class on
# the fly (package...) and then calling DBIC register_class on it.
my $schema = ($report->{database} || vars->{'tenant'});
my $rs = schema($schema)->resultset('Virtual::GenericReport')->result_source;
(my $query = $report->{query}) =~ s/;$//;
lib/App/Netdisco/Web/GenericReport.pm view on Meta::CPAN
$rs->view_definition($query);
$rs->remove_columns($rs->columns);
$rs->add_columns( exists $report->{query_columns}
? @{ $report->{query_columns} } : @column_order
);
my $set = schema($schema)->resultset('Virtual::GenericReport')
->search(undef, {
result_class => 'DBIx::Class::ResultClass::HashRefInflator',
( (exists $report->{bind_params})
? (bind => [map { param($_) } map {ref $_ ? $_->{param} : $_} @{ $report->{bind_params} }]) : () ),
});
@data = $set->all;
# Data Munging support...
my $compartment = Safe->new;
$config = $report; # closure for the config of this report
$compartment->share(qw/$config @data/);
$compartment->permit_only(qw/:default sort/);
lib/App/Netdisco/Web/Plugin.pm view on Meta::CPAN
if ($config->{api_endpoint}) {
(my $category_path = lc $config->{category}) =~ s/ /-/g;
my $params_copy = dclone ($config->{api_parameters} || []); #Â swagger plugin nukes it?
swagger_path {
tags => ['Reports'],
path => setting('api_base')."/report/$category_path/$tag",
description => $config->{label} .' Report',
parameters =>
($config->{api_parameters} ||
($config->{bind_params} ? [map { $_ => {} } @{ $config->{bind_params} }] : [])),
responses =>
($config->{api_responses} || { default => {} }),
},
get "/api/v1/report/$category_path/$tag" => require_role api => sub {
#Â #1360 workaround for swagger missing that False is false
foreach my $spec (pairs @{ $params_copy }) {
my ($param, $conf) = @$spec;
next unless exists $conf->{type} and $conf->{type} eq 'boolean';
next unless exists request->{'_query_params'}->{$param}
lib/App/Netdisco/Web/Plugin/Device/SNMP.pm view on Meta::CPAN
{ node => \%data, munge => $munge, mungers => \@mungers },
{ layout => 'noop' };
};
sub _get_snmp_data {
my ($ip, $base, $recurse) = @_;
my @parts = grep {length} split m/\./, $base;
my %meta = map { ('.'. join '.', @{$_->{oid_parts}}) => $_ }
schema(vars->{'tenant'})->resultset('Virtual::FilteredSNMPObject')
->search({}, { bind => [
$ip,
(scalar @parts + 1),
(scalar @parts + 1),
$base,
] })->hri->all;
my @items = map {{
id => $_,
mib => $meta{$_}->{mib}, #Â accessed via node.original.mib
leaf => $meta{$_}->{leaf}, # accessed via node.original.leaf
lib/App/Netdisco/Web/Plugin/Report/DeviceDnsMismatch.pm view on Meta::CPAN
api_endpoint => 1,
}
);
get '/ajax/content/report/devicednsmismatch' => require_login sub {
(my $suffix = '***:'. setting('domain_suffix')) =~ s|\Q(?^\Eu?|(?|g;
my @results
= schema(vars->{'tenant'})->resultset('Virtual::DeviceDnsMismatch')
->search( undef, { bind => [ $suffix, $suffix ] } )
->columns( [qw/ ip dns name location contact /] )->hri->all;
return unless scalar @results;
if ( request->is_ajax ) {
my $json = to_json( \@results );
template 'ajax/report/devicednsmismatch.tt', { results => $json }, { layout => 'noop' };
}
else {
header( 'Content-Type' => 'text/comma-separated-values' );
lib/App/Netdisco/Web/Plugin/Report/IpInventory.pm view on Meta::CPAN
}
)->hri;
my $rs_union = $rs1->union( [ $rs2, $rs3 ] );
if ( $never ) {
$subnet = NetAddr::IP::Lite->new('0.0.0.0/32') if ($subnet->bits ne 32);
my $rs4 = schema(vars->{'tenant'})->resultset('Virtual::CidrIps')->search(
undef,
{ bind => [ $subnet->cidr ],
columns => [qw( ip mac time_first time_last dns active node age vendor nbname )],
}
)->hri;
$rs_union = $rs_union->union( [$rs4] );
}
my $rs_sub = $rs_union->search(
{ ip => { '<<' => $subnet->cidr } },
{ select => [
lib/App/Netdisco/Web/Plugin/Report/PortUtilization.pm view on Meta::CPAN
],
}
);
get '/ajax/content/report/portutilization' => require_login sub {
return unless schema(vars->{'tenant'})->resultset('Device')->count;
my $age_num = param('age_num') || 3;
my $age_unit = param('age_unit') || 'months';
my @results = schema(vars->{'tenant'})->resultset('Virtual::PortUtilization')
->search(undef, { bind => [ "$age_num $age_unit", "$age_num $age_unit", "$age_num $age_unit" ] })->hri->all;
if (request->is_ajax) {
my $json = to_json (\@results);
template 'ajax/report/portutilization.tt', { results => $json }, { layout => 'noop' };
}
else {
header( 'Content-Type' => 'text/comma-separated-values' );
template 'ajax/report/portutilization_csv.tt', { results => \@results, }, { layout => 'noop' };
}
};
lib/App/Netdisco/Web/Plugin/Report/PortVLANMismatch.pm view on Meta::CPAN
label => 'Mismatched VLANs',
provides_csv => 1,
api_endpoint => 1,
}
);
get '/ajax/content/report/portvlanmismatch' => require_login sub {
return unless schema(vars->{'tenant'})->resultset('Device')->count;
my @results = schema(vars->{'tenant'})
->resultset('Virtual::PortVLANMismatch')->search({},{
bind => [ setting('sidebar_defaults')->{'device_ports'}->{'p_hide1002'}->{'default'}
? (1002, 1003, 1004, 1005) : (0, 0, 0, 0) ],
})
->hri->all;
# #Â note that the generated list is rendered without HTML escape,
# # so we MUST sanitise here with the grep
# foreach my $res (@results) {
# my @left = grep {m/^(?:n:)?\d+$/} map {s/\s//g; $_} split ',', $res->{left_vlans};
# my @right = grep {m/^(?:n:)?\d+$/} map {s/\s//g; $_} split ',', $res->{right_vlans};
#
lib/App/Netdisco/Web/Plugin/Report/SubnetUtilization.pm view on Meta::CPAN
my $agenot = param('age_invert') || '0';
my $daterange = param('daterange')
|| ('1970-01-01 to '. strftime('%Y-%m-%d', gmtime));
my ( $start, $end ) = $daterange =~ /(\d+-\d+-\d+)/gmx;
$start = $start . ' 00:00:00';
$end = $end . ' 23:59:59';
my @results = schema(vars->{'tenant'})->resultset('Virtual::SubnetUtilization')
->search(undef,{
bind => [ $subnet, $start, $end, $start, $subnet, $start, $start ],
})->hri->all;
return unless scalar @results;
if ( request->is_ajax ) {
template 'ajax/report/subnets.tt', { results => \@results }, { layout => 'noop' };
}
else {
header( 'Content-Type' => 'text/comma-separated-values' );
template 'ajax/report/subnets_csv.tt', { results => \@results }, { layout => 'noop' };
lib/App/Netdisco/Worker/Plugin/Arpwalk.pm view on Meta::CPAN
device => '255.255.255.255',
})->count();
return Status->done('Arpwalk is able to run');
});
register_worker({ phase => 'main' }, sub {
my ($job, $workerconf) = @_;
my @walk = schema(vars->{'tenant'})->resultset('Virtual::WalkJobs')
->search(undef,{ bind => [
'arpnip', 'arpnip',
setting('workers')->{'max_deferrals'},
setting('workers')->{'retry_after'},
]})->get_column('ip')->all;
jq_insert([
map {{
device => $_,
action => 'arpnip',
username => $job->username,
lib/App/Netdisco/Worker/Plugin/DiscoverAll.pm view on Meta::CPAN
device => '255.255.255.255',
})->count();
return Status->done('Discoverall is able to run');
});
register_worker({ phase => 'main' }, sub {
my ($job, $workerconf) = @_;
my @walk = schema(vars->{'tenant'})->resultset('Virtual::WalkJobs')
->search(undef,{ bind => [
'discover', 'discover',
setting('workers')->{'max_deferrals'},
setting('workers')->{'retry_after'},
]})->get_column('ip')->all;
jq_insert([
map {{
device => $_,
action => 'discover',
username => $job->username,
lib/App/Netdisco/Worker/Plugin/Macwalk.pm view on Meta::CPAN
device => '255.255.255.255',
})->count();
return Status->done('Macwalk is able to run');
});
register_worker({ phase => 'main' }, sub {
my ($job, $workerconf) = @_;
my @walk = schema(vars->{'tenant'})->resultset('Virtual::WalkJobs')
->search(undef,{ bind => [
'macsuck', 'macsuck',
setting('workers')->{'max_deferrals'},
setting('workers')->{'retry_after'},
]})->get_column('ip')->all;
jq_insert([
map {{
device => $_,
action => 'macsuck',
username => $job->username,
lib/App/Netdisco/Worker/Plugin/Nbtwalk.pm view on Meta::CPAN
device => '255.255.255.255',
})->count();
return Status->done('Nbtwalk is able to run');
});
register_worker({ phase => 'main' }, sub {
my ($job, $workerconf) = @_;
my @walk = schema(vars->{'tenant'})->resultset('Virtual::WalkJobs')
->search(undef,{ bind => [
'macsuck', 'macsuck',
setting('workers')->{'max_deferrals'},
setting('workers')->{'retry_after'},
]})->get_column('ip')->all;
jq_insert([
map {{
device => $_,
action => 'nbtstat',
username => $job->username,
lib/App/Netdisco/Worker/Plugin/Scheduler.pm view on Meta::CPAN
my ($job, $workerconf) = @_;
my $coder = JSON::PP->new->utf8(0)->allow_nonref(1)->allow_unknown(1);
my $sched = $coder->decode( $job->extra || {} );
my $action = $sched->{action} || $sched->{label};
return Status->error("Missing label of Scheduler entry")
unless $action;
my @walk = schema(vars->{'tenant'})->resultset('Virtual::WalkJobs')
->search(undef,{ bind => [
$action, ('scheduled-'. $sched->{label}),
setting('workers')->{'max_deferrals'},
setting('workers')->{'retry_after'},
]})->get_column('ip')->all;
jq_insert([
map {{
device => $_,
action => $action,
port => $sched->{port},
lib/App/Netdisco/Worker/Plugin/Snapshot.pm view on Meta::CPAN
set(net_snmp_options => {
%{ setting('net_snmp_options') },
'UseLongNames' => 1, # Return full OID tags
'UseSprintValue' => 0,
'UseEnums' => 0, # Don't use enumerated vals
'UseNumeric' => 1, # Return dotted decimal OID
});
my $snmp = App::Netdisco::Transport::SNMP->reader_for($device);
my $sess = $snmp->session();
my $from = SNMP::Varbind->new([ $extra || '.1' ]);
my $vars = [];
my $errornum = 0;
my %store = ();
debug sprintf 'bulkwalking %s from %s', $device->ip, ($extra || '.1');
($vars) = $sess->bulkwalk( 0, $snmp->{BulkRepeaters}, $from );
if ( $sess->{ErrorNum} ) {
return Status->error(
share/config.yml view on Meta::CPAN
- { ip: 'Device IP', _searchable: true }
- { devname: 'Name' }
- { model: 'Model' }
- { vendor: 'Vendor' }
- { creation: 'Date Added' }
- { os: 'Operating System' }
- { os_ver: 'OS Version' }
- { location: 'Location' }
- { contact: 'Contact' }
- { serial: 'Serial' }
bind_params:
- { param: 'since', default: '2 months' }
query: |
SELECT ip, COALESCE(NULLIF(dns,''), NULLIF(name,''), '') AS devname,
model, vendor, creation, os, os_ver, location, contact, serial
FROM device
WHERE creation > (LOCALTIMESTAMP - COALESCE(NULLIF(?,''), '2 months')::interval)
ORDER BY creation DESC
- tag: portswithmostvlans
category: Port
label: 'Ports with the most VLANs'
columns:
- { ip: 'Device IP', _searchable: true }
- { port: 'Port' }
- { vlans: 'VLAN Count' }
bind_params:
- { param: 'threshold', default: 1, type: 'number' }
query: |
SELECT ip, port, count(vlan) AS vlans
FROM device_port_vlan
GROUP BY ip, port
HAVING count(vlan) > COALESCE(NULLIF(?,''), '1') ::integer
ORDER BY vlans DESC, ip ASC, port ASC
- tag: duplicateprivatenetworks
category: IP
label: 'Duplicate Private Networks'
share/config.yml view on Meta::CPAN
OR subnet << 'fd00::/8')
GROUP BY subnet
HAVING count(subnet) > 1
ORDER BY subnet
- tag: vlansonlyuplinks
category: VLAN
label: 'VLANs Only On Uplinks'
columns:
- { ip: 'Device IP', _searchable: true }
- { vlans: 'VLAN List' }
bind_params:
- { param: 'chunk_size', default: 20, type: 'number' }
query: |
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
share/config.yml view on Meta::CPAN
ORDER BY ip, vlan) s2
) s1 GROUP BY ip, chunk
) s0 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:
- { param: 'chunk_size', default: 20, type: 'number' }
query: |
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 (
share/config.yml view on Meta::CPAN
ORDER BY ip, vlan) s2
) s0 GROUP BY ip, chunk
) s1 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:
- { param: 'chunk_size', default: 20, type: 'number' }
- { param: 'since', default: '3 months' }
query: |
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
share/public/javascripts/d3-3.5.17.min.js view on Meta::CPAN
!function(){function n(n){return n&&(n.ownerDocument||n.document||n).documentElement}function t(n){return n&&(n.ownerDocument&&n.ownerDocument.defaultView||n.document&&n||n.defaultView)}function e(n,t){return t>n?-1:n>t?1:n>=t?0:NaN}function r(n){ret...
r(),S.point=c,S.lineEnd=s}function c(n,t){i(f=n,h=t),g=M,p=x,v=b,d=_,m=w,S.point=i}function s(){u(M,x,y,b,_,w,g,p,f,v,d,m,o,t),S.lineEnd=a,a()}var f,h,g,p,v,d,m,y,M,x,b,_,w,S={point:e,lineStart:r,lineEnd:a,polygonStart:function(){t.polygonStart(),S.l...
return g}:En(r),w=u===i?function(){return p}:En(i);++y<M;)a.call(this,h=t[y],y)?(d.push([g=+x.call(this,h,y),p=+b.call(this,h,y)]),m.push([+_.call(this,h,y),+w.call(this,h,y)])):d.length&&(l(),d=[],m=[]);return d.length&&l(),v.length?v.join(""):null}...
shortDays:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],shortMonths:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov...
if(t==e.dx){for((r||s>e.dy)&&(s=e.dy);++i<a;)u=n[i],u.x=o,u.y=c,u.dy=s,o+=u.dx=Math.min(e.x+e.dx-o,s?l(u.area/s):0);u.z=!0,u.dx+=e.x+e.dx-o,e.y+=s,e.dy-=s}else{for((r||s>e.dx)&&(s=e.dx);++i<a;)u=n[i],u.x=o,u.y=c,u.dx=s,c+=u.dy=Math.min(e.y+e.dy-c,s?l...
share/public/javascripts/d3-force-network-chart.js view on Meta::CPAN
.style("left", v.dom.customizePosition.left + "px")
.style("top", v.dom.customizePosition.top + "px");
v.dom.customize.append("span")
.attr("class", "drag")
.call(v.tools.customizeDrag)
.append("span")
.attr("class", "title")
.text("Customize \"" + v.dom.containerId + "\"");
v.dom.customize.append("a")
.attr("class", "close focus")
.attr("tabindex", 1)
.text("Close")
.on("click", function() {
v.status.customize = false;
v.tools.removeCustomizeWizard();
v.tools.createCustomizeLink();
})
.on("keydown", function() {
if (d3.event.keyCode === 13) {
v.status.customize = false;
v.tools.removeCustomizeWizard();
share/public/javascripts/d3-force-network-chart.js view on Meta::CPAN
v.dom.customizeMenu = gridCell.append("span");
v.dom.customizeOptionsTable = gridCell.append("table");
for (key in v.confDefaults) {
if (v.confDefaults.hasOwnProperty(key) && v.confDefaults[key].display) {
i += 1;
row = v.dom.customizeOptionsTable.append("tr")
.attr("class", v.confDefaults[key].relation + "-related");
row.append("td")
.attr("class", "label")
.html("<a href=\"https://ogobrecht.github.io/d3-force-apex-plugin/module-API.html#." +
key + "\" target=\"github_d3_force\" tabindex=\"" + i + 100 + "\">" +
key + "</a>");
td = row.append("td");
form = td.append("select")
.attr("id", v.dom.containerId + "_" + key)
.attr("name", key)
.attr("value", v.conf[key])
.attr("tabindex", i + 1)
.classed("warning", v.confDefaults[key].internal)
.on("change", onSelectChange);
valueInOptions = false;
appendOptionsToSelect(key);
// append current value if not existing in default options
if (!valueInOptions) {
form.append("option")
.attr("value", v.conf[key])
.attr("selected", "selected")
.text(v.conf[key]);
share/public/javascripts/d3-force-network-chart.js view on Meta::CPAN
.style("vertical-align", "top")
.style("padding-left", "5px");
gridCell.append("span")
.html("Your Configuration Object<p style=\"font-size:10px;margin:0;\">" +
(v.status.apexPluginId ?
"To save your options please copy<br>this to your plugin region attributes.<br>" +
"Only non-default options are shown.</p>" :
"Use this to initialize your graph.<br>Only non-default options are shown.</p>")
);
v.dom.customizeConfObject = gridCell.append("textarea")
.attr("tabindex", i + 5)
.attr("readonly", "readonly");
gridCell.append("span").html("<br><br>Current Positions<br>");
v.dom.customizePositions = gridCell.append("textarea")
.attr("tabindex", i + 6)
.attr("readonly", "readonly")
.text((v.status.forceRunning ? "Force started - wait for end event to show positions..." :
JSON.stringify(graph.positions())));
gridCell.append("span").html("<br><br>Debug Log (descending)<br>");
v.dom.customizeLog = gridCell.append("textarea")
.attr("tabindex", i + 7)
.attr("readonly", "readonly");
gridRow = grid.append("tr");
gridCell = gridRow.append("td")
.attr("colspan", 2)
.html("Copyrights:");
gridRow = grid.append("tr");
gridCell = gridRow.append("td")
.attr("colspan", 2)
.html("<table><tr><td style=\"padding-right:20px;\">" +
"<a href=\"https://github.com/ogobrecht/d3-force-apex-plugin\" target=\"_blank\" " +
"tabindex=\"" + (i + 8) + "\">D3 Force APEX Plugin</a> (" + v.version +
")<br>Ottmar Gobrecht</td><td style=\"padding-right:20px;\">" +
"<a href=\"https://github.com/mbostock/d3\" target=\"d3js_org\" tabindex=\"" + (i + 9) +
"\">D3.js</a> (" + d3.version + ") and " +
"<a href=\"https://github.com/d3/d3-plugins/tree/master/lasso\" target=\"_blank\" tabindex=\"" +
(i + 10) + "\">D3 Lasso Plugin</a> (modified)<br>Mike Bostock" +
"</td></tr><tr><td colspan=\"3\">" +
"<a href=\"https://github.com/tinker10/D3-Labeler\" target=\"github_d3_labeler\" " +
"tabindex=\"" + (i + 11) +
"\">D3 Labeler Plugin</a> (automatic label placement using simulated annealing)" +
"<br>Evan Wang</td></tr></table>"); // https://github.com/tinker10/D3-Labeler
v.tools.createCustomizeMenu(v.status.customizeCurrentMenu);
v.tools.writeConfObjectIntoWizard();
if (v.status.customizeCurrentTabPosition) {
document.getElementById(v.status.customizeCurrentTabPosition).focus();
}
}
};
share/public/javascripts/d3-force-network-chart.js view on Meta::CPAN
if (v.status.customizeCurrentMenu === "nodes") {
v.dom.customizeMenu.append("span").style("font-weight", "bold").style("margin-left", "10px").text("NODES");
v.dom.customizeOptionsTable.selectAll("tr.node-related").classed("hidden", false);
v.dom.customizeOptionsTable.selectAll("tr.label-related,tr.link-related,tr.graph-related")
.classed("hidden", true);
} else {
v.dom.customizeMenu.append("a")
.style("font-weight", "bold")
.style("margin-left", "10px")
.text("NODES")
.attr("tabindex", 2)
.on("click", function() {
v.tools.createCustomizeMenu("nodes");
v.dom.customizeOptionsTable.selectAll("tr.node-related").classed("hidden", false);
v.dom.customizeOptionsTable.selectAll("tr.label-related,tr.link-related,tr.graph-related")
.classed("hidden", true);
})
.on("keydown", function() {
if (d3.event.keyCode === 13) {
v.tools.createCustomizeMenu("nodes");
v.dom.customizeOptionsTable.selectAll("tr.node-related").classed("hidden", false);
share/public/javascripts/d3-force-network-chart.js view on Meta::CPAN
if (v.status.customizeCurrentMenu === "labels") {
v.dom.customizeMenu.append("span").style("font-weight", "bold").style("margin-left", "10px").text("LABELS");
v.dom.customizeOptionsTable.selectAll("tr.label-related").classed("hidden", false);
v.dom.customizeOptionsTable.selectAll("tr.node-related,tr.link-related,tr.graph-related")
.classed("hidden", true);
} else {
v.dom.customizeMenu.append("a")
.style("font-weight", "bold")
.style("margin-left", "10px")
.text("LABELS")
.attr("tabindex", 2)
.on("click", function() {
v.tools.createCustomizeMenu("labels");
v.dom.customizeOptionsTable.selectAll("tr.label-related").classed("hidden", false);
v.dom.customizeOptionsTable.selectAll("tr.node-related,tr.link-related,tr.graph-related")
.classed("hidden", true);
})
.on("keydown", function() {
if (d3.event.keyCode === 13) {
v.tools.createCustomizeMenu("labels");
v.dom.customizeOptionsTable.selectAll("tr.label-related").classed("hidden", false);
share/public/javascripts/d3-force-network-chart.js view on Meta::CPAN
if (v.status.customizeCurrentMenu === "links") {
v.dom.customizeMenu.append("span").style("font-weight", "bold").style("margin-left", "10px").text("LINKS");
v.dom.customizeOptionsTable.selectAll("tr.link-related").classed("hidden", false);
v.dom.customizeOptionsTable.selectAll("tr.node-related,tr.label-related,tr.graph-related")
.classed("hidden", true);
} else {
v.dom.customizeMenu.append("a")
.style("font-weight", "bold")
.style("margin-left", "10px")
.text("LINKS")
.attr("tabindex", 3)
.on("click", function() {
v.tools.createCustomizeMenu("links");
v.dom.customizeOptionsTable.selectAll("tr.link-related").classed("hidden", false);
v.dom.customizeOptionsTable.selectAll("tr.node-related,tr.label-related,tr.graph-related")
.classed("hidden", true);
})
.on("keydown", function() {
if (d3.event.keyCode === 13) {
v.tools.createCustomizeMenu("links");
v.dom.customizeOptionsTable.selectAll("tr.link-related").classed("hidden", false);
share/public/javascripts/d3-force-network-chart.js view on Meta::CPAN
if (v.status.customizeCurrentMenu === "graph") {
v.dom.customizeMenu.append("span").style("font-weight", "bold").style("margin-left", "10px").text("GRAPH");
v.dom.customizeOptionsTable.selectAll("tr.graph-related").classed("hidden", false);
v.dom.customizeOptionsTable.selectAll("tr.node-related,tr.label-related,tr.link-related")
.classed("hidden", true);
} else {
v.dom.customizeMenu.append("a")
.style("font-weight", "bold")
.style("margin-left", "10px")
.text("GRAPH")
.attr("tabindex", 4)
.on("click", function() {
v.tools.createCustomizeMenu("graph");
v.dom.customizeOptionsTable.selectAll("tr.graph-related").classed("hidden", false);
v.dom.customizeOptionsTable.selectAll("tr.node-related,tr.label-related,tr.link-related")
.classed("hidden", true);
})
.on("keydown", function() {
if (d3.event.keyCode === 13) {
v.tools.createCustomizeMenu("graph");
v.dom.customizeOptionsTable.selectAll("tr.graph-related").classed("hidden", false);
share/public/javascripts/d3-force-network-chart.js view on Meta::CPAN
graph.version = function() {
return v.version;
};
/*******************************************************************************************************************
* Startup code - runs one time after the initialization of a new chart - example:
* var myChart = net_gobrechts_d3_force( domContainerId, pConf, apexPluginId ).start();
*/
if (v.status.apexPluginId) {
// bind to the apexrefresh event, so that this region can be refreshed by a dynamic action
apex.jQuery("#" + v.dom.containerId).bind("apexrefresh", function() {
graph.start();
});
//rerender on window resize
apex.jQuery(window).on("apexwindowresized", function() {
graph.render();
});
apex.jQuery("#t_Button_navControl").click(function() {
setTimeout(function() {
graph.render();
}, 500);
share/public/javascripts/dataTables.bootstrap.js view on Meta::CPAN
}
};
$(nPaging).addClass('pagination').append(
'<ul>'+
'<li class="prev disabled"><a href="#">← '+oLang.sPrevious+'</a></li>'+
'<li class="next disabled"><a href="#">'+oLang.sNext+' → </a></li>'+
'</ul>'
);
var els = $('a', nPaging);
$(els[0]).bind( 'click.DT', { action: "previous" }, fnClickHandler );
$(els[1]).bind( 'click.DT', { action: "next" }, fnClickHandler );
},
"fnUpdate": function ( oSettings, fnDraw ) {
var iListLength = 5;
var oPaging = oSettings.oInstance.fnPagingInfo();
var an = oSettings.aanFeatures.p;
var i, ien, j, sClass, iStart, iEnd, iHalf=Math.floor(iListLength/2);
if ( oPaging.iTotalPages < iListLength) {
iStart = 1;
share/public/javascripts/dataTables.bootstrap.js view on Meta::CPAN
for ( i=0, ien=an.length ; i<ien ; i++ ) {
// Remove the middle elements
$('li:gt(0)', an[i]).filter(':not(:last)').remove();
// Add the new list items and their event handlers
for ( j=iStart ; j<=iEnd ; j++ ) {
sClass = (j==oPaging.iPage+1) ? 'class="active"' : '';
$('<li '+sClass+'><a href="#">'+j+'</a></li>')
.insertBefore( $('li:last', an[i])[0] )
.bind('click', function (e) {
e.preventDefault();
oSettings._iDisplayStart = (parseInt($('a', this).text(),10)-1) * oPaging.iLength;
fnDraw( oSettings );
} );
}
// Add / remove disabled classes from the static elements
if ( oPaging.iPage === 0 ) {
$('li:first', an[i]).addClass('disabled');
} else {
share/public/javascripts/jquery-history.js view on Meta::CPAN
(function(a,b){"use strict";var c=a.History=a.History||{},d=a.jQuery;if(typeof c.Adapter!="undefined")throw new Error("History.js Adapter has already been loaded...");c.Adapter={bind:function(a,b,c){d(a).bind(b,c)},trigger:function(a,b,c){d(a).trigge...