refactor ::Util namespace

This commit is contained in:
Oliver Gorwits
2013-04-01 19:22:41 +01:00
parent a8dde50343
commit b95d0951f2
11 changed files with 355 additions and 281 deletions

View File

@@ -1,6 +1,7 @@
package App::Netdisco::Daemon::Worker::Interactive::DeviceActions; package App::Netdisco::Daemon::Worker::Interactive::DeviceActions;
use App::Netdisco::Util::Connect qw/snmp_connect get_device/; use App::Netdisco::Util::SNMP ':all';
use App::Netdisco::Util::Device 'get_device';
use App::Netdisco::Daemon::Worker::Interactive::Util ':all'; use App::Netdisco::Daemon::Worker::Interactive::Util ':all';
use Role::Tiny; use Role::Tiny;

View File

@@ -1,7 +1,7 @@
package App::Netdisco::Daemon::Worker::Interactive::PortActions; package App::Netdisco::Daemon::Worker::Interactive::PortActions;
use App::Netdisco::Util::Connect ':all'; use App::Netdisco::Util::SNMP ':all';
use App::Netdisco::Util::Permissions ':all'; use App::Netdisco::Util::Port ':all';
use App::Netdisco::Daemon::Worker::Interactive::Util ':all'; use App::Netdisco::Daemon::Worker::Interactive::Util ':all';
use Role::Tiny; use Role::Tiny;

View File

@@ -5,9 +5,7 @@ package App::Netdisco::Daemon::Worker::Interactive::Util;
use base 'Exporter'; use base 'Exporter';
our @EXPORT = (); our @EXPORT = ();
our @EXPORT_OK = qw/ done error /; our @EXPORT_OK = qw/ done error /;
our %EXPORT_TAGS = ( our %EXPORT_TAGS = (all => \@EXPORT_OK);
all => [qw/ done error /],
);
sub done { return ('done', shift) } sub done { return ('done', shift) }
sub error { return ('error', shift) } sub error { return ('error', shift) }

View File

@@ -3,7 +3,7 @@ package App::Netdisco::Daemon::Worker::Manager;
use Dancer qw/:moose :syntax :script/; use Dancer qw/:moose :syntax :script/;
use Dancer::Plugin::DBIC 'schema'; use Dancer::Plugin::DBIC 'schema';
use App::Netdisco::Util::DeviceProperties 'is_discoverable'; use App::Netdisco::Util::Device 'is_discoverable';
use Net::Domain 'hostfqdn'; use Net::Domain 'hostfqdn';
use Try::Tiny; use Try::Tiny;

View File

@@ -424,7 +424,7 @@ each).
The daemon obviously needs to use L<SNMP::Info> for device control. All the The daemon obviously needs to use L<SNMP::Info> for device control. All the
code for this has been factored out into the L<App::Netdisco::Util> namespace. code for this has been factored out into the L<App::Netdisco::Util> namespace.
The L<App::Netdisco::Util::Connect> package provides for the creation of The L<App::Netdisco::Util::SNMP> package provides for the creation of
SNMP::Info objects along with connection tests. So far, SNMPv3 is not SNMP::Info objects along with connection tests. So far, SNMPv3 is not
supported. To enable trace logging of the SNMP::Info object simply set the supported. To enable trace logging of the SNMP::Info object simply set the
C<INFO_TRACE> environment variable to a true value. The Connect library also C<INFO_TRACE> environment variable to a true value. The Connect library also

View File

@@ -1,153 +0,0 @@
package App::Netdisco::Util::Connect;
use Dancer qw/:syntax :script/;
use Dancer::Plugin::DBIC 'schema';
use SNMP::Info;
use Try::Tiny;
use Path::Class 'dir';
use base 'Exporter';
our @EXPORT = ();
our @EXPORT_OK = qw/
get_device get_port get_iid get_powerid snmp_connect
/;
our %EXPORT_TAGS = (
all => [qw/
get_device get_port get_iid get_powerid snmp_connect
/],
);
=head1 App::Netdisco::Util::Connect
A set of helper subroutines to support parts of the Netdisco application.
There are no default exports, however the C<:all> tag will export all
subroutines.
=head2 get_device( $ip )
Given an IP address, returns a L<DBIx::Class::Row> object for the Device in
the Netdisco database. The IP can be for any interface on the device.
Returns C<undef> if the device or interface IP is not known to Netdisco.
=cut
sub get_device {
my $ip = shift;
my $alias = schema('netdisco')->resultset('DeviceIp')
->search({alias => $ip})->first;
return if not eval { $alias->ip };
return schema('netdisco')->resultset('Device')
->find({ip => $alias->ip});
}
=head2 get_port( $device, $portname )
=cut
sub get_port {
my ($device, $portname) = @_;
# accept either ip or dbic object
$device = get_device($device)
if not ref $device;
my $port = schema('netdisco')->resultset('DevicePort')
->find({ip => $device->ip, port => $portname});
return $port;
}
=head2 get_iid( $info, $port )
=cut
sub get_iid {
my ($info, $port) = @_;
# accept either port name or dbic object
$port = $port->port if ref $port;
my $interfaces = $info->interfaces;
my %rev_if = reverse %$interfaces;
my $iid = $rev_if{$port};
return $iid;
}
=head2 get_powerid( $info, $port )
=cut
sub get_powerid {
my ($info, $port) = @_;
# accept either port name or dbic object
$port = $port->port if ref $port;
my $iid = get_iid($info, $port)
or return undef;
my $p_interfaces = $info->peth_port_ifindex;
my %rev_p_if = reverse %$p_interfaces;
my $powerid = $rev_p_if{$iid};
return $powerid;
}
=head2 snmp_connect( $ip )
Given an IP address, returns an L<SNMP::Info> instance configured for and
connected to that device. The IP can be any on the device, and the management
interface will be connected to.
Returns C<undef> if the connection fails.
=cut
sub snmp_connect {
my $ip = shift;
# get device details from db
my $device = get_device($ip)
or return ();
# TODO: really only supporing v2c at the moment
my %snmp_args = (
DestHost => $device->ip,
Version => ($device->snmp_ver || setting('snmpver') || 2),
Retries => (setting('snmpretries') || 2),
Timeout => (setting('snmptimeout') || 1000000),
MibDirs => [ _build_mibdirs() ],
AutoSpecify => 1,
IgnoreNetSNMPConf => 1,
Debug => ($ENV{INFO_TRACE} || 0),
);
my $info = undef;
my $last_comm = 0;
COMMUNITY: foreach my $c (@{ setting('community_rw') || []}) {
try {
$info = SNMP::Info->new(%snmp_args, Community => $c);
++$last_comm if (
$info
and (not defined $info->error)
and length $info->uptime
);
};
last COMMUNITY if $last_comm;
}
return $info;
}
sub _build_mibdirs {
return map { dir(setting('mibhome'), $_) }
@{ setting('mibdirs') || [] };
}
1;

View File

@@ -1,34 +1,55 @@
package App::Netdisco::Util::DeviceProperties; package App::Netdisco::Util::Device;
use Dancer qw/:syntax :script/; use Dancer qw/:syntax :script/;
use Dancer::Plugin::DBIC 'schema';
use NetAddr::IP::Lite ':lower'; use NetAddr::IP::Lite ':lower';
use App::Netdisco::Util::Connect 'get_device';
use base 'Exporter'; use base 'Exporter';
our @EXPORT = (); our @EXPORT = ();
our @EXPORT_OK = qw/ our @EXPORT_OK = qw/
get_device
is_discoverable is_discoverable
is_vlan_interface port_has_phone
/; /;
our %EXPORT_TAGS = ( our %EXPORT_TAGS = (all => \@EXPORT_OK);
all => [qw/
is_discoverable
is_vlan_interface port_has_phone
/],
);
=head1 App::Netdisco::Util::DeviceProperties; =head1 NAME
App::Netdisco::Util::Device
=head1 DESCRIPTION
A set of helper subroutines to support parts of the Netdisco application. A set of helper subroutines to support parts of the Netdisco application.
There are no default exports, however the C<:all> tag will export all There are no default exports, however the C<:all> tag will export all
subroutines. subroutines.
=head1 FUNCTIONS
=head2 get_device( $ip )
Given an IP address, returns a L<DBIx::Class::Row> object for the Device in
the Netdisco database. The IP can be for any interface on the device.
Returns C<undef> if the device or interface IP is not known to Netdisco.
=cut
sub get_device {
my $ip = shift;
my $alias = schema('netdisco')->resultset('DeviceIp')
->search({alias => $ip})->first;
return if not eval { $alias->ip };
return schema('netdisco')->resultset('Device')
->find({ip => $alias->ip});
}
=head2 is_discoverable( $ip ) =head2 is_discoverable( $ip )
Given an IP address, returns C<true> if Netdisco on this host is permitted to Given an IP address, returns C<true> if Netdisco on this host is permitted by
discover its configuration by the local configuration. the local configuration to discover the device.
The configuration items C<discover_no> and C<discover_only> are checked The configuration items C<discover_no> and C<discover_only> are checked
against the given IP. against the given IP.
@@ -65,32 +86,4 @@ sub is_discoverable {
return 1; return 1;
} }
=head2 is_vlan_interface( $port )
=cut
sub is_vlan_interface {
my $port = shift;
my $is_vlan = (($port->type and
$port->type =~ /^(53|propVirtual|l2vlan|l3ipvlan|135|136|137)$/i)
or ($port->port and $port->port =~ /vlan/i)
or ($port->name and $port->name =~ /vlan/i)) ? 1 : 0;
return $is_vlan;
}
=head2 port_has_phone( $port )
=cut
sub port_has_phone {
my $port = shift;
my $has_phone = ($port->remote_type
and $port->remote_type =~ /ip.phone/i) ? 1 : 0;
return $has_phone;
}
1; 1;

View File

@@ -1,74 +0,0 @@
package App::Netdisco::Util::Permissions;
use Dancer qw/:syntax :script/;
use Dancer::Plugin::DBIC 'schema';
use App::Netdisco::Util::DeviceProperties ':all';
use base 'Exporter';
our @EXPORT = ();
our @EXPORT_OK = qw/
vlan_reconfig_check port_reconfig_check
/;
our %EXPORT_TAGS = (
all => [qw/
vlan_reconfig_check port_reconfig_check
/],
);
=head1 App::Netdisco::Util::Permissions
A set of helper subroutines to support parts of the Netdisco application.
There are no default exports, however the C<:all> tag will export all
subroutines.
=head2 vlan_reconfig_check( $port )
=cut
sub vlan_reconfig_check {
my $port = shift;
my $ip = $port->ip;
my $name = $port->port;
my $is_vlan = is_vlan_interface($port);
# vlan (routed) interface check
return "forbidden: [$name] is a vlan interface on [$ip]"
if $is_vlan;
return "forbidden: not permitted to change native vlan"
if not setting('vlanctl');
return;
}
=head2 port_reconfig_check( $port )
=cut
sub port_reconfig_check {
my $port = shift;
my $ip = $port->ip;
my $name = $port->port;
my $has_phone = port_has_phone($port);
my $is_vlan = is_vlan_interface($port);
# uplink check
return "forbidden: port [$name] on [$ip] is an uplink"
if $port->remote_type and not $has_phone and not setting('allow_uplinks');
# phone check
return "forbidden: port [$name] on [$ip] is a phone"
if $has_phone and setting('portctl_nophones');
# vlan (routed) interface check
return "forbidden: [$name] is a vlan interface on [$ip]"
if $is_vlan and not setting('portctl_vlans');
return;
}
1;

View File

@@ -0,0 +1,225 @@
package App::Netdisco::Util::Port;
use Dancer qw/:syntax :script/;
use Dancer::Plugin::DBIC 'schema';
use App::Netdisco::Util::Connect 'get_device';
use base 'Exporter';
our @EXPORT = ();
our @EXPORT_OK = qw/
vlan_reconfig_check port_reconfig_check
get_port get_iid get_powerid
is_vlan_interface port_has_phone
/;
our %EXPORT_TAGS = (all => \@EXPORT_OK);
=head1 NAME
App::Netdisco::Util::Port
=head1 DESCRIPTION
A set of helper subroutines to support parts of the Netdisco application.
There are no default exports, however the C<:all> tag will export all
subroutines.
=head1 FUNCTIONS
=head2 vlan_reconfig_check( $port )
=over 4
=item *
Sanity check that C<$port> is not a vlan subinterface.
=item *
Permission check that C<vlanctl> is true in Netdisco config.
=back
Will return nothing if these checks pass OK.
=cut
sub vlan_reconfig_check {
my $port = shift;
my $ip = $port->ip;
my $name = $port->port;
my $is_vlan = is_vlan_interface($port);
# vlan (routed) interface check
return "forbidden: [$name] is a vlan interface on [$ip]"
if $is_vlan;
return "forbidden: not permitted to change native vlan"
if not setting('vlanctl');
return;
}
=head2 port_reconfig_check( $port )
=over 4
=item *
Permission check that C<allow_uplinks> is true in Netdisco config, if C<$port>
is an uplink.
=item *
Permission check that C<portctl_nophones> is not true in Netdisco config, if
C<$port> has a phone connected.
=item *
Permission check that C<portctl_vlans> is true if C<$port> is a vlan
subinterface.
=back
Will return nothing if these checks pass OK.
=cut
sub port_reconfig_check {
my $port = shift;
my $ip = $port->ip;
my $name = $port->port;
my $has_phone = port_has_phone($port);
my $is_vlan = is_vlan_interface($port);
# uplink check
return "forbidden: port [$name] on [$ip] is an uplink"
if $port->remote_type and not $has_phone and not setting('allow_uplinks');
# phone check
return "forbidden: port [$name] on [$ip] is a phone"
if $has_phone and setting('portctl_nophones');
# vlan (routed) interface check
return "forbidden: [$name] is a vlan interface on [$ip]"
if $is_vlan and not setting('portctl_vlans');
return;
}
=head2 get_port( $device, $portname )
Given a device IP address and a port name, returns a L<DBIx::Class::Row>
object for the Port on the Device in the Netdisco database.
The device IP can also be passed as a Device C<DBIx::Class> object.
Returns C<undef> if the device or port are not known to Netdisco.
=cut
sub get_port {
my ($device, $portname) = @_;
# accept either ip or dbic object
$device = get_device($device)
if not ref $device;
my $port = schema('netdisco')->resultset('DevicePort')
->find({ip => $device->ip, port => $portname});
return $port;
}
=head2 get_iid( $info, $port )
Given an L<SNMP::Info> instance for a device, and the name of a port, returns
the current interface table index for that port. This can be used in further
SNMP requests on attributes of the port.
Returns C<undef> if there is no such port name on the device.
=cut
sub get_iid {
my ($info, $port) = @_;
# accept either port name or dbic object
$port = $port->port if ref $port;
my $interfaces = $info->interfaces;
my %rev_if = reverse %$interfaces;
my $iid = $rev_if{$port};
return $iid;
}
=head2 get_powerid( $info, $port )
Given an L<SNMP::Info> instance for a device, and the name of a port, returns
the current PoE table index for the port. This can be used in further SNMP
requests on PoE attributes of the port.
Returns C<undef> if there is no such port name on the device.
=cut
sub get_powerid {
my ($info, $port) = @_;
# accept either port name or dbic object
$port = $port->port if ref $port;
my $iid = get_iid($info, $port)
or return undef;
my $p_interfaces = $info->peth_port_ifindex;
my %rev_p_if = reverse %$p_interfaces;
my $powerid = $rev_p_if{$iid};
return $powerid;
}
=head2 is_vlan_interface( $port )
Returns true if the C<$port> L<DBIx::Class> object represents a vlan
subinterface.
This uses simple checks on the port I<type> and I<name>, and therefore might
sometimes returns a false-negative result.
=cut
sub is_vlan_interface {
my $port = shift;
my $is_vlan = (($port->type and
$port->type =~ /^(53|propVirtual|l2vlan|l3ipvlan|135|136|137)$/i)
or ($port->port and $port->port =~ /vlan/i)
or ($port->name and $port->name =~ /vlan/i)) ? 1 : 0;
return $is_vlan;
}
=head2 port_has_phone( $port )
Returns true if the C<$port> L<DBIx::Class> object has a phone connected.
This uses a simple check on the I<type> of the remote connected device, and
therefore might sometimes return a false-negative result.
=cut
sub port_has_phone {
my $port = shift;
my $has_phone = ($port->remote_type
and $port->remote_type =~ /ip.phone/i) ? 1 : 0;
return $has_phone;
}
1;

View File

@@ -0,0 +1,82 @@
package App::Netdisco::Util::SNMP;
use Dancer qw/:syntax :script/;
use App::Netdisco::Util::Device 'get_device';
use SNMP::Info;
use Try::Tiny;
use Path::Class 'dir';
use base 'Exporter';
our @EXPORT = ();
our @EXPORT_OK = qw/
snmp_connect
/;
our %EXPORT_TAGS = (all => \@EXPORT_OK);
=head1 NAME
App::Netdisco::Util::SNMP
=head1 DESCRIPTION
A set of helper subroutines to support parts of the Netdisco application.
There are no default exports, however the C<:all> tag will export all
subroutines.
=head1 FUNCTIONS
=head2 snmp_connect( $ip )
Given an IP address, returns an L<SNMP::Info> instance configured for and
connected to that device. The IP can be any on the device, and the management
interface will be connected to.
Returns C<undef> if the connection fails.
=cut
sub snmp_connect {
my $ip = shift;
# get device details from db
my $device = get_device($ip)
or return ();
# TODO: only supporing v2c at the moment
my %snmp_args = (
DestHost => $device->ip,
Version => ($device->snmp_ver || setting('snmpver') || 2),
Retries => (setting('snmpretries') || 2),
Timeout => (setting('snmptimeout') || 1000000),
MibDirs => [ _build_mibdirs() ],
AutoSpecify => 1,
IgnoreNetSNMPConf => 1,
Debug => ($ENV{INFO_TRACE} || 0),
);
my $info = undef;
my $last_comm = 0;
COMMUNITY: foreach my $c ($device->snmp_comm, @{ setting('community_rw') || []}) {
next unless defined $c and length $c;
try {
$info = SNMP::Info->new(%snmp_args, Community => $c);
++$last_comm if (
$info
and (not defined $info->error)
and length $info->uptime
);
};
last COMMUNITY if $last_comm;
}
return $info;
}
sub _build_mibdirs {
return map { dir(setting('mibhome'), $_) }
@{ setting('mibdirs') || [] };
}
1;

View File

@@ -5,19 +5,21 @@ our @EXPORT = ();
our @EXPORT_OK = qw/ our @EXPORT_OK = qw/
sort_port sort_port
/; /;
our %EXPORT_TAGS = ( our %EXPORT_TAGS = (all => \@EXPORT_OK);
all => [qw/
sort_port
/],
);
=head1 App::Netdisco::Util::Web =head1 NAME
App::Netdisco::Util::Web
=head1 DESCRIPTION
A set of helper subroutines to support parts of the Netdisco application. A set of helper subroutines to support parts of the Netdisco application.
There are no default exports, however the C<:all> tag will export all There are no default exports, however the C<:all> tag will export all
subroutines. subroutines.
=head1 FUNCTIONS
=head2 sort_port( $a, $b ) =head2 sort_port( $a, $b )
Sort port names of various types used by device vendors. Interface is as Sort port names of various types used by device vendors. Interface is as