From 790c51b257b11fda3c8ecc1ca9cf16ede1513843 Mon Sep 17 00:00:00 2001 From: Oliver Gorwits Date: Tue, 20 Sep 2022 20:32:35 +0100 Subject: [PATCH] #925 implement ignore_deviceports and hide_deviceports --- Build.PL | 1 + lib/App/Netdisco/Configuration.pm | 24 ++++ lib/App/Netdisco/DB/Result/DeviceIp.pm | 38 +++++- lib/App/Netdisco/Transport/SNMP.pm | 2 +- lib/App/Netdisco/Util/Permission.pm | 45 ++++--- lib/App/Netdisco/Web/Plugin/Device/Ports.pm | 30 +++++ .../Netdisco/Worker/Plugin/Arpnip/Subnets.pm | 2 +- .../Worker/Plugin/Discover/CanonicalIP.pm | 6 +- .../Worker/Plugin/Discover/Neighbors.pm | 2 +- .../Worker/Plugin/Discover/Properties.pm | 116 +++++++++++------- share/config.yml | 89 +++++++++----- xt/20-checkacl.t | 28 ++++- 12 files changed, 284 insertions(+), 99 deletions(-) diff --git a/Build.PL b/Build.PL index b42a5597..3d2b0991 100644 --- a/Build.PL +++ b/Build.PL @@ -90,6 +90,7 @@ Module::Build->new( 'SNMP::Info' => '3.89', 'SQL::Abstract' => '1.85', 'SQL::Translator' => '0.11024', + 'Sub::Install' => '0', 'Sub::Util' => '1.40', 'Template' => '2.24', 'Template::AutoFilter' => '0', diff --git a/lib/App/Netdisco/Configuration.pm b/lib/App/Netdisco/Configuration.pm index 404a005e..dbe66b81 100644 --- a/lib/App/Netdisco/Configuration.pm +++ b/lib/App/Netdisco/Configuration.pm @@ -204,6 +204,30 @@ if (ref {} eq ref setting('macsuck_no_deviceport')) { } else { config->{'macsuck_no_deviceport'} ||= [] } +if (ref {} eq ref setting('hide_deviceports')) { + config->{'hide_deviceports'} = [ setting('hide_deviceports') ]; +} +else { config->{'hide_deviceports'} ||= [] } + +if (ref {} eq ref setting('ignore_deviceports')) { + config->{'ignore_deviceports'} = [ setting('ignore_deviceports') ]; +} +else { config->{'ignore_deviceports'} ||= [] } + +# copy old ignore_* into new settings +if (scalar @{ config->{'ignore_interfaces'} }) { + config->{'host_groups'}->{'__IGNORE_INTERFACES__'} + = config->{'ignore_interfaces'}; +} +if (scalar @{ config->{'ignore_interface_types'} }) { + config->{'host_groups'}->{'__IGNORE_INTERFACE_TYPES__'} + = config->{'ignore_interface_types'}; +} +if (scalar @{ config->{'ignore_notpresent_types'} }) { + config->{'host_groups'}->{'__NOTPRESENT_TYPES__'} + = config->{'ignore_notpresent_types'}; +} + # copy devices_no and devices_only into others foreach my $name (qw/devices_no devices_only discover_no macsuck_no arpnip_no nbtstat_no diff --git a/lib/App/Netdisco/DB/Result/DeviceIp.pm b/lib/App/Netdisco/DB/Result/DeviceIp.pm index b30fa2df..91fdf211 100644 --- a/lib/App/Netdisco/DB/Result/DeviceIp.pm +++ b/lib/App/Netdisco/DB/Result/DeviceIp.pm @@ -6,6 +6,8 @@ use strict; use warnings; use base 'App::Netdisco::DB::Result'; +use Sub::Install; + __PACKAGE__->table("device_ip"); __PACKAGE__->add_columns( "ip", @@ -47,9 +49,41 @@ routed port or virtual interface). =cut -__PACKAGE__->add_unique_constraint(['alias']); - __PACKAGE__->belongs_to( device_port => 'App::Netdisco::DB::Result::DevicePort', { 'foreign.port' => 'self.port', 'foreign.ip' => 'self.ip' } ); +=head2 device_port fields + +All C fields are mapped to accessors on this object. + +=cut + +foreach my $field (qw/ + descr + up + up_admin + type + duplex + duplex_admin + speed + speed_admin + name + mac + mtu + stp + remote_ip + remote_port + remote_type + remote_id + vlan + pvid + lastchange + /) { + + Sub::Install::install_sub({ + code => sub { return eval { (shift)->device_port->$field } }, + as => $field, + }); +} + 1; diff --git a/lib/App/Netdisco/Transport/SNMP.pm b/lib/App/Netdisco/Transport/SNMP.pm index 40fca87f..8979bcce 100644 --- a/lib/App/Netdisco/Transport/SNMP.pm +++ b/lib/App/Netdisco/Transport/SNMP.pm @@ -96,7 +96,7 @@ sub test_connection { my $addr = NetAddr::IP::Lite->new($ip) or return undef; # avoid renumbering to localhost loopbacks return undef if $addr->addr eq '0.0.0.0' - or check_acl_no($addr->addr, 'group:__LOCAL_ADDRESSES__'); + or check_acl_no($addr->addr, 'group:__LOOPBACK_ADDRESSES__'); my $device = schema('netdisco')->resultset('Device') ->new_result({ ip => $addr->addr }) or return undef; my $readers = $class->instance->readers or return undef; diff --git a/lib/App/Netdisco/Util/Permission.pm b/lib/App/Netdisco/Util/Permission.pm index 6d7fa1ab..f66d170d 100644 --- a/lib/App/Netdisco/Util/Permission.pm +++ b/lib/App/Netdisco/Util/Permission.pm @@ -94,25 +94,26 @@ sub check_acl { my $real_ip = $thing; if (blessed $thing) { - $real_ip = ($thing->can('alias') ? $thing->alias : ( - $thing->can('ip') ? $thing->ip : ( - $thing->can('addr') ? $thing->addr : $thing ))); + $real_ip = ( + $thing->can('alias') ? $thing->alias : ( + $thing->can('ip') ? $thing->ip : ( + $thing->can('addr') ? $thing->addr : $thing ))); } - return 0 if !defined $real_ip - or blessed $real_ip; # class we do not understand + return 0 if blessed $real_ip; # class we do not understand + $real_ip ||= ''; # valid to be empty - $config = [$config] if ref '' eq ref $config; + $config = [$config] if ref q{} eq ref $config; if (ref [] ne ref $config) { - error "error: acl is not a single item or list (cannot compare to $real_ip)"; + error "error: acl is not a single item or list (cannot compare to '$real_ip')"; return 0; } my $all = (scalar grep {$_ eq 'op:and'} @$config); # common case of using plain IP in ACL, so string compare for speed my $find = (scalar grep {not reftype $_ and $_ eq $real_ip} @$config); - return 1 if $find and not $all; + return 1 if $real_ip and $find and not $all; - my $addr = NetAddr::IP::Lite->new($real_ip) or return 0; + my $addr = NetAddr::IP::Lite->new($real_ip); my $name = undef; # only look up once, and only if qr// is used my $ropt = { retry => 1, retrans => 1, udp_timeout => 1, tcp_timeout => 2 }; my $qref = ref qr//; @@ -122,6 +123,9 @@ sub check_acl { next INLIST if !defined $item or $item eq 'op:and'; if ($qref eq ref $item) { + # if no IP addr, cannot match its dns + next INLIST unless $addr; + $name = ($name || hostname_from_ip($addr->addr, $ropt) || '!!none!!'); if ($name =~ $item) { return 1 if not $all; @@ -147,16 +151,23 @@ sub check_acl { next INLIST; } - if ($item =~ m/^([^:]+):([^:]+)$/) { + if ($item =~ m/^([^:]+):([^:]*)$/) { my $prop = $1; - my $match = $2; + my $match = $2 || ''; # if not an object, we can't do much with properties next INLIST unless blessed $thing; - # lazy version of vendor: and model: - if ($neg xor ($thing->can($prop) and defined eval { $thing->$prop } - and $thing->$prop =~ m/^$match$/)) { + # prop:val + if ($neg xor ($thing->can($prop) and + defined eval { $thing->$prop } and + ref $thing->$prop eq q{} + and $thing->$prop =~ m/^$match$/) ) { + return 1 if not $all; + } + # empty or missing property + elsif ($neg xor ($match eq q{} and + (!defined eval { $thing->$prop } or $thing->$prop eq q{})) ) { return 1 if not $all; } else { @@ -169,6 +180,9 @@ sub check_acl { my $first = $1; my $last = $2; + # if no IP addr, cannot match IP range + next INLIST unless $addr; + if ($item =~ m/:/) { next INLIST if $addr->bits != 128 and not $all; @@ -208,6 +222,9 @@ sub check_acl { # could be something in error, and IP/host is only option left next INLIST if ref $item; + # if no IP addr, cannot match IP prefix + next INLIST unless $addr; + my $ip = NetAddr::IP::Lite->new($item) or next INLIST; next INLIST if $ip->bits != $addr->bits and not $all; diff --git a/lib/App/Netdisco/Web/Plugin/Device/Ports.pm b/lib/App/Netdisco/Web/Plugin/Device/Ports.pm index afa1bebc..1eb2b4da 100644 --- a/lib/App/Netdisco/Web/Plugin/Device/Ports.pm +++ b/lib/App/Netdisco/Web/Plugin/Device/Ports.pm @@ -4,6 +4,7 @@ use Dancer ':syntax'; use Dancer::Plugin::DBIC; use Dancer::Plugin::Auth::Extensible; +use App::Netdisco::Util::Permission 'check_acl_no'; use App::Netdisco::Util::Port 'port_reconfig_check'; use App::Netdisco::Util::Web (); # for sort_port use App::Netdisco::Web::Plugin; @@ -214,6 +215,35 @@ get '/ajax/content/device/ports' => require_login sub { } } + # filter out hidden ones + if (not param('p_include_hidden')) { + my $device_ips = {}; + map { push @{ $device_ips->{$_->port} }, $_ } + $device->device_ips(undef, {prefetch => 'device_port'})->all; + + map { push @{ $device_ips->{$_->port} }, $_ } + grep { ! exists $device_ips->{$_->port} } + @results; + + foreach my $map (@{ setting('hide_deviceports')}) { + next unless ref {} eq ref $map; + + foreach my $key (sort keys %$map) { + # lhs matches device, rhs matches port + next unless check_acl_no($device, $key); + + PORT: foreach my $port (sort keys %$device_ips) { + foreach my $thing (@{ $device_ips->{$port} }) { + next unless check_acl_no($thing, $map->{$key}); + + @results = grep { $_->port ne $port } @results; + next PORT; + } + } + } + } + } + # sort ports @results = sort { &App::Netdisco::Util::Web::sort_port($a->port, $b->port) } @results; diff --git a/lib/App/Netdisco/Worker/Plugin/Arpnip/Subnets.pm b/lib/App/Netdisco/Worker/Plugin/Arpnip/Subnets.pm index 95b95567..36fd8d49 100644 --- a/lib/App/Netdisco/Worker/Plugin/Arpnip/Subnets.pm +++ b/lib/App/Netdisco/Worker/Plugin/Arpnip/Subnets.pm @@ -42,7 +42,7 @@ sub gather_subnets { my $addr = $ip->addr; next if $addr eq '0.0.0.0'; - next if check_acl_no($ip, 'group:__LOCAL_ADDRESSES__'); + next if check_acl_no($ip, 'group:__LOOPBACK_ADDRESSES__'); next if setting('ignore_private_nets') and $ip->is_rfc1918; my $netmask = $ip_netmask->{$addr} || $ip->bits(); diff --git a/lib/App/Netdisco/Worker/Plugin/Discover/CanonicalIP.pm b/lib/App/Netdisco/Worker/Plugin/Discover/CanonicalIP.pm index e01f38f1..a12f274f 100644 --- a/lib/App/Netdisco/Worker/Plugin/Discover/CanonicalIP.pm +++ b/lib/App/Netdisco/Worker/Plugin/Discover/CanonicalIP.pm @@ -5,7 +5,7 @@ use App::Netdisco::Worker::Plugin; use aliased 'App::Netdisco::Worker::Status'; use App::Netdisco::Transport::SNMP (); -use App::Netdisco::Util::Permission 'check_acl_only'; +use App::Netdisco::Util::Permission 'check_acl_no'; use App::Netdisco::Util::DNS 'ipv4_from_hostname'; use App::Netdisco::Util::Device 'is_discoverable'; use Dancer::Plugin::DBIC 'schema'; @@ -44,8 +44,8 @@ register_worker({ phase => 'main', driver => 'snmp' }, sub { foreach my $key (sort keys %$map) { # lhs matches device, rhs matches device_ip - if (check_acl_only($device, $key) - and check_acl_only($alias, $map->{$key})) { + if (check_acl_no($device, $key) + and check_acl_no($alias, $map->{$key})) { if (not is_discoverable( $alias->alias )) { debug sprintf ' [%s] device - cannot renumber to %s - not discoverable', diff --git a/lib/App/Netdisco/Worker/Plugin/Discover/Neighbors.pm b/lib/App/Netdisco/Worker/Plugin/Discover/Neighbors.pm index 19f3e9d5..4447d8d3 100644 --- a/lib/App/Netdisco/Worker/Plugin/Discover/Neighbors.pm +++ b/lib/App/Netdisco/Worker/Plugin/Discover/Neighbors.pm @@ -185,7 +185,7 @@ sub store_neighbors { # useable remote IP... if ((! $r_netaddr) or ($remote_ip eq '0.0.0.0') or - check_acl_no($remote_ip, 'group:__LOCAL_ADDRESSES__')) { + check_acl_no($remote_ip, 'group:__LOOPBACK_ADDRESSES__')) { if ($remote_id) { my $devices = schema('netdisco')->resultset('Device'); diff --git a/lib/App/Netdisco/Worker/Plugin/Discover/Properties.pm b/lib/App/Netdisco/Worker/Plugin/Discover/Properties.pm index 04384b40..a0ecc1ac 100644 --- a/lib/App/Netdisco/Worker/Plugin/Discover/Properties.pm +++ b/lib/App/Netdisco/Worker/Plugin/Discover/Properties.pm @@ -5,7 +5,7 @@ use App::Netdisco::Worker::Plugin; use aliased 'App::Netdisco::Worker::Status'; use App::Netdisco::Transport::SNMP (); -use App::Netdisco::Util::Permission qw/check_acl_no check_acl_only/; +use App::Netdisco::Util::Permission 'check_acl_no'; use App::Netdisco::Util::FastResolver 'hostnames_resolve_async'; use App::Netdisco::Util::Device 'get_device'; use App::Netdisco::Util::DNS 'hostname_from_ip'; @@ -66,7 +66,7 @@ register_worker({ phase => 'early', driver => 'snmp' }, sub { my $protect = setting('snmp_field_protection')->{'device'} || {}; my %dirty = $device->get_dirty_columns; foreach my $field (keys %dirty) { - next unless check_acl_only($ip, $protect->{$field}); + next unless check_acl_no($ip, $protect->{$field}); if (!defined $dirty{$field} or $dirty{$field} eq '') { return $job->cancel("discover cancelled: $ip failed to return valid $field"); } @@ -101,7 +101,7 @@ register_worker({ phase => 'early', driver => 'snmp' }, sub { }); } - return Status->info(" [$device] device - OK to continue discover"); + return Status->info(" [$device] device - OK to continue discover (not a duplicate)"); }); register_worker({ phase => 'early', driver => 'snmp' }, sub { @@ -113,7 +113,7 @@ register_worker({ phase => 'early', driver => 'snmp' }, sub { my $snmp = App::Netdisco::Transport::SNMP->reader_for($device) or return Status->defer("discover failed: could not SNMP connect to $device"); - my $pass = Status->info(" [$device] device - OK to continue discover"); + my $pass = Status->info(" [$device] device - OK to continue discover (valid interfaces)"); my $interfaces = $snmp->interfaces; # OK if no interfaces @@ -172,6 +172,8 @@ register_worker({ phase => 'early', driver => 'snmp' }, sub { }); }); + +# NOTE must come after the IP Aliases gathering for ignore ACLs to work register_worker({ phase => 'early', driver => 'snmp' }, sub { my ($job, $workerconf) = @_; @@ -180,6 +182,13 @@ register_worker({ phase => 'early', driver => 'snmp' }, sub { my $snmp = App::Netdisco::Transport::SNMP->reader_for($device) or return Status->defer("discover failed: could not SNMP connect to $device"); + # gather device_ips for use in ACLs later + my $device_ips = {}; + foreach my $dip ($device->device_ips()->all) { + next unless defined $dip->port and $dip->port; + push @{ $device_ips->{ $dip->port } }, $dip; + } + my $interfaces = $snmp->interfaces; my $i_type = $snmp->i_type; my $i_ignore = $snmp->i_ignore; @@ -222,41 +231,20 @@ register_worker({ phase => 'early', driver => 'snmp' }, sub { } # build device interfaces suitable for DBIC - my %interfaces; - foreach my $entry (keys %$interfaces) { + my %deviceports; + PORT: foreach my $entry (keys %$interfaces) { my $port = $interfaces->{$entry}; if (not $port) { debug sprintf ' [%s] interfaces - ignoring %s (no port mapping)', $device->ip, $entry; - next; - } - - if (scalar grep {$port =~ m/^$_$/} @{setting('ignore_interfaces') || []}) { - debug sprintf - ' [%s] interfaces - ignoring %s (%s) (config:ignore_interfaces)', - $device->ip, $entry, $port; - next; + next PORT; } if (exists $i_ignore->{$entry}) { debug sprintf ' [%s] interfaces - ignoring %s (%s) (%s) (SNMP::Info::i_ignore)', $device->ip, $entry, $port, ($i_type->{$entry} || ''); - next; - } - - # Skip interfaces by type filter - if (defined $i_type->{$entry} and (scalar grep {$i_type->{$entry} =~ m/^$_$/} @{setting('ignore_interface_types') || []})) { - debug sprintf ' [%s] interfaces - ignoring %s (%s) (%s) (config:ignore_interface_types)', - $device->ip, $entry, $port, $i_type->{$entry}; - next; - } - - # Skip interfaces which are 'notPresent' and match the notpresent type filter - if (defined $i_up->{$entry} and defined $i_type->{$entry} and $i_up->{$entry} eq 'notPresent' and (scalar grep {$i_type->{$entry} =~ m/^$_$/} @{setting('ignore_notpresent_types') || []}) ) { - debug sprintf ' [%s] interfaces - ignoring %s (%s) (%s) (config:ignore_notpresent_types)', - $device->ip, $entry, $port, $i_up->{$entry}; - next; + next PORT; } my $lc = $i_lastchange->{$entry} || 0; @@ -285,7 +273,9 @@ register_worker({ phase => 'early', driver => 'snmp' }, sub { } } - $interfaces{$port} = { + # create a DBIx::Class row for this port which can be used to test ACLs + # also include the Device IP alias if we have one for L3 interfaces + $deviceports{$port} = { port => $port, descr => $i_descr->{$entry}, up => $i_up->{$entry}, @@ -304,19 +294,57 @@ register_worker({ phase => 'early', driver => 'snmp' }, sub { has_subinterfaces => 'false', is_master => 'false', slave_of => undef, - lastchange => $lc, + lastchange => $lc, }; } - # must do this after building %interfaces so that we can set is_master + if (scalar @{ setting('ignore_deviceports') }) { + foreach my $port (keys %$device_ips) { + if (!exists $deviceports{$port}) { + delete $device_ips->{$port}; + next; + } + foreach my $dip (@{ $device_ips->{$port} }) { + $dip->set_inflated_columns({ device_port => $deviceports{$port} }); + } + } + foreach my $port (keys %deviceports) { + next if exists $device_ips->{$port}; + push @{ $device_ips->{$port} }, + schema('netdisco')->resultset('DevicePort') + ->new_result( $deviceports{$port} ); + } + + foreach my $map (@{ setting('ignore_deviceports')}) { + next unless ref {} eq ref $map; + + foreach my $key (sort keys %$map) { + # lhs matches device, rhs matches port + next unless check_acl_no($device, $key); + + PORT: foreach my $port (sort keys %$device_ips) { + foreach my $thing (@{ $device_ips->{$port} }) { + next unless check_acl_no($thing, $map->{$key}); + + debug sprintf ' [%s] interfaces - ignoring %s (config:ignore_deviceports)', + $device->ip, $port; + delete $deviceports{$port}; + next PORT; + } + } + } + } + } + + # must do this after building %deviceports so that we can set is_master foreach my $sidx (keys %$agg_ports) { my $slave = $interfaces->{$sidx} or next; next unless defined $agg_ports->{$sidx}; # slave without a master?! my $master = $interfaces->{ $agg_ports->{$sidx} } or next; - next unless exists $interfaces{$slave} and exists $interfaces{$master}; + next unless exists $deviceports{$slave} and exists $deviceports{$master}; - $interfaces{$slave}->{slave_of} = $master; - $interfaces{$master}->{is_master} = 'true'; + $deviceports{$slave}->{slave_of} = $master; + $deviceports{$master}->{is_master} = 'true'; } # also for VLAN subinterfaces @@ -325,27 +353,29 @@ register_worker({ phase => 'early', driver => 'snmp' }, sub { # parent without subinterfaces? next unless defined $i_subs->{$pidx} and ref [] eq ref $i_subs->{$pidx} - and scalar @{ $i_subs->{$pidx} }; + and scalar @{ $i_subs->{$pidx} } + and exists $deviceports{$parent}; - $interfaces{$parent}->{has_subinterfaces} = 'true'; + $deviceports{$parent}->{has_subinterfaces} = 'true'; foreach my $sidx (@{ $i_subs->{$pidx} }) { my $sub = $interfaces->{$sidx} or next; - $interfaces{$sub}->{slave_of} = $parent; + next unless exists $deviceports{$sub}; + $deviceports{$sub}->{slave_of} = $parent; } } # support for Hooks - vars->{'hook_data'}->{'ports'} = [values %interfaces]; + vars->{'hook_data'}->{'ports'} = [values %deviceports]; schema('netdisco')->resultset('DevicePort')->txn_do_locked(sub { my $gone = $device->ports->delete({keep_nodes => 1}); debug sprintf ' [%s] interfaces - removed %d interfaces', $device->ip, $gone; $device->update_or_insert(undef, {for => 'update'}); - $device->ports->populate([values %interfaces]); + $device->ports->populate([values %deviceports]); return Status->info(sprintf ' [%s] interfaces - added %d new interfaces', - $device->ip, scalar values %interfaces); + $device->ip, scalar values %deviceports); }); }); @@ -397,7 +427,7 @@ sub _get_ipv4_aliases { my $addr = $ip->addr; next if $addr eq '0.0.0.0'; - next if check_acl_no($ip, 'group:__LOCAL_ADDRESSES__'); + next if check_acl_no($ip, 'group:__LOOPBACK_ADDRESSES__'); next if setting('ignore_private_nets') and $ip->is_rfc1918; my $iid = $ip_index->{$addr}; @@ -450,7 +480,7 @@ sub _get_ipv6_aliases { my $addr = $ip->addr; next if $addr eq '::0'; - next if check_acl_no($ip, 'group:__LOCAL_ADDRESSES__'); + next if check_acl_no($ip, 'group:__LOOPBACK_ADDRESSES__'); my $port = $interfaces->{ $ipv6_index->{$iid} }; my $subnet = $ipv6_pfxlen->{$iid} diff --git a/share/config.yml b/share/config.yml index c38a7f77..773755c7 100644 --- a/share/config.yml +++ b/share/config.yml @@ -147,6 +147,7 @@ sidebar_defaults: age_unit: { default: months } p_vlan_names: { label: 'Use VLAN Names', default: null, idx: 0 } p_hide1002: { label: 'Hide VLAN 1002-1005', default: null, idx: 1 } + p_include_hidden: { label: 'Include Hidden Ports', default: null, idx: 2 } device_netmap: showips: { default: null } showspeed: { default: null } @@ -209,6 +210,7 @@ check_userlog: false devport_vlan_limit: 150 login_logo: "" defanged_admin: 'admin' +hide_deviceports: [] # ------------- # NETDISCO CORE @@ -220,9 +222,51 @@ host_groups: __ANY__: - '0.0.0.0/0' - '::/0' - __LOCAL_ADDRESSES__: + __LOOPBACK_ADDRESSES__: - '::1' - '127.0.0.0/8' + __LOCAL_ADDRESSES__: + - '169.254.0.0/16' + - 'fe80::/10' + __LOOPBACK_WITH_NO_IP__: + - 'op:and' + - 'alias:' + - 'type:softwareLoopback' + __IGNORE_INTERFACES__: + - 'port:EOBC' + - 'port:unrouted VLAN(?: \d+)?' + - 'port:StackPort' + - 'port:Control Plane Interface' + - 'port:SPAN (S|R)P Interface' + - 'port:StackSub-.*' + - 'port:StackPort\d+' + - 'port:netflow' + - 'port:Vlan\d+-mpls layer' + - 'port:BRI\S+-Bearer Channel' + - 'port:BRI\S+-Physical' + - 'port:BRI\S+-Signalling' + - 'port:BRI\S+-Signaling' + - 'port:Embedded-Service-Engine\d+\/\d+' + - 'port:Virtual-Template\d+' + - 'port:Virtual-Access\d+' + - 'port:(E|T)\d \d\/\d\/\d' + - 'port:InLoopback0' + - 'port:NULL\d' + - 'port:Register-Tunnel\d' + - 'port:Blade-Aggregation\d' + - 'port:M-GigabitEthernet\d\/\d\/\d' + - 'port:Ethernet(?:-| )QOS Packet Scheduler' + - 'port:Ethernet(?:-| )WFP (?:802\.3|Native) MAC Layer Lightweight Filter' + - 'port:ii\d\/\d\/\d+' + __IGNORE_INTERFACE_TYPES__: [] + __NOTPRESENT_TYPES__: + - 'type:ethernetCsmacd' + - 'type:tunnel' + - 'type:ieee8023adLag' + __IGNORE_NOTPRESENT_TYPES__: + - 'op:and' + - 'up:notPresent' + - 'group:__NOTPRESENT_TYPES__' host_group_displaynames: {} device_identity: [] community: [] @@ -290,37 +334,18 @@ expire_userlog: 365 expire_nodeip_freshness: null store_wireless_clients: true store_modules: true -ignore_interfaces: - - 'EOBC' - - 'unrouted VLAN(?: \d+)?' - - 'StackPort' - - 'Control Plane Interface' - - 'SPAN (S|R)P Interface' - - 'StackSub-.*' - - 'StackPort\d+' - - 'netflow' - - 'Vlan\d+-mpls layer' - - 'BRI\S+-Bearer Channel' - - 'BRI\S+-Physical' - - 'BRI\S+-Signalling' - - 'BRI\S+-Signaling' - - 'Embedded-Service-Engine\d+\/\d+' - - 'Virtual-Template\d+' - - 'Virtual-Access\d+' - - '(E|T)\d \d\/\d\/\d' - - 'InLoopback0' - - 'NULL\d' - - 'Register-Tunnel\d' - - 'Blade-Aggregation\d' - - 'M-GigabitEthernet\d\/\d\/\d' - - 'Ethernet(?:-| )QOS Packet Scheduler' - - 'Ethernet(?:-| )WFP (?:802\.3|Native) MAC Layer Lightweight Filter' - - 'ii\d\/\d\/\d+' +ignore_deviceports: + 'group:__ANY__': + - 'group:__IGNORE_INTERFACES__' + - 'group:__IGNORE_INTERFACE_TYPES__' + - 'group:__IGNORE_NOTPRESENT_TYPES__' + 'vendor:juniper': + - 'subnet:128\.0\.0\.0/2' + - 'port:.+\.1638\d' + - 'port:.+\.3276\d' +ignore_interfaces: [] ignore_interface_types: [] -ignore_notpresent_types: - - 'ethernetCsmacd' - - 'tunnel' - - 'ieee8023adLag' +ignore_notpresent_types: [] ignore_private_nets: false reverse_sysname: false phone_capabilities: @@ -357,7 +382,7 @@ jobs_qdepth: 50 dns: max_outstanding: 50 hosts_file: '/etc/hosts' - no: ['group:__LOCAL_ADDRESSES__','169.254.0.0/16','fe80::/10'] + no: ['group:__LOCAL_ADDRESSES__','group:__LOOPBACK_ADDRESSES__'] hooks: [] diff --git a/xt/20-checkacl.t b/xt/20-checkacl.t index 23f07c2a..86f8268b 100644 --- a/xt/20-checkacl.t +++ b/xt/20-checkacl.t @@ -32,6 +32,8 @@ my @conf = ( 'op:and', # 20 'group:groupreftest', # 21 '!group:groupreftest', # 22 + + '192.0.2.1', #23 ); # name, ipv4, ipv6, v4 prefix, v6 prefix @@ -109,7 +111,29 @@ ok(check_acl('localhost',$conf[0]), 'scalar promoted'); ok(check_acl('localhost',$conf[1]), 'not scalar promoted'); is(check_acl('www.microsoft.com',$conf[0]), 0, 'failed scalar promoted'); -# device property -# negated device property +use App::Netdisco::DB; +my $dip = App::Netdisco::DB->resultset('DeviceIp')->new_result({ + ip => '127.0.0.1', + port => 'TenGigabitEthernet1/10', + alias => '192.0.2.1', + device_port => + App::Netdisco::DB->resultset('DevicePort')->new_result({ + ip => '127.0.0.1', + port => 'TenGigabitEthernet1/10', + type => 'l3ipvlan', + }) +}); + +# device properties +ok(check_acl($dip, [$conf[23]]), 'instance anon property deviceport:alias'); +ok(check_acl($dip, ['ip:'.$conf[2]]), 'instance named property deviceport:ip'); +ok(check_acl($dip, ['!ip:'. $conf[23]]), 'negated instance named property deviceport:ip'); +is(check_acl($dip, ['port:'.$conf[2]]), 0, 'failed instance named property deviceport:ip'); +ok(check_acl($dip, ['port:.*GigabitEthernet.*']), 'instance named property regexp deviceport:port'); + +ok(check_acl($dip, ['type:l3ipvlan']), 'related item field match'); +ok(check_acl($dip, ['remote_ip:']), 'related item field empty'); +ok(check_acl($dip, ['!type:']), 'related item field not empty'); +is(check_acl($dip, ['foobar:xyz']), 0, 'unknown property'); done_testing;