relocate repo files so ND2 is the only code

This commit is contained in:
Oliver Gorwits
2017-04-14 23:08:55 +01:00
parent 9a016ea6ba
commit d23b32500f
469 changed files with 0 additions and 6920 deletions

View File

@@ -0,0 +1,45 @@
package App::Netdisco::DB::ResultSet::Admin;
use base 'App::Netdisco::DB::ResultSet';
use strict;
use warnings;
__PACKAGE__->load_components(qw/
+App::Netdisco::DB::ExplicitLocking
/);
=head1 ADDITIONAL METHODS
=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
=item entered_stamp
=item started_stamp
=item finished_stamp
=back
=cut
sub with_times {
my ($rs, $cond, $attrs) = @_;
return $rs
->search_rs($cond, $attrs)
->search({},
{
'+columns' => {
entered_stamp => \"to_char(entered, 'YYYY-MM-DD HH24:MI')",
started_stamp => \"to_char(started, 'YYYY-MM-DD HH24:MI')",
finished_stamp => \"to_char(finished, 'YYYY-MM-DD HH24:MI')",
},
});
}
1;

View File

@@ -0,0 +1,612 @@
package App::Netdisco::DB::ResultSet::Device;
use base 'App::Netdisco::DB::ResultSet';
use strict;
use warnings;
use NetAddr::IP::Lite ':lower';
=head1 ADDITIONAL METHODS
=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
=item uptime_age
=item last_discover_stamp
=item last_macsuck_stamp
=item last_arpnip_stamp
=back
=cut
sub with_times {
my ($rs, $cond, $attrs) = @_;
return $rs
->search_rs($cond, $attrs)
->search({},
{
'+columns' => {
uptime_age => \("replace(age(timestamp 'epoch' + uptime / 100 * interval '1 second', "
."timestamp '1970-01-01 00:00:00-00')::text, 'mon', 'month')"),
last_discover_stamp => \"to_char(last_discover, 'YYYY-MM-DD HH24:MI')",
last_macsuck_stamp => \"to_char(last_macsuck, 'YYYY-MM-DD HH24:MI')",
last_arpnip_stamp => \"to_char(last_arpnip, 'YYYY-MM-DD HH24:MI')",
since_last_discover => \"extract(epoch from (age(now(), last_discover)))",
since_last_macsuck => \"extract(epoch from (age(now(), last_macsuck)))",
since_last_arpnip => \"extract(epoch from (age(now(), last_arpnip)))",
},
});
}
=head2 search_aliases( {$name or $ip or $prefix}, \%options? )
Tries to find devices in Netdisco which have an identity corresponding to
C<$name>, C<$ip> or C<$prefix>.
The search is across all aliases of the device, as well as its "root IP"
identity. Note that this search will try B<not> to use DNS, in case the current
name for an IP does not correspond to the data within Netdisco.
Passing a zero value to the C<partial> key of the C<options> hashref will
prevent partial matching of a host name. Otherwise the default is to perform
a partial, case-insensitive search on the host name fields.
=cut
sub search_aliases {
my ($rs, $q, $options) = @_;
$q ||= '255.255.255.255'; # hack to return empty resultset on error
$options ||= {};
$options->{partial} = 1 if !defined $options->{partial};
# rough approximation of IP addresses (v4 in v6 not supported).
# this helps us avoid triggering any DNS.
my $by_ip = ($q =~ m{^(?:[.0-9/]+|[:0-9a-f/]+)$}i) ? 1 : 0;
my $clause;
if ($by_ip) {
my $ip = NetAddr::IP::Lite->new($q)
or return undef; # could be a MAC address!
$clause = [
'me.ip' => { '<<=' => $ip->cidr },
'device_ips.alias' => { '<<=' => $ip->cidr },
];
}
else {
$q = "\%$q\%" if ($options->{partial} and $q !~ m/\%/);
$clause = [
'me.name' => { '-ilike' => $q },
'me.dns' => { '-ilike' => $q },
'device_ips.dns' => { '-ilike' => $q },
];
}
return $rs->search(
{
-or => $clause,
},
{
order_by => [qw/ me.dns me.ip /],
join => 'device_ips',
distinct => 1,
}
);
}
=head2 search_for_device( $name or $ip or $prefix )
This is a wrapper for C<search_aliases> which:
=over 4
=item *
Disables partial matching on host names
=item *
Returns only the first result of any found devices
=back
If not matching devices are found, C<undef> is returned.
=cut
sub search_for_device {
my ($rs, $q, $options) = @_;
$options ||= {};
$options->{partial} = 0;
return $rs->search_aliases($q, $options)->first();
}
=head2 search_by_field( \%cond, \%attrs? )
This variant of the standard C<search()> method returns a ResultSet of Device
entries. It is written to support web forms which accept fields that match and
locate Devices in the database.
The hashref parameter should contain fields from the Device table which will
be intelligently used in a search query.
In addition, you can provide the key C<matchall> which, given a True or False
value, controls whether fields must all match or whether any can match, to
select a row.
Supported keys:
=over 4
=item matchall
If a True value, fields must all match to return a given row of the Device
table, otherwise any field matching will cause the row to be included in
results.
=item name
Can match the C<name> field as a substring.
=item location
Can match the C<location> field as a substring.
=item description
Can match the C<description> field as a substring (usually this field contains
a description of the vendor operating system).
=item model
Will match exactly the C<model> field.
=item os
Will match exactly the C<os> field, which is the operating sytem.
=item os_ver
Will match exactly the C<os_ver> field, which is the operating sytem software version.
=item vendor
Will match exactly the C<vendor> (manufacturer).
=item dns
Can match any of the Device IP address aliases as a substring.
=item ip
Can be a string IP or a NetAddr::IP object, either way being treated as an
IPv4 or IPv6 prefix within which the device must have one IP address alias.
=back
=cut
sub search_by_field {
my ($rs, $p, $attrs) = @_;
die "condition parameter to search_by_field must be hashref\n"
if ref {} ne ref $p or 0 == scalar keys %$p;
my $op = $p->{matchall} ? '-and' : '-or';
# this is a bit of an inelegant trick to catch junk data entry,
# whilst avoiding returning *all* entries in the table
if ($p->{ip} and 'NetAddr::IP::Lite' ne ref $p->{ip}) {
$p->{ip} = ( NetAddr::IP::Lite->new($p->{ip})
|| NetAddr::IP::Lite->new('255.255.255.255') );
}
# For Search on Layers
my @layer_search = ( '_', '_', '_', '_', '_', '_', '_' );
# @layer_search is computer indexed, left->right
my $layers = $p->{layers};
if ( defined $layers && ref $layers ) {
foreach my $layer (@$layers) {
next unless defined $layer and length($layer);
next if ( $layer < 1 || $layer > 7 );
$layer_search[ $layer - 1 ] = 1;
}
}
elsif ( defined $layers ) {
$layer_search[ $layers - 1 ] = 1;
}
# the database field is in order 87654321
my $layer_string = join( '', reverse @layer_search );
return $rs
->search_rs({}, $attrs)
->search({
$op => [
($p->{name} ? ('me.name' =>
{ '-ilike' => "\%$p->{name}\%" }) : ()),
($p->{location} ? ('me.location' =>
{ '-ilike' => "\%$p->{location}\%" }) : ()),
($p->{description} ? ('me.description' =>
{ '-ilike' => "\%$p->{description}\%" }) : ()),
($p->{layers} ? ('me.layers' =>
{ '-ilike' => "\%$layer_string" }) : ()),
($p->{model} ? ('me.model' =>
{ '-in' => $p->{model} }) : ()),
($p->{os} ? ('me.os' =>
{ '-in' => $p->{os} }) : ()),
($p->{os_ver} ? ('me.os_ver' =>
{ '-in' => $p->{os_ver} }) : ()),
($p->{vendor} ? ('me.vendor' =>
{ '-in' => $p->{vendor} }) : ()),
($p->{dns} ? (
-or => [
'me.dns' => { '-ilike' => "\%$p->{dns}\%" },
'device_ips.dns' => { '-ilike' => "\%$p->{dns}\%" },
]) : ()),
($p->{ip} ? (
-or => [
'me.ip' => { '<<=' => $p->{ip}->cidr },
'device_ips.alias' => { '<<=' => $p->{ip}->cidr },
]) : ()),
],
},
{
order_by => [qw/ me.dns me.ip /],
(($p->{dns} or $p->{ip}) ? (
join => 'device_ips',
distinct => 1,
) : ()),
}
);
}
=head2 search_fuzzy( $value )
This method accepts a single parameter only and returns a ResultSet of rows
from the Device table where one field matches the passed parameter.
The following fields are inspected for a match:
=over 4
=item contact
=item serial
=item location
=item name
=item description
=item dns
=item ip (including aliases)
=back
=cut
sub search_fuzzy {
my ($rs, $q) = @_;
die "missing param to search_fuzzy\n"
unless $q;
$q = "\%$q\%" if $q !~ m/\%/;
# basic IP check is a string match
my $ip_clause = [
'me.ip::text' => { '-ilike' => $q },
'device_ips.alias::text' => { '-ilike' => $q },
];
# but also allow prefix search
(my $qc = $q) =~ s/\%//g;
if (my $ip = NetAddr::IP::Lite->new($qc)) {
$ip_clause = [
'me.ip' => { '<<=' => $ip->cidr },
'device_ips.alias' => { '<<=' => $ip->cidr },
];
}
return $rs->search(
{
-or => [
'me.contact' => { '-ilike' => $q },
'me.serial' => { '-ilike' => $q },
'me.location' => { '-ilike' => $q },
'me.name' => { '-ilike' => $q },
'me.description' => { '-ilike' => $q },
-or => [
'me.dns' => { '-ilike' => $q },
'device_ips.dns' => { '-ilike' => $q },
],
-or => $ip_clause,
],
},
{
order_by => [qw/ me.dns me.ip /],
join => 'device_ips',
distinct => 1,
}
);
}
=head2 carrying_vlan( \%cond, \%attrs? )
my $set = $rs->carrying_vlan({ vlan => 123 });
Like C<search()>, this returns a ResultSet of matching rows from the Device
table.
The returned devices each are aware of the given Vlan.
=over 4
=item *
The C<cond> parameter must be a hashref containing a key C<vlan> with
the value to search for.
=item *
Results are ordered by the Device DNS and IP fields.
=item *
Related rows from the C<device_vlan> table will be prefetched.
=back
=cut
sub carrying_vlan {
my ($rs, $cond, $attrs) = @_;
die "vlan number required for carrying_vlan\n"
if ref {} ne ref $cond or !exists $cond->{vlan};
return $rs
->search_rs({ 'vlans.vlan' => $cond->{vlan} },
{
order_by => [qw/ me.dns me.ip /],
columns => [
'me.ip', 'me.dns',
'me.model', 'me.os',
'me.vendor', 'vlans.vlan',
'vlans.description'
],
join => 'vlans'
})
->search({}, $attrs);
}
=head2 carrying_vlan_name( \%cond, \%attrs? )
my $set = $rs->carrying_vlan_name({ name => 'Branch Office' });
Like C<search()>, this returns a ResultSet of matching rows from the Device
table.
The returned devices each are aware of the named Vlan.
=over 4
=item *
The C<cond> parameter must be a hashref containing a key C<name> with
the value to search for. The value may optionally include SQL wildcard
characters.
=item *
Results are ordered by the Device DNS and IP fields.
=item *
Related rows from the C<device_vlan> table will be prefetched.
=back
=cut
sub carrying_vlan_name {
my ($rs, $cond, $attrs) = @_;
die "vlan name required for carrying_vlan_name\n"
if ref {} ne ref $cond or !exists $cond->{name};
$cond->{'vlans.description'} = { '-ilike' => delete $cond->{name} };
return $rs
->search_rs({}, {
order_by => [qw/ me.dns me.ip /],
columns => [
'me.ip', 'me.dns',
'me.model', 'me.os',
'me.vendor', 'vlans.vlan',
'vlans.description'
],
join => 'vlans'
})
->search($cond, $attrs);
}
=head2 has_layer( $layer )
my $rset = $rs->has_layer(3);
This predefined C<search()> returns a ResultSet of matching rows from the
Device table of devices advertising support of the supplied layer in the
OSI Model.
=over 4
=item *
The C<layer> parameter must be an integer between 1 and 7.
=cut
sub has_layer {
my ( $rs, $layer ) = @_;
die "layer required and must be between 1 and 7\n"
if !$layer || $layer < 1 || $layer > 7;
return $rs->search_rs( \[ 'substring(layers,9-?, 1)::int = 1', $layer ] );
}
=back
=head2 get_models
Returns a sorted list of Device models with the following columns only:
=over 4
=item vendor
=item model
=item count
=back
Where C<count> is the number of instances of that Vendor's Model in the
Netdisco database.
=cut
sub get_models {
my $rs = shift;
return $rs->search({}, {
select => [ 'vendor', 'model', { count => 'ip' } ],
as => [qw/vendor model count/],
group_by => [qw/vendor model/],
order_by => [{-asc => 'vendor'}, {-asc => 'model'}],
})
}
=head2 get_releases
Returns a sorted list of Device OS releases with the following columns only:
=over 4
=item os
=item os_ver
=item count
=back
Where C<count> is the number of devices running that OS release in the
Netdisco database.
=cut
sub get_releases {
my $rs = shift;
return $rs->search({}, {
select => [ 'os', 'os_ver', { count => 'ip' } ],
as => [qw/os os_ver count/],
group_by => [qw/os os_ver/],
order_by => [{-asc => 'os'}, {-asc => 'os_ver'}],
})
}
=head2 with_port_count
This is a modifier for any C<search()> which
will add the following additional synthesized column to the result set:
=over 4
=item port_count
=back
=cut
sub with_port_count {
my ($rs, $cond, $attrs) = @_;
return $rs
->search_rs($cond, $attrs)
->search({},
{
'+columns' => {
port_count =>
$rs->result_source->schema->resultset('DevicePort')
->search(
{
'dp.ip' => { -ident => 'me.ip' },
'dp.type' => { '!=' => 'propVirtual' },
},
{ alias => 'dp' }
)->count_rs->as_query,
},
});
}
=head1 SPECIAL METHODS
=head2 delete( \%options? )
Overrides the built-in L<DBIx::Class> delete method to more efficiently
handle the removal or archiving of nodes.
=cut
sub delete {
my $self = shift;
my $schema = $self->result_source->schema;
my $devices = $self->search(undef, { columns => 'ip' });
foreach my $set (qw/
DeviceIp
DeviceVlan
DevicePower
DeviceModule
Community
/) {
$schema->resultset($set)->search(
{ ip => { '-in' => $devices->as_query } },
)->delete;
}
$schema->resultset('Admin')->search({
device => { '-in' => $devices->as_query },
})->delete;
$schema->resultset('Topology')->search({
-or => [
{ dev1 => { '-in' => $devices->as_query } },
{ dev2 => { '-in' => $devices->as_query } },
],
})->delete;
$schema->resultset('DevicePort')->search(
{ ip => { '-in' => $devices->as_query } },
)->delete(@_);
# now let DBIC do its thing
return $self->next::method();
}
1;

View File

@@ -0,0 +1,107 @@
package App::Netdisco::DB::ResultSet::DeviceModule;
use base 'App::Netdisco::DB::ResultSet';
use strict;
use warnings;
=head1 ADDITIONAL METHODS
=head2 search_by_field( \%cond, \%attrs? )
This variant of the standard C<search()> method returns a ResultSet of Device
Module entries. It is written to support web forms which accept fields that
match and locate Device Modules in the database.
The hashref parameter should contain fields from the Device Module table
which will be intelligently used in a search query.
In addition, you can provide the key C<matchall> which, given a True or False
value, controls whether fields must all match or whether any can match, to
select a row.
Supported keys:
=over 4
=item matchall
If a True value, fields must all match to return a given row of the Device
table, otherwise any field matching will cause the row to be included in
results.
=item description
Can match the C<description> field as a substring.
=item name
Can match the C<name> field as a substring.
=item type
Can match the C<type> field as a substring.
=item model
Can match the C<model> field as a substring.
=item serial
Can match the C<serial> field as a substring.
=item class
Will match exactly the C<class> field.
=item ips
List of Device IPs containing modules.
=back
=cut
sub search_by_field {
my ( $rs, $p, $attrs ) = @_;
die "condition parameter to search_by_field must be hashref\n"
if ref {} ne ref $p
or 0 == scalar keys %$p;
my $op = $p->{matchall} ? '-and' : '-or';
return $rs->search_rs( {}, $attrs )->search(
{ $op => [
( $p->{description}
? ( 'me.description' =>
{ '-ilike' => "\%$p->{description}\%" } )
: ()
),
( $p->{name}
? ( 'me.name' => { '-ilike' => "\%$p->{name}\%" } )
: ()
),
( $p->{type}
? ( 'me.type' => { '-ilike' => "\%$p->{type}\%" } )
: ()
),
( $p->{model}
? ( 'me.model' => { '-ilike' => "\%$p->{model}\%" } )
: ()
),
( $p->{serial}
? ( 'me.serial' => { '-ilike' => "\%$p->{serial}\%" } )
: ()
),
( $p->{class}
? ( 'me.class' => { '-in' => $p->{class} } )
: ()
),
( $p->{ips} ? ( 'me.ip' => { '-in' => $p->{ips} } ) : () ),
],
}
);
}
1;

View File

@@ -0,0 +1,174 @@
package App::Netdisco::DB::ResultSet::DevicePort;
use base 'App::Netdisco::DB::ResultSet';
use strict;
use warnings;
__PACKAGE__->load_components(qw/
+App::Netdisco::DB::ExplicitLocking
/);
=head1 ADDITIONAL METHODS
=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
=item lastchange_stamp
=back
=cut
sub with_times {
my ($rs, $cond, $attrs) = @_;
return $rs
->search_rs($cond, $attrs)
->search({},
{
'+columns' => { lastchange_stamp =>
\("to_char(device.last_discover - (device.uptime - me.lastchange) / 100 * interval '1 second', "
."'YYYY-MM-DD HH24:MI:SS')") },
join => 'device',
});
}
=head2 with_free_ports
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
=item is_free
=back
In the C<$cond> hash (the first parameter) pass in the C<age_num> which must
be an integer, and the C<age_unit> which must be a string of either C<days>,
C<weeks>, C<months> or C<years>.
=cut
sub with_is_free {
my ($rs, $cond, $attrs) = @_;
my $interval = (delete $cond->{age_num}) .' '. (delete $cond->{age_unit});
return $rs
->search_rs($cond, $attrs)
->search({},
{
'+columns' => { is_free =>
\["me.up != 'up' and "
."age(now(), to_timestamp(extract(epoch from device.last_discover) "
."- (device.uptime - me.lastchange)/100)) "
."> ?::interval",
[{} => $interval]] },
join => 'device',
});
}
=head2 only_free_ports
This is a modifier for any C<search()> (including the helpers below) which
will restrict results based on whether the port is considered "free".
In the C<$cond> hash (the first parameter) pass in the C<age_num> which must
be an integer, and the C<age_unit> which must be a string of either C<days>,
C<weeks>, C<months> or C<years>.
=cut
sub only_free_ports {
my ($rs, $cond, $attrs) = @_;
my $interval = (delete $cond->{age_num}) .' '. (delete $cond->{age_unit});
return $rs
->search_rs($cond, $attrs)
->search(
{
'me.up' => { '!=' => 'up' },
},{
where =>
\["age(now(), to_timestamp(extract(epoch from device.last_discover) "
."- (device.uptime - me.lastchange)/100)) "
."> ?::interval",
[{} => $interval]],
join => 'device' },
);
}
=head2 with_vlan_count
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
=item vlan_count
=back
=cut
sub with_vlan_count {
my ($rs, $cond, $attrs) = @_;
return $rs
->search_rs($cond, $attrs)
->search({},
{
'+columns' => { vlan_count =>
$rs->result_source->schema->resultset('DevicePortVlan')
->search(
{
'dpv.ip' => { -ident => 'me.ip' },
'dpv.port' => { -ident => 'me.port' },
},
{ alias => 'dpv' }
)->count_rs->as_query
},
});
}
=head1 SPECIAL METHODS
=head2 delete( \%options? )
Overrides the built-in L<DBIx::Class> delete method to more efficiently
handle the removal or archiving of nodes.
=cut
sub delete {
my $self = shift;
my $schema = $self->result_source->schema;
my $ports = $self->search(undef, { columns => 'ip' });
foreach my $set (qw/
DevicePortPower
DevicePortVlan
DevicePortWireless
DevicePortSsid
/) {
$schema->resultset($set)->search(
{ ip => { '-in' => $ports->as_query }},
)->delete;
}
$schema->resultset('Node')->search(
{ switch => { '-in' => $ports->as_query }},
)->delete(@_);
# now let DBIC do its thing
return $self->next::method();
}
1;

View File

@@ -0,0 +1,39 @@
package App::Netdisco::DB::ResultSet::DevicePortLog;
use base 'App::Netdisco::DB::ResultSet';
use strict;
use warnings;
__PACKAGE__->load_components(qw/
+App::Netdisco::DB::ExplicitLocking
/);
=head1 ADDITIONAL METHODS
=head2 with_times
This is a modifier for any C<search()> which will add the following additional
synthesized column to the result set:
=over 4
=item creation_stamp
=back
=cut
sub with_times {
my ($rs, $cond, $attrs) = @_;
return $rs
->search_rs($cond, $attrs)
->search({},
{
'+columns' => {
creation_stamp => \"to_char(creation, 'YYYY-MM-DD HH24:MI:SS')",
},
});
}
1;

View File

@@ -0,0 +1,48 @@
package App::Netdisco::DB::ResultSet::DevicePortSsid;
use base 'App::Netdisco::DB::ResultSet';
use strict;
use warnings;
__PACKAGE__->load_components(
qw/
+App::Netdisco::DB::ExplicitLocking
/
);
=head1 ADDITIONAL METHODS
=head2 get_ssids
Returns a sorted list of SSIDs with the following columns only:
=over 4
=item ssid
=item broadcast
=item count
=back
Where C<count> is the number of instances of the SSID in the Netdisco
database.
=cut
sub get_ssids {
my $rs = shift;
return $rs->search(
{},
{ select => [ 'ssid', 'broadcast', { count => 'ssid' } ],
as => [qw/ ssid broadcast count /],
group_by => [qw/ ssid broadcast /],
order_by => { -desc => [qw/count/] },
}
)
}
1;

View File

@@ -0,0 +1,78 @@
package App::Netdisco::DB::ResultSet::DevicePower;
use base 'App::Netdisco::DB::ResultSet';
use strict;
use warnings;
=head1 ADDITIONAL METHODS
=head2 with_poestats
This is a modifier for any C<search()> which will add the following
additional synthesized columns to the result set:
=over 4
=item poe_capable_ports
Count of ports which have the ability to supply PoE.
=item poe_powered_ports
Count of ports with PoE administratively disabled.
=item poe_disabled_ports
Count of ports which are delivering power.
=item poe_errored_ports
Count of ports either reporting a fault or in test mode.
=item poe_power_committed
Total power that has been negotiated and therefore committed on ports
actively supplying power.
=item poe_power_delivering
Total power as measured on ports actively supplying power.
=back
=cut
sub with_poestats {
my ($rs, $cond, $attrs) = @_;
return $rs
->search_rs($cond, $attrs)
->search({},
{
'columns' => {
ip => \"DISTINCT ON (me.ip, me.module) me.ip",
module => 'module',
power => 'power::bigint',
status => 'status',
poe_capable_ports => \"COUNT(ports.port) OVER (PARTITION BY me.ip, me.module)",
poe_powered_ports => \"SUM(CASE WHEN ports.status = 'deliveringPower' THEN 1 ELSE 0 END) OVER (PARTITION BY me.ip, me.module)",
poe_disabled_ports => \"SUM(CASE WHEN ports.admin = 'false' THEN 1 ELSE 0 END) OVER (PARTITION BY me.ip, me.module)",
poe_errored_ports => \"SUM(CASE WHEN ports.status ILIKE '%fault' THEN 1 ELSE 0 END) OVER (PARTITION BY me.ip, me.module)",
poe_power_committed => \("SUM(CASE "
. "WHEN ports.status = 'deliveringPower' AND ports.class = 'class0' THEN 15.4 "
. "WHEN ports.status = 'deliveringPower' AND ports.class = 'class1' THEN 4.0 "
. "WHEN ports.status = 'deliveringPower' AND ports.class = 'class2' THEN 7.0 "
. "WHEN ports.status = 'deliveringPower' AND ports.class = 'class3' THEN 15.4 "
. "WHEN ports.status = 'deliveringPower' AND ports.class = 'class4' THEN 30.0 "
. "WHEN ports.status = 'deliveringPower' AND ports.class IS NULL THEN 15.4 "
. "ELSE 0 END) OVER (PARTITION BY me.ip, me.module)"),
poe_power_delivering => \("SUM(CASE WHEN (ports.power IS NULL OR ports.power = '0') "
. "THEN 0 ELSE round(ports.power/1000.0, 1) END) "
. "OVER (PARTITION BY me.ip, me.module)")
},
join => 'ports'
});
}
1;

View File

@@ -0,0 +1,149 @@
package App::Netdisco::DB::ResultSet::Node;
use base 'App::Netdisco::DB::ResultSet';
use strict;
use warnings;
__PACKAGE__->load_components(qw/
+App::Netdisco::DB::ExplicitLocking
/);
=head1 ADDITIONAL METHODS
=head2 search_by_mac( \%cond, \%attrs? )
my $set = $rs->search_by_mac({mac => '00:11:22:33:44:55', active => 1});
Like C<search()>, this returns a ResultSet of matching rows from the Node
table.
=over 4
=item *
The C<cond> parameter must be a hashref containing a key C<mac> with
the value to search for.
=item *
Results are ordered by time last seen.
=item *
Additional columns C<time_first_stamp> and C<time_last_stamp> provide
preformatted timestamps of the C<time_first> and C<time_last> fields.
=item *
A JOIN is performed on the Device table and the Device C<dns> column
prefetched.
=back
To limit results only to active nodes, set C<< {active => 1} >> in C<cond>.
=cut
sub search_by_mac {
my ($rs, $cond, $attrs) = @_;
die "mac address required for search_by_mac\n"
if ref {} ne ref $cond or !exists $cond->{mac};
$cond->{'me.mac'} = delete $cond->{mac};
return $rs
->search_rs({}, {
order_by => {'-desc' => 'time_last'},
'+columns' => [
'device.dns',
{ time_first_stamp => \"to_char(time_first, 'YYYY-MM-DD HH24:MI')" },
{ time_last_stamp => \"to_char(time_last, 'YYYY-MM-DD HH24:MI')" },
],
join => 'device',
})
->search($cond, $attrs);
}
=head1 SPECIAL METHODS
=head2 delete( \%options? )
Overrides the built-in L<DBIx::Class> delete method to more efficiently
handle the removal or archiving of nodes.
=cut
sub delete {
my $self = shift;
my ($opts) = @_;
$opts = {} if (ref {} ne ref $opts);
my $schema = $self->result_source->schema;
my $nodes = $self->search(undef, { columns => 'mac' });
if (exists $opts->{archive_nodes} and $opts->{archive_nodes}) {
foreach my $set (qw/
NodeIp
NodeNbt
NodeMonitor
Node
/) {
$schema->resultset($set)->search(
{ mac => { '-in' => $nodes->as_query }},
)->update({ active => \'false' });
}
$schema->resultset('NodeWireless')
->search({ mac => { '-in' => $nodes->as_query }})->delete;
# avoid letting DBIC delete nodes
return 0E0;
}
elsif (exists $opts->{only_nodes} and $opts->{only_nodes}) {
# now let DBIC do its thing
return $self->next::method();
}
elsif (exists $opts->{keep_nodes} and $opts->{keep_nodes}) {
# avoid letting DBIC delete nodes
return 0E0;
}
else {
# for node_ip and node_nbt *only* delete if there are no longer
# any active nodes referencing the IP or NBT (hence 2nd IN clause).
foreach my $set (qw/
NodeIp
NodeNbt
/) {
$schema->resultset($set)->search({
'me.mac' => { '-in' => $schema->resultset($set)->search({
'-and' => [
-bool => 'nodes.active',
'me.mac' => { '-in' => $nodes->as_query }
]
},
{
columns => 'mac',
join => 'nodes',
group_by => 'me.mac',
having => \[ 'count(nodes.mac) = 0' ],
})->as_query,
},
})->delete;
}
foreach my $set (qw/
NodeMonitor
NodeWireless
/) {
$schema->resultset($set)->search(
{ mac => { '-in' => $nodes->as_query }},
)->delete;
}
# now let DBIC do its thing
return $self->next::method();
}
}
1;

View File

@@ -0,0 +1,219 @@
package App::Netdisco::DB::ResultSet::NodeIp;
use base 'App::Netdisco::DB::ResultSet';
use strict;
use warnings;
__PACKAGE__->load_components(qw/
+App::Netdisco::DB::ExplicitLocking
/);
my $search_attr = {
order_by => {'-desc' => 'time_last'},
'+columns' => [
'oui.company',
'oui.abbrev',
{ time_first_stamp => \"to_char(time_first, 'YYYY-MM-DD HH24:MI')" },
{ time_last_stamp => \"to_char(time_last, 'YYYY-MM-DD HH24:MI')" },
],
join => 'oui'
};
=head1 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
=item time_first_stamp
=item time_last_stamp
=back
=cut
sub with_times {
my ($rs, $cond, $attrs) = @_;
return $rs
->search_rs({}, $search_attr)
->search($cond, $attrs);
}
=head1 search_by_ip( \%cond, \%attrs? )
my $set = $rs->search_by_ip({ip => '192.0.2.1', active => 1});
Like C<search()>, this returns a ResultSet of matching rows from the
NodeIp table.
=over 4
=item *
The C<cond> parameter must be a hashref containing a key C<ip> with the value
to search for. Value can either be a simple string of IPv4 or IPv6, or a
L<NetAddr::IP::Lite> object in which case all results within the CIDR/Prefix
will be retrieved.
=item *
Results are ordered by time last seen.
=item *
Additional columns C<time_first_stamp> and C<time_last_stamp> provide
preformatted timestamps of the C<time_first> and C<time_last> fields.
=item *
A JOIN is performed on the OUI table and the OUI C<company> column prefetched.
=back
To limit results only to active IPs, set C<< {active => 1} >> in C<cond>.
=cut
sub search_by_ip {
my ($rs, $cond, $attrs) = @_;
die "ip address required for search_by_ip\n"
if ref {} ne ref $cond or !exists $cond->{ip};
# handle either plain text IP or NetAddr::IP (/32 or CIDR)
my ($op, $ip) = ('=', delete $cond->{ip});
if ('NetAddr::IP::Lite' eq ref $ip and $ip->num > 1) {
$op = '<<=';
$ip = $ip->cidr;
}
$cond->{ip} = { $op => $ip };
return $rs
->search_rs({}, $search_attr)
->search($cond, $attrs);
}
=head1 search_by_name( \%cond, \%attrs? )
my $set = $rs->search_by_name({dns => 'foo.example.com', active => 1});
Like C<search()>, this returns a ResultSet of matching rows from the
NodeIp table.
=over 4
=item *
The NodeIp table must have a C<dns> column for this search to work. Typically
this column is the IP's DNS PTR record, cached at the time of Netdisco Arpnip.
=item *
The C<cond> parameter must be a hashref containing a key C<dns> with the value
to search for. The value may optionally include SQL wildcard characters.
=item *
Results are ordered by time last seen.
=item *
Additional columns C<time_first_stamp> and C<time_last_stamp> provide
preformatted timestamps of the C<time_first> and C<time_last> fields.
=item *
A JOIN is performed on the OUI table and the OUI C<company> column prefetched.
=back
To limit results only to active IPs, set C<< {active => 1} >> in C<cond>.
=cut
sub search_by_dns {
my ($rs, $cond, $attrs) = @_;
die "dns field required for search_by_dns\n"
if ref {} ne ref $cond or !exists $cond->{dns};
$cond->{dns} = { '-ilike' => delete $cond->{dns} };
return $rs
->search_rs({}, $search_attr)
->search($cond, $attrs);
}
=head1 search_by_mac( \%cond, \%attrs? )
my $set = $rs->search_by_mac({mac => '00:11:22:33:44:55', active => 1});
Like C<search()>, this returns a ResultSet of matching rows from the
NodeIp table.
=over 4
=item *
The C<cond> parameter must be a hashref containing a key C<mac> with the value
to search for.
=item *
Results are ordered by time last seen.
=item *
Additional columns C<time_first_stamp> and C<time_last_stamp> provide
preformatted timestamps of the C<time_first> and C<time_last> fields.
=item *
A JOIN is performed on the OUI table and the OUI C<company> column prefetched.
=back
To limit results only to active IPs, set C<< {active => 1} >> in C<cond>.
=cut
sub search_by_mac {
my ($rs, $cond, $attrs) = @_;
die "mac address required for search_by_mac\n"
if ref {} ne ref $cond or !exists $cond->{mac};
return $rs
->search_rs({}, $search_attr)
->search($cond, $attrs);
}
=head2 ip_version( $version )
my $rset = $rs->ip_version(4);
This predefined C<search()> returns a ResultSet of matching rows from the
NodeIp table of nodes with addresses of the supplied IP version.
=over 4
=item *
The C<version> parameter must be an integer either 4 or 6.
=cut
sub ip_version {
my ( $rs, $version ) = @_;
die "ip_version input must be either 4 or 6\n"
unless $version && ( $version == 4 || $version == 6 );
return $rs->search_rs( \[ 'family(me.ip) = ?', $version ] );
}
1;

View File

@@ -0,0 +1,189 @@
package App::Netdisco::DB::ResultSet::NodeNbt;
use base 'App::Netdisco::DB::ResultSet';
use strict;
use warnings;
__PACKAGE__->load_components(qw/
+App::Netdisco::DB::ExplicitLocking
/);
my $search_attr = {
order_by => {'-desc' => 'time_last'},
'+columns' => [
'oui.company',
{ time_first_stamp => \"to_char(time_first, 'YYYY-MM-DD HH24:MI')" },
{ time_last_stamp => \"to_char(time_last, 'YYYY-MM-DD HH24:MI')" },
],
join => 'oui'
};
=head1 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
=item time_first_stamp
=item time_last_stamp
=back
=cut
sub with_times {
my ($rs, $cond, $attrs) = @_;
return $rs
->search_rs({}, $search_attr)
->search($cond, $attrs);
}
=head1 search_by_ip( \%cond, \%attrs? )
my $set = $rs->search_by_ip({ip => '192.0.2.1', active => 1});
Like C<search()>, this returns a ResultSet of matching rows from the
NodeNbt table.
=over 4
=item *
The C<cond> parameter must be a hashref containing a key C<ip> with the value
to search for. Value can either be a simple string of IPv4 or IPv6, or a
L<NetAddr::IP::Lite> object in which case all results within the CIDR/Prefix
will be retrieved.
=item *
Results are ordered by time last seen.
=item *
Additional columns C<time_first_stamp> and C<time_last_stamp> provide
preformatted timestamps of the C<time_first> and C<time_last> fields.
=item *
A JOIN is performed on the OUI table and the OUI C<company> column prefetched.
=back
To limit results only to active IPs, set C<< {active => 1} >> in C<cond>.
=cut
sub search_by_ip {
my ($rs, $cond, $attrs) = @_;
die "ip address required for search_by_ip\n"
if ref {} ne ref $cond or !exists $cond->{ip};
# handle either plain text IP or NetAddr::IP (/32 or CIDR)
my ($op, $ip) = ('=', delete $cond->{ip});
if ('NetAddr::IP::Lite' eq ref $ip and $ip->num > 1) {
$op = '<<=';
$ip = $ip->cidr;
}
$cond->{ip} = { $op => $ip };
return $rs
->search_rs({}, $search_attr)
->search($cond, $attrs);
}
=head1 search_by_name( \%cond, \%attrs? )
my $set = $rs->search_by_name({nbname => 'MYNAME', active => 1});
Like C<search()>, this returns a ResultSet of matching rows from the
NodeNbt table.
=over 4
=item *
The C<cond> parameter must be a hashref containing a key C<nbname> with the
value to search for. The value may optionally include SQL wildcard characters.
=item *
Results are ordered by time last seen.
=item *
Additional columns C<time_first_stamp> and C<time_last_stamp> provide
preformatted timestamps of the C<time_first> and C<time_last> fields.
=item *
A JOIN is performed on the OUI table and the OUI C<company> column prefetched.
=back
To limit results only to active IPs, set C<< {active => 1} >> in C<cond>.
=cut
sub search_by_name {
my ($rs, $cond, $attrs) = @_;
die "nbname field required for search_by_name\n"
if ref {} ne ref $cond or !exists $cond->{nbname};
$cond->{nbname} = { '-ilike' => delete $cond->{nbname} };
return $rs
->search_rs({}, $search_attr)
->search($cond, $attrs);
}
=head1 search_by_mac( \%cond, \%attrs? )
my $set = $rs->search_by_mac({mac => '00:11:22:33:44:55', active => 1});
Like C<search()>, this returns a ResultSet of matching rows from the
NodeNbt table.
=over 4
=item *
The C<cond> parameter must be a hashref containing a key C<mac> with the value
to search for.
=item *
Results are ordered by time last seen.
=item *
Additional columns C<time_first_stamp> and C<time_last_stamp> provide
preformatted timestamps of the C<time_first> and C<time_last> fields.
=item *
A JOIN is performed on the OUI table and the OUI C<company> column prefetched.
=back
To limit results only to active IPs, set C<< {active => 1} >> in C<cond>.
=cut
sub search_by_mac {
my ($rs, $cond, $attrs) = @_;
die "mac address required for search_by_mac\n"
if ref {} ne ref $cond or !exists $cond->{mac};
return $rs
->search_rs({}, $search_attr)
->search($cond, $attrs);
}
1;

View File

@@ -0,0 +1,11 @@
package App::Netdisco::DB::ResultSet::NodeWireless;
use base 'App::Netdisco::DB::ResultSet';
use strict;
use warnings;
__PACKAGE__->load_components(qw/
+App::Netdisco::DB::ExplicitLocking
/);
1;

View File

@@ -0,0 +1,11 @@
package App::Netdisco::DB::ResultSet::Subnet;
use base 'App::Netdisco::DB::ResultSet';
use strict;
use warnings;
__PACKAGE__->load_components(qw/
+App::Netdisco::DB::ExplicitLocking
/);
1;