make an App::Netdisco dist using Module::Install

This commit is contained in:
Oliver Gorwits
2012-12-17 18:31:16 +00:00
parent 6a0aa7864e
commit 05086e8b78
125 changed files with 428 additions and 127 deletions

View File

@@ -0,0 +1,44 @@
package App::Netdisco;
use strict;
use warnings FATAL => 'all';
use 5.10.0;
use File::ShareDir 'module_dir';
use Path::Class;
our $VERSION = '2.00_009';
BEGIN {
if (not length $ENV{DANCER_APPDIR}
or not -f file($ENV{DANCER_APPDIR}, 'config.yml')) {
my $auto = dir(File::ShareDir::module_dir('App::Netdisco'))->absolute;
$ENV{DANCER_APPDIR} ||= $auto->stringify;
$ENV{DANCER_CONFDIR} ||= $auto->stringify;
$ENV{DANCER_ENVDIR} ||= $auto->subdir('environments')->stringify;
$ENV{DANCER_PUBLIC} ||= $auto->subdir('public')->stringify;
$ENV{DANCER_VIEWS} ||= $auto->subdir('views')->stringify;
}
}
=head1 App::Netdisco
Netdisco is an Open Source web-based network management tool.
=head1 AUTHOR
Oliver Gorwits <oliver@cpan.org>
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2012 by The Netdisco Developer Team.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut
1;

View File

@@ -0,0 +1,31 @@
use utf8;
package App::Netdisco::DB;
# Created by DBIx::Class::Schema::Loader
# DO NOT MODIFY THE FIRST PART OF THIS FILE
use strict;
use warnings;
use base 'DBIx::Class::Schema';
__PACKAGE__->load_namespaces;
# Created by DBIx::Class::Schema::Loader v0.07015 @ 2012-01-07 14:20:02
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:tQTf/oInVydRDsuIFLSU4A
our $VERSION = 3; # schema version used for upgrades, keep as integer
use Path::Class;
use File::Basename;
my (undef, $libpath, undef) = fileparse( $INC{ 'App/Netdisco/DB.pm' } );
our $schema_versions_dir = Path::Class::Dir->new($libpath)
->subdir("DB", "schema_versions")->stringify;
__PACKAGE__->load_components(qw/Schema::Versioned/);
__PACKAGE__->upgrade_directory($schema_versions_dir);
# You can replace this text with custom code or comments, and it will be preserved on regeneration
1;

View File

@@ -0,0 +1,58 @@
use utf8;
package App::Netdisco::DB::Result::Admin;
# Created by DBIx::Class::Schema::Loader
# DO NOT MODIFY THE FIRST PART OF THIS FILE
use strict;
use warnings;
use base 'DBIx::Class::Core';
__PACKAGE__->table("admin");
__PACKAGE__->add_columns(
"job",
{
data_type => "integer",
is_auto_increment => 1,
is_nullable => 0,
sequence => "admin_job_seq",
},
"entered",
{
data_type => "timestamp",
default_value => \"current_timestamp",
is_nullable => 1,
original => { default_value => \"now()" },
},
"started",
{ data_type => "timestamp", is_nullable => 1 },
"finished",
{ data_type => "timestamp", is_nullable => 1 },
"device",
{ data_type => "inet", is_nullable => 1 },
"port",
{ data_type => "text", is_nullable => 1 },
"action",
{ data_type => "text", is_nullable => 1 },
"subaction",
{ data_type => "text", is_nullable => 1 },
"status",
{ data_type => "text", is_nullable => 1 },
"username",
{ data_type => "text", is_nullable => 1 },
"userip",
{ data_type => "inet", is_nullable => 1 },
"log",
{ data_type => "text", is_nullable => 1 },
"debug",
{ data_type => "boolean", is_nullable => 1 },
);
# Created by DBIx::Class::Schema::Loader v0.07015 @ 2012-01-07 14:20:02
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:gW4JW4pMgrufFIxFeYPYpw
__PACKAGE__->set_primary_key("job");
# You can replace this text with custom code or comments, and it will be preserved on regeneration
1;

View File

@@ -0,0 +1,184 @@
use utf8;
package App::Netdisco::DB::Result::Device;
# Created by DBIx::Class::Schema::Loader
# DO NOT MODIFY THE FIRST PART OF THIS FILE
use strict;
use warnings;
use base 'DBIx::Class::Core';
__PACKAGE__->table("device");
__PACKAGE__->add_columns(
"ip",
{ data_type => "inet", is_nullable => 0 },
"creation",
{
data_type => "timestamp",
default_value => \"current_timestamp",
is_nullable => 1,
original => { default_value => \"now()" },
},
"dns",
{ data_type => "text", is_nullable => 1 },
"description",
{ data_type => "text", is_nullable => 1 },
"uptime",
{ data_type => "bigint", is_nullable => 1 },
"contact",
{ data_type => "text", is_nullable => 1 },
"name",
{ data_type => "text", is_nullable => 1 },
"location",
{ data_type => "text", is_nullable => 1 },
"layers",
{ data_type => "varchar", is_nullable => 1, size => 8 },
"ports",
{ data_type => "integer", is_nullable => 1 },
"mac",
{ data_type => "macaddr", is_nullable => 1 },
"serial",
{ data_type => "text", is_nullable => 1 },
"model",
{ data_type => "text", is_nullable => 1 },
"ps1_type",
{ data_type => "text", is_nullable => 1 },
"ps2_type",
{ data_type => "text", is_nullable => 1 },
"ps1_status",
{ data_type => "text", is_nullable => 1 },
"ps2_status",
{ data_type => "text", is_nullable => 1 },
"fan",
{ data_type => "text", is_nullable => 1 },
"slots",
{ data_type => "integer", is_nullable => 1 },
"vendor",
{ data_type => "text", is_nullable => 1 },
"os",
{ data_type => "text", is_nullable => 1 },
"os_ver",
{ data_type => "text", is_nullable => 1 },
"log",
{ data_type => "text", is_nullable => 1 },
"snmp_ver",
{ data_type => "integer", is_nullable => 1 },
"snmp_comm",
{ data_type => "text", is_nullable => 1 },
"snmp_class",
{ data_type => "text", is_nullable => 1 },
"vtp_domain",
{ data_type => "text", is_nullable => 1 },
"last_discover",
{ data_type => "timestamp", is_nullable => 1 },
"last_macsuck",
{ data_type => "timestamp", is_nullable => 1 },
"last_arpnip",
{ data_type => "timestamp", is_nullable => 1 },
);
__PACKAGE__->set_primary_key("ip");
# Created by DBIx::Class::Schema::Loader v0.07015 @ 2012-01-07 14:20:02
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:671/XuuvsO2aMB1+IRWFjg
=head1 RELATIONSHIPS
=head2 device_ips
Returns rows from the C<device_ip> table which relate to this Device. That is,
all the interface IP aliases configured on the Device.
=cut
__PACKAGE__->has_many( device_ips => 'App::Netdisco::DB::Result::DeviceIp', 'ip' );
=head2 vlans
Returns the C<device_vlan> entries for this Device. That is, the list of VLANs
configured on or known by this Device.
=cut
__PACKAGE__->has_many( vlans => 'App::Netdisco::DB::Result::DeviceVlan', 'ip' );
=head2 ports
Returns the set of ports on this Device.
=cut
__PACKAGE__->has_many( ports => 'App::Netdisco::DB::Result::DevicePort', 'ip' );
=head2 port_vlans
Returns the set of VLANs known to be configured on Ports on this Device,
either tagged or untagged.
The JOIN is of type "RIGHT" meaning that the results are constrained to VLANs
only on Ports on this Device.
=cut
__PACKAGE__->has_many(
port_vlans => 'App::Netdisco::DB::Result::DevicePortVlan',
'ip', { join_type => 'RIGHT' }
);
# helper which assumes we've just RIGHT JOINed to Vlans table
sub vlan { return (shift)->vlans->first }
=head1 ADDITIONAL COLUMNS
=head2 uptime_age
Formatted version of the C<uptime> field.
The format is in "X days/months/years" style, similar to:
1 year 4 months 05:46:00
=cut
sub uptime_age { return (shift)->get_column('uptime_age') }
=head2 last_discover_stamp
Formatted version of the C<last_discover> field, accurate to the minute.
The format is somewhat like ISO 8601 or RFC3339 but without the middle C<T>
between the date stamp and time stamp. That is:
2012-02-06 12:49
=cut
sub last_discover_stamp { return (shift)->get_column('last_discover_stamp') }
=head2 last_macsuck_stamp
Formatted version of the C<last_macsuck> field, accurate to the minute.
The format is somewhat like ISO 8601 or RFC3339 but without the middle C<T>
between the date stamp and time stamp. That is:
2012-02-06 12:49
=cut
sub last_macsuck_stamp { return (shift)->get_column('last_macsuck_stamp') }
=head2 last_arpnip_stamp
Formatted version of the C<last_arpnip> field, accurate to the minute.
The format is somewhat like ISO 8601 or RFC3339 but without the middle C<T>
between the date stamp and time stamp. That is:
2012-02-06 12:49
=cut
sub last_arpnip_stamp { return (shift)->get_column('last_arpnip_stamp') }
1;

View File

@@ -0,0 +1,59 @@
use utf8;
package App::Netdisco::DB::Result::DeviceIp;
# Created by DBIx::Class::Schema::Loader
# DO NOT MODIFY THE FIRST PART OF THIS FILE
use strict;
use warnings;
use base 'DBIx::Class::Core';
__PACKAGE__->table("device_ip");
__PACKAGE__->add_columns(
"ip",
{ data_type => "inet", is_nullable => 0 },
"alias",
{ data_type => "inet", is_nullable => 0 },
"subnet",
{ data_type => "cidr", is_nullable => 1 },
"port",
{ data_type => "text", is_nullable => 1 },
"dns",
{ data_type => "text", is_nullable => 1 },
"creation",
{
data_type => "timestamp",
default_value => \"current_timestamp",
is_nullable => 1,
original => { default_value => \"now()" },
},
);
__PACKAGE__->set_primary_key("ip", "alias");
# Created by DBIx::Class::Schema::Loader v0.07015 @ 2012-01-07 14:20:02
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:/ugGtBSGyrJ7s6yqJ9bclQ
=head1 RELATIONSHIPS
=head2 device
Returns the entry from the C<device> table to which this IP alias relates.
=cut
__PACKAGE__->belongs_to( device => 'App::Netdisco::DB::Result::Device', 'ip' );
=head2 device_port
Returns the Port on which this IP address is configured (typically a loopback,
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' } );
1;

View File

@@ -0,0 +1,59 @@
use utf8;
package App::Netdisco::DB::Result::DeviceModule;
# Created by DBIx::Class::Schema::Loader
# DO NOT MODIFY THE FIRST PART OF THIS FILE
use strict;
use warnings;
use base 'DBIx::Class::Core';
__PACKAGE__->table("device_module");
__PACKAGE__->add_columns(
"ip",
{ data_type => "inet", is_nullable => 0 },
"index",
{ data_type => "integer", is_nullable => 0 },
"description",
{ data_type => "text", is_nullable => 1 },
"type",
{ data_type => "text", is_nullable => 1 },
"parent",
{ data_type => "integer", is_nullable => 1 },
"name",
{ data_type => "text", is_nullable => 1 },
"class",
{ data_type => "text", is_nullable => 1 },
"pos",
{ data_type => "integer", is_nullable => 1 },
"hw_ver",
{ data_type => "text", is_nullable => 1 },
"fw_ver",
{ data_type => "text", is_nullable => 1 },
"sw_ver",
{ data_type => "text", is_nullable => 1 },
"serial",
{ data_type => "text", is_nullable => 1 },
"model",
{ data_type => "text", is_nullable => 1 },
"fru",
{ data_type => "boolean", is_nullable => 1 },
"creation",
{
data_type => "timestamp",
default_value => \"current_timestamp",
is_nullable => 1,
original => { default_value => \"now()" },
},
"last_discover",
{ data_type => "timestamp", is_nullable => 1 },
);
__PACKAGE__->set_primary_key("ip", "index");
# Created by DBIx::Class::Schema::Loader v0.07015 @ 2012-01-07 14:20:02
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:nuwxZBoiip9trdJFmgk3Fw
# You can replace this text with custom code or comments, and it will be preserved on regeneration
1;

View File

@@ -0,0 +1,253 @@
use utf8;
package App::Netdisco::DB::Result::DevicePort;
# Created by DBIx::Class::Schema::Loader
# DO NOT MODIFY THE FIRST PART OF THIS FILE
use strict;
use warnings;
use base 'DBIx::Class::Core';
__PACKAGE__->table("device_port");
__PACKAGE__->add_columns(
"ip",
{ data_type => "inet", is_nullable => 0 },
"port",
{ data_type => "text", is_nullable => 0 },
"creation",
{
data_type => "timestamp",
default_value => \"current_timestamp",
is_nullable => 1,
original => { default_value => \"now()" },
},
"descr",
{ data_type => "text", is_nullable => 1 },
"up",
{ data_type => "text", is_nullable => 1 },
"up_admin",
{ data_type => "text", is_nullable => 1 },
"type",
{ data_type => "text", is_nullable => 1 },
"duplex",
{ data_type => "text", is_nullable => 1 },
"duplex_admin",
{ data_type => "text", is_nullable => 1 },
"speed",
{ data_type => "text", is_nullable => 1 },
"name",
{ data_type => "text", is_nullable => 1 },
"mac",
{ data_type => "macaddr", is_nullable => 1 },
"mtu",
{ data_type => "integer", is_nullable => 1 },
"stp",
{ data_type => "text", is_nullable => 1 },
"remote_ip",
{ data_type => "inet", is_nullable => 1 },
"remote_port",
{ data_type => "text", is_nullable => 1 },
"remote_type",
{ data_type => "text", is_nullable => 1 },
"remote_id",
{ data_type => "text", is_nullable => 1 },
"vlan",
{ data_type => "text", is_nullable => 1 },
"pvid",
{ data_type => "integer", is_nullable => 1 },
"lastchange",
{ data_type => "bigint", is_nullable => 1 },
);
__PACKAGE__->set_primary_key("port", "ip");
# Created by DBIx::Class::Schema::Loader v0.07015 @ 2012-01-07 14:20:02
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:lcbweb0loNwHoWUuxTN/hA
=head1 RELATIONSHIPS
=head2 device
Returns the Device table entry to which the given Port is related.
=cut
__PACKAGE__->belongs_to( device => 'App::Netdisco::DB::Result::Device', 'ip');
=head2 nodes / active_nodes / nodes_with_age / active_nodes_with_age
Returns the set of Nodes whose MAC addresses are associated with this Device
Port.
The C<active> variants return only the subset of nodes currently in the switch
MAC address table, that is the active ones.
The C<with_age> variants add an additional column C<time_last_age>, a
preformatted value for the Node's C<time_last> field, which reads as "X
days/weeks/months/years".
=cut
__PACKAGE__->has_many( nodes => 'App::Netdisco::DB::Result::Node',
{
'foreign.switch' => 'self.ip',
'foreign.port' => 'self.port',
},
{ join_type => 'LEFT' },
);
__PACKAGE__->has_many( nodes_with_age => 'App::Netdisco::DB::Result::Virtual::NodeWithAge',
{
'foreign.switch' => 'self.ip',
'foreign.port' => 'self.port',
},
{ join_type => 'LEFT' },
);
__PACKAGE__->has_many( active_nodes => 'App::Netdisco::DB::Result::Virtual::ActiveNode',
{
'foreign.switch' => 'self.ip',
'foreign.port' => 'self.port',
},
{ join_type => 'LEFT' },
);
__PACKAGE__->has_many( active_nodes_with_age => 'App::Netdisco::DB::Result::Virtual::ActiveNodeWithAge',
{
'foreign.switch' => 'self.ip',
'foreign.port' => 'self.port',
},
{ join_type => 'LEFT' },
);
=head2 neighbor_alias
When a device port has an attached neighbor device, this relationship will
return the IP address of the neighbor. See the C<neighbor> helper method if
what you really want is to retrieve the Device entry for that neighbor.
The JOIN is of type "LEFT" in case the neighbor device is known but has not
been fully discovered by Netdisco and so does not exist itself in the
database.
=cut
__PACKAGE__->belongs_to( neighbor_alias => 'App::Netdisco::DB::Result::DeviceIp',
{ 'foreign.alias' => 'self.remote_ip' },
{ join_type => 'LEFT' },
);
=head2 power
Returns a row from the C<device_port_power> table if one refers to this
device port.
=cut
__PACKAGE__->might_have( power => 'App::Netdisco::DB::Result::DevicePortPower', {
'foreign.ip' => 'self.ip', 'foreign.port' => 'self.port',
});
=head2 port_vlans_tagged
Returns a set of rows from the C<device_port_vlan> table relating to this
port, where the VLANs are all tagged.
=cut
__PACKAGE__->has_many( port_vlans_tagged => 'App::Netdisco::DB::Result::Virtual::DevicePortVlanTagged',
{
'foreign.ip' => 'self.ip',
'foreign.port' => 'self.port',
},
{ join_type => 'LEFT' },
);
=head2 tagged_vlans
As compared to C<port_vlans_tagged>, this relationship returns a set of VLAN
row objects for the VLANs on the given port, which might be more useful if you
want to find out details such as the VLAN name.
See also C<tagged_vlans_count>.
=cut
__PACKAGE__->many_to_many( tagged_vlans => 'port_vlans_tagged', 'vlan' );
=head2 oui
Returns the C<oui> table entry matching this Port. You can then join on this
relation and retrieve the Company name from the related table.
The JOIN is of type LEFT, in case the OUI table has not been populated.
=cut
__PACKAGE__->belongs_to( oui => 'App::Netdisco::DB::Result::Oui',
sub {
my $args = shift;
return {
"$args->{foreign_alias}.oui" =>
{ '=' => \"substring(cast($args->{self_alias}.mac as varchar) for 8)" }
};
},
{ join_type => 'LEFT' }
);
=head1 ADDITIONAL METHODS
=head2
Returns the Device entry for the neighbour Device on the given port.
Might return an undefined value if there is no neighbor on the port, or if the
neighbor has not been fully discovered by Netdisco and so does not exist in
the database.
=cut
sub neighbor {
my $row = shift;
return eval { $row->neighbor_alias->device || undef };
}
=head1 ADDITIONAL COLUMNS
=head2 tagged_vlans_count
Returns the number of tagged VLANs active on this device port. Enable this
column by applying the C<with_vlan_count()> modifier to C<search()>.
=cut
sub tagged_vlans_count { return (shift)->get_column('tagged_vlans_count') }
=head2 lastchange_stamp
Formatted version of the C<lastchange> field, accurate to the minute. Enable
this column by applying the C<with_vlan_count()> modifier to C<search()>.
The format is somewhat like ISO 8601 or RFC3339 but without the middle C<T>
between the date stamp and time stamp. That is:
2012-02-06 12:49
=cut
sub lastchange_stamp { return (shift)->get_column('lastchange_stamp') }
=head2 is_free
This method can be used to evaluate whether a device port could be considered
unused, based on the last time it changed from the "up" state to a "down"
state.
See the C<with_is_free> and C<only_free_ports> modifiers to C<search()>.
=cut
sub is_free { return (shift)->get_column('is_free') }
1;

View File

@@ -0,0 +1,49 @@
use utf8;
package App::Netdisco::DB::Result::DevicePortLog;
# Created by DBIx::Class::Schema::Loader
# DO NOT MODIFY THE FIRST PART OF THIS FILE
use strict;
use warnings;
use base 'DBIx::Class::Core';
__PACKAGE__->table("device_port_log");
__PACKAGE__->add_columns(
"id",
{
data_type => "integer",
is_auto_increment => 1,
is_nullable => 0,
sequence => "device_port_log_id_seq",
},
"ip",
{ data_type => "inet", is_nullable => 1 },
"port",
{ data_type => "text", is_nullable => 1 },
"reason",
{ data_type => "text", is_nullable => 1 },
"log",
{ data_type => "text", is_nullable => 1 },
"username",
{ data_type => "text", is_nullable => 1 },
"userip",
{ data_type => "inet", is_nullable => 1 },
"action",
{ data_type => "text", is_nullable => 1 },
"creation",
{
data_type => "timestamp",
default_value => \"current_timestamp",
is_nullable => 1,
original => { default_value => \"now()" },
},
);
# Created by DBIx::Class::Schema::Loader v0.07015 @ 2012-01-07 14:20:02
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:5moCbYoDG2BqT7VrP/MRkA
# You can replace this text with custom code or comments, and it will be preserved on regeneration
1;

View File

@@ -0,0 +1,46 @@
use utf8;
package App::Netdisco::DB::Result::DevicePortPower;
# Created by DBIx::Class::Schema::Loader
# DO NOT MODIFY THE FIRST PART OF THIS FILE
use strict;
use warnings;
use base 'DBIx::Class::Core';
__PACKAGE__->table("device_port_power");
__PACKAGE__->add_columns(
"ip",
{ data_type => "inet", is_nullable => 0 },
"port",
{ data_type => "text", is_nullable => 0 },
"module",
{ data_type => "integer", is_nullable => 1 },
"admin",
{ data_type => "text", is_nullable => 1 },
"status",
{ data_type => "text", is_nullable => 1 },
"class",
{ data_type => "text", is_nullable => 1 },
"power",
{ data_type => "integer", is_nullable => 1 },
);
__PACKAGE__->set_primary_key("port", "ip");
# Created by DBIx::Class::Schema::Loader v0.07015 @ 2012-01-07 14:20:02
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:sHcdItRUFUOAtIZQjdWbcg
=head1 RELATIONSHIPS
=head2 port
Returns the entry from the C<port> table for which this Power entry applies.
=cut
__PACKAGE__->belongs_to( port => 'App::Netdisco::DB::Result::DevicePort', {
'foreign.ip' => 'self.ip', 'foreign.port' => 'self.port',
});
1;

View File

@@ -0,0 +1,31 @@
use utf8;
package App::Netdisco::DB::Result::DevicePortSsid;
# Created by DBIx::Class::Schema::Loader
# DO NOT MODIFY THE FIRST PART OF THIS FILE
use strict;
use warnings;
use base 'DBIx::Class::Core';
__PACKAGE__->table("device_port_ssid");
__PACKAGE__->add_columns(
"ip",
{ data_type => "inet", is_nullable => 1 },
"port",
{ data_type => "text", is_nullable => 1 },
"ssid",
{ data_type => "text", is_nullable => 1 },
"broadcast",
{ data_type => "boolean", is_nullable => 1 },
"bssid",
{ data_type => "macaddr", is_nullable => 1 },
);
# Created by DBIx::Class::Schema::Loader v0.07015 @ 2012-01-07 14:20:02
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:zvgylKzUQtizJZCe1rEdUg
# You can replace this text with custom code or comments, and it will be preserved on regeneration
1;

View File

@@ -0,0 +1,76 @@
use utf8;
package App::Netdisco::DB::Result::DevicePortVlan;
# Created by DBIx::Class::Schema::Loader
# DO NOT MODIFY THE FIRST PART OF THIS FILE
use strict;
use warnings;
use base 'DBIx::Class::Core';
__PACKAGE__->table("device_port_vlan");
__PACKAGE__->add_columns(
"ip",
{ data_type => "inet", is_nullable => 0 },
"port",
{ data_type => "text", is_nullable => 0 },
"vlan",
{ data_type => "integer", is_nullable => 0 },
"native",
{ data_type => "boolean", default_value => \"false", is_nullable => 0 },
"creation",
{
data_type => "timestamp",
default_value => \"current_timestamp",
is_nullable => 1,
original => { default_value => \"now()" },
},
"last_discover",
{
data_type => "timestamp",
default_value => \"current_timestamp",
is_nullable => 1,
original => { default_value => \"now()" },
},
"vlantype",
{ data_type => "text", is_nullable => 1 },
);
__PACKAGE__->set_primary_key("ip", "port", "vlan");
# Created by DBIx::Class::Schema::Loader v0.07015 @ 2012-01-07 14:20:02
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:/3KLjJ3D18pGaPEaw9EU5w
=head1 RELATIONSHIPS
=head2 device
Returns the entry from the C<device> table which hosts the Port on which this
VLAN is configured.
=cut
__PACKAGE__->belongs_to( device => 'App::Netdisco::DB::Result::Device', 'ip' );
=head2 port
Returns the entry from the C<port> table on which this VLAN is configured.
=cut
__PACKAGE__->belongs_to( port => 'App::Netdisco::DB::Result::DevicePort', {
'foreign.ip' => 'self.ip', 'foreign.port' => 'self.port',
});
=head2 vlan
Returns the entry from the C<device_vlan> table describing this VLAN in
detail, typically in order that the C<name> can be retrieved.
=cut
__PACKAGE__->belongs_to( vlan => 'App::Netdisco::DB::Result::DeviceVlan', {
'foreign.ip' => 'self.ip', 'foreign.vlan' => 'self.vlan',
});
1;

View File

@@ -0,0 +1,29 @@
use utf8;
package App::Netdisco::DB::Result::DevicePortWireless;
# Created by DBIx::Class::Schema::Loader
# DO NOT MODIFY THE FIRST PART OF THIS FILE
use strict;
use warnings;
use base 'DBIx::Class::Core';
__PACKAGE__->table("device_port_wireless");
__PACKAGE__->add_columns(
"ip",
{ data_type => "inet", is_nullable => 1 },
"port",
{ data_type => "text", is_nullable => 1 },
"channel",
{ data_type => "integer", is_nullable => 1 },
"power",
{ data_type => "integer", is_nullable => 1 },
);
# Created by DBIx::Class::Schema::Loader v0.07015 @ 2012-01-07 14:20:02
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:T5GmnCj/9BB7meiGZ3xN7g
# You can replace this text with custom code or comments, and it will be preserved on regeneration
1;

View File

@@ -0,0 +1,30 @@
use utf8;
package App::Netdisco::DB::Result::DevicePower;
# Created by DBIx::Class::Schema::Loader
# DO NOT MODIFY THE FIRST PART OF THIS FILE
use strict;
use warnings;
use base 'DBIx::Class::Core';
__PACKAGE__->table("device_power");
__PACKAGE__->add_columns(
"ip",
{ data_type => "inet", is_nullable => 0 },
"module",
{ data_type => "integer", is_nullable => 0 },
"power",
{ data_type => "integer", is_nullable => 1 },
"status",
{ data_type => "text", is_nullable => 1 },
);
__PACKAGE__->set_primary_key("ip", "module");
# Created by DBIx::Class::Schema::Loader v0.07015 @ 2012-01-07 14:20:02
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:awZRI/IH2VewzGlxISsr7w
# You can replace this text with custom code or comments, and it will be preserved on regeneration
1;

View File

@@ -0,0 +1,42 @@
use utf8;
package App::Netdisco::DB::Result::DeviceRoute;
# Created by DBIx::Class::Schema::Loader
# DO NOT MODIFY THE FIRST PART OF THIS FILE
use strict;
use warnings;
use base 'DBIx::Class::Core';
__PACKAGE__->table("device_route");
__PACKAGE__->add_columns(
"ip",
{ data_type => "inet", is_nullable => 0 },
"network",
{ data_type => "cidr", is_nullable => 0 },
"creation",
{
data_type => "timestamp",
default_value => \"current_timestamp",
is_nullable => 1,
original => { default_value => \"now()" },
},
"dest",
{ data_type => "inet", is_nullable => 0 },
"last_discover",
{
data_type => "timestamp",
default_value => \"current_timestamp",
is_nullable => 1,
original => { default_value => \"now()" },
},
);
__PACKAGE__->set_primary_key("ip", "network", "dest");
# Created by DBIx::Class::Schema::Loader v0.07015 @ 2012-01-07 14:20:02
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:3jcvPP60E5BvwnUbXql7mQ
# You can replace this text with custom code or comments, and it will be preserved on regeneration
1;

View File

@@ -0,0 +1,87 @@
use utf8;
package App::Netdisco::DB::Result::DeviceVlan;
# Created by DBIx::Class::Schema::Loader
# DO NOT MODIFY THE FIRST PART OF THIS FILE
use strict;
use warnings;
use base 'DBIx::Class::Core';
__PACKAGE__->table("device_vlan");
__PACKAGE__->add_columns(
"ip",
{ data_type => "inet", is_nullable => 0 },
"vlan",
{ data_type => "integer", is_nullable => 0 },
"description",
{ data_type => "text", is_nullable => 1 },
"creation",
{
data_type => "timestamp",
default_value => \"current_timestamp",
is_nullable => 1,
original => { default_value => \"now()" },
},
"last_discover",
{
data_type => "timestamp",
default_value => \"current_timestamp",
is_nullable => 1,
original => { default_value => \"now()" },
},
);
__PACKAGE__->set_primary_key("ip", "vlan");
# Created by DBIx::Class::Schema::Loader v0.07015 @ 2012-01-07 14:20:02
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:hBJRcdzOic4d3u4pD1m8iA
=head1 RELATIONSHIPS
=head2 device
Returns the entry from the C<device> table on which this VLAN entry was discovered.
=cut
__PACKAGE__->belongs_to( device => 'App::Netdisco::DB::Result::Device', 'ip' );
=head2 port_vlans_tagged
Link relationship for C<tagging_ports>, see below.
=cut
__PACKAGE__->has_many( port_vlans_tagged => 'App::Netdisco::DB::Result::Virtual::DevicePortVlanTagged',
{ 'foreign.ip' => 'self.ip', 'foreign.vlan' => 'self.vlan' },
);
=head2 port_vlans_native
Link relationship to support C<native_ports>, see below.
=cut
__PACKAGE__->has_many( port_vlans_native => 'App::Netdisco::DB::Result::Virtual::DevicePortVlanNative',
{ 'foreign.ip' => 'self.ip', 'foreign.vlan' => 'self.vlan' },
);
=head2 tagging_ports
Returns the set of Device Ports on which this VLAN is configured to be tagged.
=cut
__PACKAGE__->many_to_many( tagging_ports => 'port_vlans_tagged', 'port' );
=head2 native_ports
Returns the set of Device Ports on which this VLAN is the native VLAN (that
is, untagged).
=cut
__PACKAGE__->many_to_many( native_ports => 'port_vlans_native', 'port' );
1;

View File

@@ -0,0 +1,41 @@
use utf8;
package App::Netdisco::DB::Result::Log;
# Created by DBIx::Class::Schema::Loader
# DO NOT MODIFY THE FIRST PART OF THIS FILE
use strict;
use warnings;
use base 'DBIx::Class::Core';
__PACKAGE__->table("log");
__PACKAGE__->add_columns(
"id",
{
data_type => "integer",
is_auto_increment => 1,
is_nullable => 0,
sequence => "log_id_seq",
},
"creation",
{
data_type => "timestamp",
default_value => \"current_timestamp",
is_nullable => 1,
original => { default_value => \"now()" },
},
"class",
{ data_type => "text", is_nullable => 1 },
"entry",
{ data_type => "text", is_nullable => 1 },
"logfile",
{ data_type => "text", is_nullable => 1 },
);
# Created by DBIx::Class::Schema::Loader v0.07015 @ 2012-01-07 14:20:02
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:eonwOHvvzWm88Ug+IGKuzg
# You can replace this text with custom code or comments, and it will be preserved on regeneration
1;

View File

@@ -0,0 +1,135 @@
use utf8;
package App::Netdisco::DB::Result::Node;
# Created by DBIx::Class::Schema::Loader
# DO NOT MODIFY THE FIRST PART OF THIS FILE
use strict;
use warnings;
use base 'DBIx::Class::Core';
__PACKAGE__->table("node");
__PACKAGE__->add_columns(
"mac",
{ data_type => "macaddr", is_nullable => 0 },
"switch",
{ data_type => "inet", is_nullable => 0 },
"port",
{ data_type => "text", is_nullable => 0 },
"active",
{ data_type => "boolean", is_nullable => 1 },
"oui",
{ data_type => "varchar", is_nullable => 1, size => 8 },
"time_first",
{
data_type => "timestamp",
default_value => \"current_timestamp",
is_nullable => 1,
original => { default_value => \"now()" },
},
"time_recent",
{
data_type => "timestamp",
default_value => \"current_timestamp",
is_nullable => 1,
original => { default_value => \"now()" },
},
"time_last",
{
data_type => "timestamp",
default_value => \"current_timestamp",
is_nullable => 1,
original => { default_value => \"now()" },
},
);
__PACKAGE__->set_primary_key("mac", "switch", "port");
# Created by DBIx::Class::Schema::Loader v0.07015 @ 2012-01-07 14:20:02
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:sGGyKEfUkoIFVtmj1wnH7A
=head1 RELATIONSHIPS
=head2 device
Returns the single C<device> to which this Node entry was associated at the
time of discovery.
The JOIN is of type LEFT, in case the C<device> is no longer present in the
database but the relation is being used in C<search()>.
=cut
__PACKAGE__->belongs_to( device => 'App::Netdisco::DB::Result::Device',
{ 'foreign.ip' => 'self.switch' }, { join_type => 'LEFT' } );
=head2 device_port
Returns the single C<device_port> to which this Node entry was associated at
the time of discovery.
The JOIN is of type LEFT, in case the C<device> is no longer present in the
database but the relation is being used in C<search()>.
=cut
# device port may have been deleted (reconfigured modules?) but node remains
__PACKAGE__->belongs_to( device_port => 'App::Netdisco::DB::Result::DevicePort',
{ 'foreign.ip' => 'self.switch', 'foreign.port' => 'self.port' },
{ join_type => 'LEFT' }
);
=head2 ips
Returns the set of C<node_ip> entries associated with this Node. That is, the
IP addresses which this MAC address was hosting at the time of discovery.
Note that the Active status of the returned IP entries will all be the same as
the current Node's.
=cut
__PACKAGE__->has_many( ips => 'App::Netdisco::DB::Result::NodeIp',
{ 'foreign.mac' => 'self.mac', 'foreign.active' => 'self.active' } );
=head2 oui
Returns the C<oui> table entry matching this Node. You can then join on this
relation and retrieve the Company name from the related table.
The JOIN is of type LEFT, in case the OUI table has not been populated.
=cut
__PACKAGE__->belongs_to( oui => 'App::Netdisco::DB::Result::Oui', 'oui',
{ join_type => 'LEFT' } );
=head1 ADDITIONAL COLUMNS
=head2 time_first_stamp
Formatted version of the C<time_first> field, accurate to the minute.
The format is somewhat like ISO 8601 or RFC3339 but without the middle C<T>
between the date stamp and time stamp. That is:
2012-02-06 12:49
=cut
sub time_first_stamp { return (shift)->get_column('time_first_stamp') }
=head2 time_last_stamp
Formatted version of the C<time_last> field, accurate to the minute.
The format is somewhat like ISO 8601 or RFC3339 but without the middle C<T>
between the date stamp and time stamp. That is:
2012-02-06 12:49
=cut
sub time_last_stamp { return (shift)->get_column('time_last_stamp') }
1;

View File

@@ -0,0 +1,209 @@
use utf8;
package App::Netdisco::DB::Result::NodeIp;
# Created by DBIx::Class::Schema::Loader
# DO NOT MODIFY THE FIRST PART OF THIS FILE
use strict;
use warnings;
use base 'DBIx::Class::Core';
__PACKAGE__->table("node_ip");
__PACKAGE__->add_columns(
"mac",
{ data_type => "macaddr", is_nullable => 0 },
"ip",
{ data_type => "inet", is_nullable => 0 },
"dns",
{ data_type => "text", is_nullable => 1 },
"active",
{ data_type => "boolean", is_nullable => 1 },
"time_first",
{
data_type => "timestamp",
default_value => \"current_timestamp",
is_nullable => 1,
original => { default_value => \"now()" },
},
"time_last",
{
data_type => "timestamp",
default_value => \"current_timestamp",
is_nullable => 1,
original => { default_value => \"now()" },
},
);
__PACKAGE__->set_primary_key("mac", "ip");
# Created by DBIx::Class::Schema::Loader v0.07015 @ 2012-01-07 14:20:02
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:9+CuvuVWH88WxAf6IBij8g
=head1 RELATIONSHIPS
=head2 oui
Returns the C<oui> table entry matching this Node. You can then join on this
relation and retrieve the Company name from the related table.
The JOIN is of type LEFT, in case the OUI table has not been populated.
=cut
__PACKAGE__->belongs_to( oui => 'App::Netdisco::DB::Result::Oui',
sub {
my $args = shift;
return {
"$args->{foreign_alias}.oui" =>
{ '=' => \"substring(cast($args->{self_alias}.mac as varchar) for 8)" }
};
},
{ join_type => 'LEFT' }
);
=head2 node_ips
Returns the set of all C<node_ip> entries which are associated together with
this IP. That is, all the IP addresses hosted on the same interface (MAC
address) as the current Node IP entry.
Note that the set will include the original Node IP object itself. If you wish
to find the I<other> IPs excluding this one, see the C<ip_aliases> helper
routine, below.
Remember you can pass a filter to this method to find only active or inactive
nodes, but do take into account that both the C<node> and C<node_ip> tables
include independent C<active> fields.
=cut
__PACKAGE__->has_many( node_ips => 'App::Netdisco::DB::Result::NodeIp',
{ 'foreign.mac' => 'self.mac' } );
=head2 nodes
Returns the set of C<node> entries associated with this IP. That is, all the
MAC addresses recorded which have ever hosted this IP Address.
Remember you can pass a filter to this method to find only active or inactive
nodes, but do take into account that both the C<node> and C<node_ip> tables
include independent C<active> fields.
See also the C<node_sightings> helper routine, below.
=cut
__PACKAGE__->has_many( nodes => 'App::Netdisco::DB::Result::Node',
{ 'foreign.mac' => 'self.mac' } );
my $search_attr = {
order_by => {'-desc' => 'time_last'},
'+columns' => {
time_first_stamp => \"to_char(time_first, 'YYYY-MM-DD HH24:MI')",
time_last_stamp => \"to_char(time_last, 'YYYY-MM-DD HH24:MI')",
},
};
=head2 ip_aliases( \%cond, \%attrs? )
Returns the set of other C<node_ip> entries hosted on the same interface (MAC
address) as the current Node IP, excluding the current IP itself.
Remember you can pass a filter to this method to find only active or inactive
nodes, but do take into account that both the C<node> and C<node_ip> tables
include independent C<active> fields.
=over 4
=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.
=back
=cut
sub ip_aliases {
my ($row, $cond, $attrs) = @_;
my $rs = $row->node_ips({ip => { '!=' => $row->ip }});
return $rs
->search_rs({}, $search_attr)
->search($cond, $attrs);
}
=head2 node_sightings( \%cond, \%attrs? )
Returns the set of C<node> entries associated with this IP. That is, all the
MAC addresses recorded which have ever hosted this IP Address.
Remember you can pass a filter to this method to find only active or inactive
nodes, but do take into account that both the C<node> and C<node_ip> tables
include independent C<active> fields.
=over 4
=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 DNS column prefetched.
=back
=cut
sub node_sightings {
my ($row, $cond, $attrs) = @_;
return $row
->nodes({}, {
'+columns' => [qw/ device.dns /],
join => 'device',
})
->search_rs({}, $search_attr)
->search($cond, $attrs);
}
=head1 ADDITIONAL COLUMNS
=head2 time_first_stamp
Formatted version of the C<time_first> field, accurate to the minute.
The format is somewhat like ISO 8601 or RFC3339 but without the middle C<T>
between the date stamp and time stamp. That is:
2012-02-06 12:49
=cut
sub time_first_stamp { return (shift)->get_column('time_first_stamp') }
=head2 time_last_stamp
Formatted version of the C<time_last> field, accurate to the minute.
The format is somewhat like ISO 8601 or RFC3339 but without the middle C<T>
between the date stamp and time stamp. That is:
2012-02-06 12:49
=cut
sub time_last_stamp { return (shift)->get_column('time_last_stamp') }
1;

View File

@@ -0,0 +1,37 @@
use utf8;
package App::Netdisco::DB::Result::NodeMonitor;
# Created by DBIx::Class::Schema::Loader
# DO NOT MODIFY THE FIRST PART OF THIS FILE
use strict;
use warnings;
use base 'DBIx::Class::Core';
__PACKAGE__->table("node_monitor");
__PACKAGE__->add_columns(
"mac",
{ data_type => "macaddr", is_nullable => 0 },
"active",
{ data_type => "boolean", is_nullable => 1 },
"why",
{ data_type => "text", is_nullable => 1 },
"cc",
{ data_type => "text", is_nullable => 1 },
"date",
{
data_type => "timestamp",
default_value => \"current_timestamp",
is_nullable => 1,
original => { default_value => \"now()" },
},
);
__PACKAGE__->set_primary_key("mac");
# Created by DBIx::Class::Schema::Loader v0.07015 @ 2012-01-07 14:20:02
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:0prRdz2XYlFuE+nahsI2Yg
# You can replace this text with custom code or comments, and it will be preserved on regeneration
1;

View File

@@ -0,0 +1,50 @@
use utf8;
package App::Netdisco::DB::Result::NodeNbt;
# Created by DBIx::Class::Schema::Loader
# DO NOT MODIFY THE FIRST PART OF THIS FILE
use strict;
use warnings;
use base 'DBIx::Class::Core';
__PACKAGE__->table("node_nbt");
__PACKAGE__->add_columns(
"mac",
{ data_type => "macaddr", is_nullable => 0 },
"ip",
{ data_type => "inet", is_nullable => 1 },
"nbname",
{ data_type => "text", is_nullable => 1 },
"domain",
{ data_type => "text", is_nullable => 1 },
"server",
{ data_type => "boolean", is_nullable => 1 },
"nbuser",
{ data_type => "text", is_nullable => 1 },
"active",
{ data_type => "boolean", is_nullable => 1 },
"time_first",
{
data_type => "timestamp",
default_value => \"current_timestamp",
is_nullable => 1,
original => { default_value => \"now()" },
},
"time_last",
{
data_type => "timestamp",
default_value => \"current_timestamp",
is_nullable => 1,
original => { default_value => \"now()" },
},
);
__PACKAGE__->set_primary_key("mac");
# Created by DBIx::Class::Schema::Loader v0.07015 @ 2012-01-07 14:20:02
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:XFpxaGAWE13iizQIuVOP3g
# You can replace this text with custom code or comments, and it will be preserved on regeneration
1;

View File

@@ -0,0 +1,49 @@
use utf8;
package App::Netdisco::DB::Result::NodeWireless;
# Created by DBIx::Class::Schema::Loader
# DO NOT MODIFY THE FIRST PART OF THIS FILE
use strict;
use warnings;
use base 'DBIx::Class::Core';
__PACKAGE__->table("node_wireless");
__PACKAGE__->add_columns(
"mac",
{ data_type => "macaddr", is_nullable => 0 },
"uptime",
{ data_type => "integer", is_nullable => 1 },
"maxrate",
{ data_type => "integer", is_nullable => 1 },
"txrate",
{ data_type => "integer", is_nullable => 1 },
"sigstrength",
{ data_type => "integer", is_nullable => 1 },
"sigqual",
{ data_type => "integer", is_nullable => 1 },
"rxpkt",
{ data_type => "integer", is_nullable => 1 },
"txpkt",
{ data_type => "integer", is_nullable => 1 },
"rxbyte",
{ data_type => "bigint", is_nullable => 1 },
"txbyte",
{ data_type => "bigint", is_nullable => 1 },
"time_last",
{
data_type => "timestamp",
default_value => \"current_timestamp",
is_nullable => 1,
original => { default_value => \"now()" },
},
);
__PACKAGE__->set_primary_key("mac");
# Created by DBIx::Class::Schema::Loader v0.07015 @ 2012-01-07 14:20:02
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:3xsSiWzL85ih3vhdews8Hg
# You can replace this text with custom code or comments, and it will be preserved on regeneration
1;

View File

@@ -0,0 +1,26 @@
use utf8;
package App::Netdisco::DB::Result::Oui;
# Created by DBIx::Class::Schema::Loader
# DO NOT MODIFY THE FIRST PART OF THIS FILE
use strict;
use warnings;
use base 'DBIx::Class::Core';
__PACKAGE__->table("oui");
__PACKAGE__->add_columns(
"oui",
{ data_type => "varchar", is_nullable => 0, size => 8 },
"company",
{ data_type => "text", is_nullable => 1 },
);
__PACKAGE__->set_primary_key("oui");
# Created by DBIx::Class::Schema::Loader v0.07015 @ 2012-01-07 14:20:02
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:s51mj6SvstPd4GdNEy9SoA
# You can replace this text with custom code or comments, and it will be preserved on regeneration
1;

View File

@@ -0,0 +1,38 @@
use utf8;
package App::Netdisco::DB::Result::Process;
# Created by DBIx::Class::Schema::Loader
# DO NOT MODIFY THE FIRST PART OF THIS FILE
use strict;
use warnings;
use base 'DBIx::Class::Core';
__PACKAGE__->table("process");
__PACKAGE__->add_columns(
"controller",
{ data_type => "integer", is_nullable => 0 },
"device",
{ data_type => "inet", is_nullable => 0 },
"action",
{ data_type => "text", is_nullable => 0 },
"status",
{ data_type => "text", is_nullable => 1 },
"count",
{ data_type => "integer", is_nullable => 1 },
"creation",
{
data_type => "timestamp",
default_value => \"current_timestamp",
is_nullable => 1,
original => { default_value => \"now()" },
},
);
# Created by DBIx::Class::Schema::Loader v0.07015 @ 2012-01-07 14:20:02
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:28hTnOo4oNwJabiWWHBgCw
# You can replace this text with custom code or comments, and it will be preserved on regeneration
1;

View File

@@ -0,0 +1,33 @@
use utf8;
package App::Netdisco::DB::Result::Session;
# Created by DBIx::Class::Schema::Loader
# DO NOT MODIFY THE FIRST PART OF THIS FILE
use strict;
use warnings;
use base 'DBIx::Class::Core';
__PACKAGE__->table("sessions");
__PACKAGE__->add_columns(
"id",
{ data_type => "char", is_nullable => 0, size => 32 },
"creation",
{
data_type => "timestamp",
default_value => \"current_timestamp",
is_nullable => 1,
original => { default_value => \"now()" },
},
"a_session",
{ data_type => "text", is_nullable => 1 },
);
__PACKAGE__->set_primary_key("id");
# Created by DBIx::Class::Schema::Loader v0.07015 @ 2012-01-07 14:20:02
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:khNPh72VjQh8QHayuW/p1w
# You can replace this text with custom code or comments, and it will be preserved on regeneration
1;

View File

@@ -0,0 +1,38 @@
use utf8;
package App::Netdisco::DB::Result::Subnet;
# Created by DBIx::Class::Schema::Loader
# DO NOT MODIFY THE FIRST PART OF THIS FILE
use strict;
use warnings;
use base 'DBIx::Class::Core';
__PACKAGE__->table("subnets");
__PACKAGE__->add_columns(
"net",
{ data_type => "cidr", is_nullable => 0 },
"creation",
{
data_type => "timestamp",
default_value => \"current_timestamp",
is_nullable => 1,
original => { default_value => \"now()" },
},
"last_discover",
{
data_type => "timestamp",
default_value => \"current_timestamp",
is_nullable => 1,
original => { default_value => \"now()" },
},
);
__PACKAGE__->set_primary_key("net");
# Created by DBIx::Class::Schema::Loader v0.07015 @ 2012-01-07 14:20:02
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:1EHOfYx8PYOHoTkViZR6OA
# You can replace this text with custom code or comments, and it will be preserved on regeneration
1;

View File

@@ -0,0 +1,22 @@
use utf8;
package App::Netdisco::DB::Result::Topology;
use strict;
use warnings;
use base 'DBIx::Class::Core';
__PACKAGE__->table("topology");
__PACKAGE__->add_columns(
"dev1",
{ data_type => "inet", is_nullable => 0 },
"port1",
{ data_type => "text", is_nullable => 0 },
"dev2",
{ data_type => "inet", is_nullable => 0 },
"port2",
{ data_type => "text", is_nullable => 0 },
);
1;

View File

@@ -0,0 +1,45 @@
use utf8;
package App::Netdisco::DB::Result::User;
# Created by DBIx::Class::Schema::Loader
# DO NOT MODIFY THE FIRST PART OF THIS FILE
use strict;
use warnings;
use base 'DBIx::Class::Core';
__PACKAGE__->table("users");
__PACKAGE__->add_columns(
"username",
{ data_type => "varchar", is_nullable => 0, size => 50 },
"password",
{ data_type => "text", is_nullable => 1 },
"creation",
{
data_type => "timestamp",
default_value => \"current_timestamp",
is_nullable => 1,
original => { default_value => \"now()" },
},
"last_on",
{ data_type => "timestamp", is_nullable => 1 },
"port_control",
{ data_type => "boolean", default_value => \"false", is_nullable => 1 },
"ldap",
{ data_type => "boolean", default_value => \"false", is_nullable => 1 },
"admin",
{ data_type => "boolean", default_value => \"false", is_nullable => 1 },
"fullname",
{ data_type => "text", is_nullable => 1 },
"note",
{ data_type => "text", is_nullable => 1 },
);
__PACKAGE__->set_primary_key("username");
# Created by DBIx::Class::Schema::Loader v0.07015 @ 2012-01-07 14:20:02
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:2awpSJkzXP7+8eyT4vGjfw
# You can replace this text with custom code or comments, and it will be preserved on regeneration
1;

View File

@@ -0,0 +1,43 @@
use utf8;
package App::Netdisco::DB::Result::UserLog;
# Created by DBIx::Class::Schema::Loader
# DO NOT MODIFY THE FIRST PART OF THIS FILE
use strict;
use warnings;
use base 'DBIx::Class::Core';
__PACKAGE__->table("user_log");
__PACKAGE__->add_columns(
"entry",
{
data_type => "integer",
is_auto_increment => 1,
is_nullable => 0,
sequence => "user_log_entry_seq",
},
"username",
{ data_type => "varchar", is_nullable => 1, size => 50 },
"userip",
{ data_type => "inet", is_nullable => 1 },
"event",
{ data_type => "text", is_nullable => 1 },
"details",
{ data_type => "text", is_nullable => 1 },
"creation",
{
data_type => "timestamp",
default_value => \"current_timestamp",
is_nullable => 1,
original => { default_value => \"now()" },
},
);
# Created by DBIx::Class::Schema::Loader v0.07015 @ 2012-01-07 14:20:02
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:BFrhjYJOhcLIHeWviu9rjw
# You can replace this text with custom code or comments, and it will be preserved on regeneration
1;

View File

@@ -0,0 +1,19 @@
use utf8;
package App::Netdisco::DB::Result::Virtual::ActiveNode;
use strict;
use warnings;
use base 'App::Netdisco::DB::Result::Node';
__PACKAGE__->load_components('Helper::Row::SubClass');
__PACKAGE__->subclass;
__PACKAGE__->table_class('DBIx::Class::ResultSource::View');
__PACKAGE__->table("active_node");
__PACKAGE__->result_source_instance->is_virtual(1);
__PACKAGE__->result_source_instance->view_definition(q{
SELECT * FROM node WHERE active
});
1;

View File

@@ -0,0 +1,27 @@
use utf8;
package App::Netdisco::DB::Result::Virtual::ActiveNodeWithAge;
use strict;
use warnings;
use base 'App::Netdisco::DB::Result::Virtual::ActiveNode';
__PACKAGE__->load_components('Helper::Row::SubClass');
__PACKAGE__->subclass;
__PACKAGE__->table_class('DBIx::Class::ResultSource::View');
__PACKAGE__->table("active_node_with_age");
__PACKAGE__->result_source_instance->is_virtual(1);
__PACKAGE__->result_source_instance->view_definition(q{
SELECT *,
replace(age( date_trunc( 'minute', time_last + interval '30 second' ) ) ::text, 'mon', 'month')
AS time_last_age
FROM node WHERE active
});
__PACKAGE__->add_columns(
"time_last_age",
{ data_type => "text", is_nullable => 1 },
);
1;

View File

@@ -0,0 +1,34 @@
package App::Netdisco::DB::Result::Virtual::DeviceLinks;
use strict;
use warnings;
use base 'DBIx::Class::Core';
__PACKAGE__->table_class('DBIx::Class::ResultSource::View');
__PACKAGE__->table('device_links');
__PACKAGE__->result_source_instance->is_virtual(1);
__PACKAGE__->result_source_instance->view_definition(<<ENDSQL
SELECT dp.ip AS left_ip, di.ip AS right_ip
FROM ( SELECT device_port.ip, device_port.remote_ip
FROM device_port
WHERE device_port.remote_port IS NOT NULL
GROUP BY device_port.ip, device_port.remote_ip
ORDER BY device_port.ip) dp
LEFT JOIN device_ip di ON dp.remote_ip = di.alias
WHERE di.ip IS NOT NULL
ORDER BY dp.ip
ENDSQL
);
__PACKAGE__->add_columns(
'left_ip' => {
data_type => 'inet',
},
'right_ip' => {
data_type => 'inet',
},
);
1;

View File

@@ -0,0 +1,19 @@
use utf8;
package App::Netdisco::DB::Result::Virtual::DevicePortVlanNative;
use strict;
use warnings;
use base 'App::Netdisco::DB::Result::DevicePortVlan';
__PACKAGE__->load_components('Helper::Row::SubClass');
__PACKAGE__->subclass;
__PACKAGE__->table_class('DBIx::Class::ResultSource::View');
__PACKAGE__->table("device_port_vlan_native");
__PACKAGE__->result_source_instance->is_virtual(1);
__PACKAGE__->result_source_instance->view_definition(q{
SELECT * FROM device_port_vlan WHERE native
});
1;

View File

@@ -0,0 +1,19 @@
use utf8;
package App::Netdisco::DB::Result::Virtual::DevicePortVlanTagged;
use strict;
use warnings;
use base 'App::Netdisco::DB::Result::DevicePortVlan';
__PACKAGE__->load_components('Helper::Row::SubClass');
__PACKAGE__->subclass;
__PACKAGE__->table_class('DBIx::Class::ResultSource::View');
__PACKAGE__->table("device_port_vlan_tagged");
__PACKAGE__->result_source_instance->is_virtual(1);
__PACKAGE__->result_source_instance->view_definition(q{
SELECT * FROM device_port_vlan WHERE NOT native
});
1;

View File

@@ -0,0 +1,27 @@
use utf8;
package App::Netdisco::DB::Result::Virtual::NodeWithAge;
use strict;
use warnings;
use base 'App::Netdisco::DB::Result::Node';
__PACKAGE__->load_components('Helper::Row::SubClass');
__PACKAGE__->subclass;
__PACKAGE__->table_class('DBIx::Class::ResultSource::View');
__PACKAGE__->table("node_with_age");
__PACKAGE__->result_source_instance->is_virtual(1);
__PACKAGE__->result_source_instance->view_definition(q{
SELECT *,
replace(age( date_trunc( 'minute', time_last + interval '30 second' ) ) ::text, 'mon', 'month')
AS time_last_age
FROM node
});
__PACKAGE__->add_columns(
"time_last_age",
{ data_type => "text", is_nullable => 1 },
);
1;

View File

@@ -0,0 +1,412 @@
package App::Netdisco::DB::ResultSet::Device;
use base 'DBIx::Class::ResultSet';
use strict;
use warnings FATAL => 'all';
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')",
},
});
}
=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_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 (length $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') );
}
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->{model} ? ('me.model' =>
{ '-in' => $p->{model} }) : ()),
($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 and have at least one
Port configured in the Vlan (either tagged, or not).
=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};
$cond->{'vlans.vlan'} = $cond->{vlan};
$cond->{'port_vlans.vlan'} = delete $cond->{vlan};
return $rs
->search_rs($cond,
{
order_by => [qw/ me.dns me.ip /],
columns => [qw/ me.ip me.dns me.model me.os me.vendor /],
join => 'port_vlans',
prefetch => '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 and have at least one
Port configured in the Vlan (either tagged, or not).
=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' => $cond->{name} };
return $rs
->search_rs({}, {
order_by => [qw/ me.dns me.ip /],
columns => [qw/ me.ip me.dns me.model me.os me.vendor /],
prefetch => 'vlans',
})
->search($cond, $attrs);
}
=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'}, {-desc => 'count'}, {-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'}, {-desc => 'count'}, {-asc => 'os_ver'}],
})
}
=head2 get_distinct_col( $column )
Returns an asciibetical sorted list of the distinct values in the given column
of the Device table. This is useful for web forms when you want to provide a
drop-down list of possible options.
=cut
sub get_distinct_col {
my ($rs, $col) = @_;
return $rs unless $col;
return $rs->search({},
{
columns => [$col],
order_by => $col,
distinct => 1
}
)->get_column($col)->all;
}
1;

View File

@@ -0,0 +1,136 @@
package App::Netdisco::DB::ResultSet::DevicePort;
use base 'DBIx::Class::ResultSet';
use strict;
use warnings FATAL => 'all';
=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 - 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 =>
\["up != 'up' and "
."age(to_timestamp(extract(epoch from device.last_discover) "
."- (device.uptime - 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(
{
'up' => { '!=' => 'up' },
},{
where =>
\["age(to_timestamp(extract(epoch from device.last_discover) "
."- (device.uptime - 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 tagged_vlans_count
=back
=cut
sub with_vlan_count {
my ($rs, $cond, $attrs) = @_;
return $rs
->search_rs($cond, $attrs)
->search({},
{
'+columns' => { tagged_vlans_count =>
$rs->result_source->schema->resultset('Virtual::DevicePortVlanTagged')
->search(
{
'dpvt.ip' => { -ident => 'me.ip' },
'dpvt.port' => { -ident => 'me.port' },
},
{ alias => 'dpvt' }
)->count_rs->as_query
},
});
}
1;

View File

@@ -0,0 +1,62 @@
package App::Netdisco::DB::ResultSet::Node;
use base 'DBIx::Class::ResultSet';
use strict;
use warnings FATAL => 'all';
=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 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);
}
1;

View File

@@ -0,0 +1,167 @@
package App::Netdisco::DB::ResultSet::NodeIp;
use base 'DBIx::Class::ResultSet';
use strict;
use warnings FATAL => 'all';
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 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
NetAddr::IP 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);
}
1;

View File

@@ -0,0 +1,425 @@
--
-- Created by SQL::Translator::Producer::PostgreSQL
-- Created on Wed Oct 10 14:24:20 2012
--
--
-- Table: admin.
--
CREATE TABLE "admin" (
"job" serial NOT NULL,
"entered" timestamp DEFAULT current_timestamp,
"started" timestamp,
"finished" timestamp,
"device" inet,
"port" text,
"action" text,
"subaction" text,
"status" text,
"username" text,
"userip" inet,
"log" text,
"debug" boolean
);
--
-- Table: device.
--
CREATE TABLE "device" (
"ip" inet NOT NULL,
"creation" timestamp DEFAULT current_timestamp,
"dns" text,
"description" text,
"uptime" bigint,
"contact" text,
"name" text,
"location" text,
"layers" character varying(8),
"ports" integer,
"mac" macaddr,
"serial" text,
"model" text,
"ps1_type" text,
"ps2_type" text,
"ps1_status" text,
"ps2_status" text,
"fan" text,
"slots" integer,
"vendor" text,
"os" text,
"os_ver" text,
"log" text,
"snmp_ver" integer,
"snmp_comm" text,
"snmp_class" text,
"vtp_domain" text,
"last_discover" timestamp,
"last_macsuck" timestamp,
"last_arpnip" timestamp,
PRIMARY KEY ("ip")
);
--
-- Table: device_module.
--
CREATE TABLE "device_module" (
"ip" inet NOT NULL,
"index" integer NOT NULL,
"description" text,
"type" text,
"parent" integer,
"name" text,
"class" text,
"pos" integer,
"hw_ver" text,
"fw_ver" text,
"sw_ver" text,
"serial" text,
"model" text,
"fru" boolean,
"creation" timestamp DEFAULT current_timestamp,
"last_discover" timestamp,
PRIMARY KEY ("ip", "index")
);
--
-- Table: device_port_log.
--
CREATE TABLE "device_port_log" (
"id" serial NOT NULL,
"ip" inet,
"port" text,
"reason" text,
"log" text,
"username" text,
"userip" inet,
"action" text,
"creation" timestamp DEFAULT current_timestamp
);
--
-- Table: device_port_power.
--
CREATE TABLE "device_port_power" (
"ip" inet NOT NULL,
"port" text NOT NULL,
"module" integer,
"admin" text,
"status" text,
"class" text,
"power" integer,
PRIMARY KEY ("port", "ip")
);
--
-- Table: device_port_ssid.
--
CREATE TABLE "device_port_ssid" (
"ip" inet,
"port" text,
"ssid" text,
"broadcast" boolean
);
--
-- Table: device_port_wireless.
--
CREATE TABLE "device_port_wireless" (
"ip" inet,
"port" text,
"channel" integer,
"power" integer
);
--
-- Table: device_power.
--
CREATE TABLE "device_power" (
"ip" inet NOT NULL,
"module" integer NOT NULL,
"power" integer,
"status" text,
PRIMARY KEY ("ip", "module")
);
--
-- Table: device_route.
--
CREATE TABLE "device_route" (
"ip" inet NOT NULL,
"network" cidr NOT NULL,
"creation" timestamp DEFAULT current_timestamp,
"dest" inet NOT NULL,
"last_discover" timestamp DEFAULT current_timestamp,
PRIMARY KEY ("ip", "network", "dest")
);
--
-- Table: log.
--
CREATE TABLE "log" (
"id" serial NOT NULL,
"creation" timestamp DEFAULT current_timestamp,
"class" text,
"entry" text,
"logfile" text
);
--
-- Table: node_ip.
--
CREATE TABLE "node_ip" (
"mac" macaddr NOT NULL,
"ip" inet NOT NULL,
"active" boolean,
"time_first" timestamp DEFAULT current_timestamp,
"time_last" timestamp DEFAULT current_timestamp,
PRIMARY KEY ("mac", "ip")
);
--
-- Table: node_monitor.
--
CREATE TABLE "node_monitor" (
"mac" macaddr NOT NULL,
"active" boolean,
"why" text,
"cc" text,
"date" timestamp DEFAULT current_timestamp,
PRIMARY KEY ("mac")
);
--
-- Table: node_nbt.
--
CREATE TABLE "node_nbt" (
"mac" macaddr NOT NULL,
"ip" inet,
"nbname" text,
"domain" text,
"server" boolean,
"nbuser" text,
"active" boolean,
"time_first" timestamp DEFAULT current_timestamp,
"time_last" timestamp DEFAULT current_timestamp,
PRIMARY KEY ("mac")
);
--
-- Table: node_wireless.
--
CREATE TABLE "node_wireless" (
"mac" macaddr NOT NULL,
"uptime" integer,
"maxrate" integer,
"txrate" integer,
"sigstrength" integer,
"sigqual" integer,
"rxpkt" integer,
"txpkt" integer,
"rxbyte" bigint,
"txbyte" bigint,
"time_last" timestamp DEFAULT current_timestamp,
PRIMARY KEY ("mac")
);
--
-- Table: oui.
--
CREATE TABLE "oui" (
"oui" character varying(8) NOT NULL,
"company" text,
PRIMARY KEY ("oui")
);
--
-- Table: process.
--
CREATE TABLE "process" (
"controller" integer NOT NULL,
"device" inet NOT NULL,
"action" text NOT NULL,
"status" text,
"count" integer,
"creation" timestamp DEFAULT current_timestamp
);
--
-- Table: sessions.
--
CREATE TABLE "sessions" (
"id" character(32) NOT NULL,
"creation" timestamp DEFAULT current_timestamp,
"a_session" text,
PRIMARY KEY ("id")
);
--
-- Table: subnets.
--
CREATE TABLE "subnets" (
"net" cidr NOT NULL,
"creation" timestamp DEFAULT current_timestamp,
"last_discover" timestamp DEFAULT current_timestamp,
PRIMARY KEY ("net")
);
--
-- Table: user_log.
--
CREATE TABLE "user_log" (
"entry" serial NOT NULL,
"username" character varying(50),
"userip" inet,
"event" text,
"details" text,
"creation" timestamp DEFAULT current_timestamp
);
--
-- Table: users.
--
CREATE TABLE "users" (
"username" character varying(50) NOT NULL,
"password" text,
"creation" timestamp DEFAULT current_timestamp,
"last_on" timestamp,
"port_control" boolean DEFAULT false,
"ldap" boolean DEFAULT false,
"admin" boolean DEFAULT false,
"fullname" text,
"note" text,
PRIMARY KEY ("username")
);
--
-- Table: device_vlan.
--
CREATE TABLE "device_vlan" (
"ip" inet NOT NULL,
"vlan" integer NOT NULL,
"description" text,
"creation" timestamp DEFAULT current_timestamp,
"last_discover" timestamp DEFAULT current_timestamp,
PRIMARY KEY ("ip", "vlan")
);
CREATE INDEX "device_vlan_idx_ip" on "device_vlan" ("ip");
--
-- Table: device_ip.
--
CREATE TABLE "device_ip" (
"ip" inet NOT NULL,
"alias" inet NOT NULL,
"subnet" cidr,
"port" text,
"dns" text,
"creation" timestamp DEFAULT current_timestamp,
PRIMARY KEY ("ip", "alias"),
CONSTRAINT "device_ip_alias" UNIQUE ("alias")
);
CREATE INDEX "device_ip_idx_ip" on "device_ip" ("ip");
CREATE INDEX "device_ip_idx_ip_port" on "device_ip" ("ip", "port");
--
-- Table: device_port.
--
CREATE TABLE "device_port" (
"ip" inet NOT NULL,
"port" text NOT NULL,
"creation" timestamp DEFAULT current_timestamp,
"descr" text,
"up" text,
"up_admin" text,
"type" text,
"duplex" text,
"duplex_admin" text,
"speed" text,
"name" text,
"mac" macaddr,
"mtu" integer,
"stp" text,
"remote_ip" inet,
"remote_port" text,
"remote_type" text,
"remote_id" text,
"vlan" text,
"pvid" integer,
"lastchange" bigint,
PRIMARY KEY ("port", "ip")
);
CREATE INDEX "device_port_idx_ip" on "device_port" ("ip");
CREATE INDEX "device_port_idx_remote_ip" on "device_port" ("remote_ip");
--
-- Table: device_port_vlan.
--
CREATE TABLE "device_port_vlan" (
"ip" inet NOT NULL,
"port" text NOT NULL,
"vlan" integer NOT NULL,
"native" boolean DEFAULT false NOT NULL,
"creation" timestamp DEFAULT current_timestamp,
"last_discover" timestamp DEFAULT current_timestamp,
PRIMARY KEY ("ip", "port", "vlan")
);
CREATE INDEX "device_port_vlan_idx_ip" on "device_port_vlan" ("ip");
CREATE INDEX "device_port_vlan_idx_ip_port" on "device_port_vlan" ("ip", "port");
CREATE INDEX "device_port_vlan_idx_ip_vlan" on "device_port_vlan" ("ip", "vlan");
--
-- Table: node.
--
CREATE TABLE "node" (
"mac" macaddr NOT NULL,
"switch" inet NOT NULL,
"port" text NOT NULL,
"active" boolean,
"oui" character varying(8),
"time_first" timestamp DEFAULT current_timestamp,
"time_recent" timestamp DEFAULT current_timestamp,
"time_last" timestamp DEFAULT current_timestamp,
PRIMARY KEY ("mac", "switch", "port")
);
CREATE INDEX "node_idx_switch" on "node" ("switch");
CREATE INDEX "node_idx_switch_port" on "node" ("switch", "port");
CREATE INDEX "node_idx_oui" on "node" ("oui");
-- Not used in Netdisco, because they upset the legacy netdisco.pm code
--
-- --
-- -- Foreign Key Definitions
-- --
--
-- ALTER TABLE "device_vlan" ADD CONSTRAINT "device_vlan_fk_ip" FOREIGN KEY ("ip")
-- REFERENCES "device" ("ip") ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE;
--
-- ALTER TABLE "device_ip" ADD CONSTRAINT "device_ip_fk_ip" FOREIGN KEY ("ip")
-- REFERENCES "device" ("ip") ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE;
--
-- ALTER TABLE "device_ip" ADD CONSTRAINT "device_ip_fk_ip_port" FOREIGN KEY ("ip", "port")
-- REFERENCES "device_port" ("ip", "port") DEFERRABLE;
--
-- ALTER TABLE "device_port" ADD CONSTRAINT "device_port_fk_ip" FOREIGN KEY ("ip")
-- REFERENCES "device" ("ip") ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE;
--
-- ALTER TABLE "device_port" ADD CONSTRAINT "device_port_fk_remote_ip" FOREIGN KEY ("remote_ip")
-- REFERENCES "device_ip" ("alias") DEFERRABLE;
--
-- ALTER TABLE "device_port_vlan" ADD CONSTRAINT "device_port_vlan_fk_ip" FOREIGN KEY ("ip")
-- REFERENCES "device" ("ip") ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE;
--
-- ALTER TABLE "device_port_vlan" ADD CONSTRAINT "device_port_vlan_fk_ip_port" FOREIGN KEY ("ip", "port")
-- REFERENCES "device_port" ("ip", "port") DEFERRABLE;
--
-- ALTER TABLE "device_port_vlan" ADD CONSTRAINT "device_port_vlan_fk_ip_vlan" FOREIGN KEY ("ip", "vlan")
-- REFERENCES "device_vlan" ("ip", "vlan") DEFERRABLE;
--
-- ALTER TABLE "node" ADD CONSTRAINT "node_fk_switch" FOREIGN KEY ("switch")
-- REFERENCES "device" ("ip") DEFERRABLE;
--
-- ALTER TABLE "node" ADD CONSTRAINT "node_fk_switch_port" FOREIGN KEY ("switch", "port")
-- REFERENCES "device_port" ("ip", "port") ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE;
--
-- ALTER TABLE "node" ADD CONSTRAINT "node_fk_oui" FOREIGN KEY ("oui")
-- REFERENCES "oui" ("oui") DEFERRABLE;
--

View File

@@ -0,0 +1,438 @@
--
-- Created by SQL::Translator::Producer::PostgreSQL
-- Created on Wed Oct 10 15:38:36 2012
--
--
-- Table: admin.
--
CREATE TABLE "admin" (
"job" serial NOT NULL,
"entered" timestamp DEFAULT current_timestamp,
"started" timestamp,
"finished" timestamp,
"device" inet,
"port" text,
"action" text,
"subaction" text,
"status" text,
"username" text,
"userip" inet,
"log" text,
"debug" boolean
);
--
-- Table: device.
--
CREATE TABLE "device" (
"ip" inet NOT NULL,
"creation" timestamp DEFAULT current_timestamp,
"dns" text,
"description" text,
"uptime" bigint,
"contact" text,
"name" text,
"location" text,
"layers" character varying(8),
"ports" integer,
"mac" macaddr,
"serial" text,
"model" text,
"ps1_type" text,
"ps2_type" text,
"ps1_status" text,
"ps2_status" text,
"fan" text,
"slots" integer,
"vendor" text,
"os" text,
"os_ver" text,
"log" text,
"snmp_ver" integer,
"snmp_comm" text,
"snmp_class" text,
"vtp_domain" text,
"last_discover" timestamp,
"last_macsuck" timestamp,
"last_arpnip" timestamp,
PRIMARY KEY ("ip")
);
--
-- Table: device_module.
--
CREATE TABLE "device_module" (
"ip" inet NOT NULL,
"index" integer NOT NULL,
"description" text,
"type" text,
"parent" integer,
"name" text,
"class" text,
"pos" integer,
"hw_ver" text,
"fw_ver" text,
"sw_ver" text,
"serial" text,
"model" text,
"fru" boolean,
"creation" timestamp DEFAULT current_timestamp,
"last_discover" timestamp,
PRIMARY KEY ("ip", "index")
);
--
-- Table: device_port_log.
--
CREATE TABLE "device_port_log" (
"id" serial NOT NULL,
"ip" inet,
"port" text,
"reason" text,
"log" text,
"username" text,
"userip" inet,
"action" text,
"creation" timestamp DEFAULT current_timestamp
);
--
-- Table: device_port_power.
--
CREATE TABLE "device_port_power" (
"ip" inet NOT NULL,
"port" text NOT NULL,
"module" integer,
"admin" text,
"status" text,
"class" text,
"power" integer,
PRIMARY KEY ("port", "ip")
);
--
-- Table: device_port_ssid.
--
CREATE TABLE "device_port_ssid" (
"ip" inet,
"port" text,
"ssid" text,
"broadcast" boolean,
"bssid" macaddr
);
--
-- Table: device_port_wireless.
--
CREATE TABLE "device_port_wireless" (
"ip" inet,
"port" text,
"channel" integer,
"power" integer
);
--
-- Table: device_power.
--
CREATE TABLE "device_power" (
"ip" inet NOT NULL,
"module" integer NOT NULL,
"power" integer,
"status" text,
PRIMARY KEY ("ip", "module")
);
--
-- Table: device_route.
--
CREATE TABLE "device_route" (
"ip" inet NOT NULL,
"network" cidr NOT NULL,
"creation" timestamp DEFAULT current_timestamp,
"dest" inet NOT NULL,
"last_discover" timestamp DEFAULT current_timestamp,
PRIMARY KEY ("ip", "network", "dest")
);
--
-- Table: log.
--
CREATE TABLE "log" (
"id" serial NOT NULL,
"creation" timestamp DEFAULT current_timestamp,
"class" text,
"entry" text,
"logfile" text
);
--
-- Table: node_ip.
--
CREATE TABLE "node_ip" (
"mac" macaddr NOT NULL,
"ip" inet NOT NULL,
"active" boolean,
"time_first" timestamp DEFAULT current_timestamp,
"time_last" timestamp DEFAULT current_timestamp,
"dns" text,
PRIMARY KEY ("mac", "ip")
);
--
-- Table: node_monitor.
--
CREATE TABLE "node_monitor" (
"mac" macaddr NOT NULL,
"active" boolean,
"why" text,
"cc" text,
"date" timestamp DEFAULT current_timestamp,
PRIMARY KEY ("mac")
);
--
-- Table: node_nbt.
--
CREATE TABLE "node_nbt" (
"mac" macaddr NOT NULL,
"ip" inet,
"nbname" text,
"domain" text,
"server" boolean,
"nbuser" text,
"active" boolean,
"time_first" timestamp DEFAULT current_timestamp,
"time_last" timestamp DEFAULT current_timestamp,
PRIMARY KEY ("mac")
);
--
-- Table: node_wireless.
--
CREATE TABLE "node_wireless" (
"mac" macaddr NOT NULL,
"uptime" integer,
"maxrate" integer,
"txrate" integer,
"sigstrength" integer,
"sigqual" integer,
"rxpkt" integer,
"txpkt" integer,
"rxbyte" bigint,
"txbyte" bigint,
"time_last" timestamp DEFAULT current_timestamp,
PRIMARY KEY ("mac")
);
--
-- Table: oui.
--
CREATE TABLE "oui" (
"oui" character varying(8) NOT NULL,
"company" text,
PRIMARY KEY ("oui")
);
--
-- Table: process.
--
CREATE TABLE "process" (
"controller" integer NOT NULL,
"device" inet NOT NULL,
"action" text NOT NULL,
"status" text,
"count" integer,
"creation" timestamp DEFAULT current_timestamp
);
--
-- Table: sessions.
--
CREATE TABLE "sessions" (
"id" character(32) NOT NULL,
"creation" timestamp DEFAULT current_timestamp,
"a_session" text,
PRIMARY KEY ("id")
);
--
-- Table: subnets.
--
CREATE TABLE "subnets" (
"net" cidr NOT NULL,
"creation" timestamp DEFAULT current_timestamp,
"last_discover" timestamp DEFAULT current_timestamp,
PRIMARY KEY ("net")
);
--
-- Table: topology.
--
CREATE TABLE "topology" (
"dev1" inet NOT NULL,
"port1" text NOT NULL,
"dev2" inet NOT NULL,
"port2" text NOT NULL
);
--
-- Table: user_log.
--
CREATE TABLE "user_log" (
"entry" serial NOT NULL,
"username" character varying(50),
"userip" inet,
"event" text,
"details" text,
"creation" timestamp DEFAULT current_timestamp
);
--
-- Table: users.
--
CREATE TABLE "users" (
"username" character varying(50) NOT NULL,
"password" text,
"creation" timestamp DEFAULT current_timestamp,
"last_on" timestamp,
"port_control" boolean DEFAULT false,
"ldap" boolean DEFAULT false,
"admin" boolean DEFAULT false,
"fullname" text,
"note" text,
PRIMARY KEY ("username")
);
--
-- Table: device_vlan.
--
CREATE TABLE "device_vlan" (
"ip" inet NOT NULL,
"vlan" integer NOT NULL,
"description" text,
"creation" timestamp DEFAULT current_timestamp,
"last_discover" timestamp DEFAULT current_timestamp,
PRIMARY KEY ("ip", "vlan")
);
CREATE INDEX "device_vlan_idx_ip" on "device_vlan" ("ip");
--
-- Table: device_ip.
--
CREATE TABLE "device_ip" (
"ip" inet NOT NULL,
"alias" inet NOT NULL,
"subnet" cidr,
"port" text,
"dns" text,
"creation" timestamp DEFAULT current_timestamp,
PRIMARY KEY ("ip", "alias"),
CONSTRAINT "device_ip_alias" UNIQUE ("alias")
);
CREATE INDEX "device_ip_idx_ip" on "device_ip" ("ip");
CREATE INDEX "device_ip_idx_ip_port" on "device_ip" ("ip", "port");
--
-- Table: device_port.
--
CREATE TABLE "device_port" (
"ip" inet NOT NULL,
"port" text NOT NULL,
"creation" timestamp DEFAULT current_timestamp,
"descr" text,
"up" text,
"up_admin" text,
"type" text,
"duplex" text,
"duplex_admin" text,
"speed" text,
"name" text,
"mac" macaddr,
"mtu" integer,
"stp" text,
"remote_ip" inet,
"remote_port" text,
"remote_type" text,
"remote_id" text,
"vlan" text,
"pvid" integer,
"lastchange" bigint,
PRIMARY KEY ("port", "ip")
);
CREATE INDEX "device_port_idx_ip" on "device_port" ("ip");
CREATE INDEX "device_port_idx_remote_ip" on "device_port" ("remote_ip");
--
-- Table: device_port_vlan.
--
CREATE TABLE "device_port_vlan" (
"ip" inet NOT NULL,
"port" text NOT NULL,
"vlan" integer NOT NULL,
"native" boolean DEFAULT false NOT NULL,
"creation" timestamp DEFAULT current_timestamp,
"last_discover" timestamp DEFAULT current_timestamp,
"vlantype" text,
PRIMARY KEY ("ip", "port", "vlan")
);
CREATE INDEX "device_port_vlan_idx_ip" on "device_port_vlan" ("ip");
CREATE INDEX "device_port_vlan_idx_ip_port" on "device_port_vlan" ("ip", "port");
CREATE INDEX "device_port_vlan_idx_ip_vlan" on "device_port_vlan" ("ip", "vlan");
--
-- Table: node.
--
CREATE TABLE "node" (
"mac" macaddr NOT NULL,
"switch" inet NOT NULL,
"port" text NOT NULL,
"active" boolean,
"oui" character varying(8),
"time_first" timestamp DEFAULT current_timestamp,
"time_recent" timestamp DEFAULT current_timestamp,
"time_last" timestamp DEFAULT current_timestamp,
PRIMARY KEY ("mac", "switch", "port")
);
CREATE INDEX "node_idx_switch" on "node" ("switch");
CREATE INDEX "node_idx_switch_port" on "node" ("switch", "port");
CREATE INDEX "node_idx_oui" on "node" ("oui");
-- Not used in Netdisco, because they upset the legacy netdisco.pm code
--
-- --
-- -- Foreign Key Definitions
-- --
--
-- ALTER TABLE "device_vlan" ADD CONSTRAINT "device_vlan_fk_ip" FOREIGN KEY ("ip")
-- REFERENCES "device" ("ip") ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE;
--
-- ALTER TABLE "device_ip" ADD CONSTRAINT "device_ip_fk_ip" FOREIGN KEY ("ip")
-- REFERENCES "device" ("ip") ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE;
--
-- ALTER TABLE "device_ip" ADD CONSTRAINT "device_ip_fk_ip_port" FOREIGN KEY ("ip", "port")
-- REFERENCES "device_port" ("ip", "port") DEFERRABLE;
--
-- ALTER TABLE "device_port" ADD CONSTRAINT "device_port_fk_ip" FOREIGN KEY ("ip")
-- REFERENCES "device" ("ip") ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE;
--
-- ALTER TABLE "device_port" ADD CONSTRAINT "device_port_fk_remote_ip" FOREIGN KEY ("remote_ip")
-- REFERENCES "device_ip" ("alias") DEFERRABLE;
--
-- ALTER TABLE "device_port_vlan" ADD CONSTRAINT "device_port_vlan_fk_ip" FOREIGN KEY ("ip")
-- REFERENCES "device" ("ip") ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE;
--
-- ALTER TABLE "device_port_vlan" ADD CONSTRAINT "device_port_vlan_fk_ip_port" FOREIGN KEY ("ip", "port")
-- REFERENCES "device_port" ("ip", "port") DEFERRABLE;
--
-- ALTER TABLE "device_port_vlan" ADD CONSTRAINT "device_port_vlan_fk_ip_vlan" FOREIGN KEY ("ip", "vlan")
-- REFERENCES "device_vlan" ("ip", "vlan") DEFERRABLE;
--
-- ALTER TABLE "node" ADD CONSTRAINT "node_fk_switch" FOREIGN KEY ("switch")
-- REFERENCES "device" ("ip") DEFERRABLE;
--
-- ALTER TABLE "node" ADD CONSTRAINT "node_fk_switch_port" FOREIGN KEY ("switch", "port")
-- REFERENCES "device_port" ("ip", "port") ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE;
--
-- ALTER TABLE "node" ADD CONSTRAINT "node_fk_oui" FOREIGN KEY ("oui")
-- REFERENCES "oui" ("oui") DEFERRABLE;
--

View File

@@ -0,0 +1,3 @@
--
-- Created by SQL::Translator::Producer::PostgreSQL
-- Created on Wed Oct 10 14:24:20 2012

View File

@@ -0,0 +1,19 @@
-- Convert schema '/home/sy0/git/netdisco-frontend-sandpit/Netdisco/lib/Netdisco/DB/schema_versions/Netdisco-DB-1-PostgreSQL.sql' to '/home/sy0/git/netdisco-frontend-sandpit/Netdisco/lib/Netdisco/DB/schema_versions/Netdisco-DB-2-PostgreSQL.sql':;
BEGIN;
CREATE TABLE "topology" (
"dev1" inet NOT NULL,
"port1" text NOT NULL,
"dev2" inet NOT NULL,
"port2" text NOT NULL
);
ALTER TABLE device_port_ssid ADD COLUMN bssid macaddr;
ALTER TABLE device_port_vlan ADD COLUMN vlantype text;
ALTER TABLE node_ip ADD COLUMN dns text;
COMMIT;

View File

@@ -0,0 +1,450 @@
--
-- Created by SQL::Translator::Producer::PostgreSQL
-- Created on Wed Oct 10 14:24:20 2012
--
--
-- Table: admin.
--
DROP TABLE "admin" CASCADE;
CREATE TABLE "admin" (
"job" serial NOT NULL,
"entered" timestamp DEFAULT current_timestamp,
"started" timestamp,
"finished" timestamp,
"device" inet,
"port" text,
"action" text,
"subaction" text,
"status" text,
"username" text,
"userip" inet,
"log" text,
"debug" boolean
);
--
-- Table: device.
--
DROP TABLE "device" CASCADE;
CREATE TABLE "device" (
"ip" inet NOT NULL,
"creation" timestamp DEFAULT current_timestamp,
"dns" text,
"description" text,
"uptime" bigint,
"contact" text,
"name" text,
"location" text,
"layers" character varying(8),
"ports" integer,
"mac" macaddr,
"serial" text,
"model" text,
"ps1_type" text,
"ps2_type" text,
"ps1_status" text,
"ps2_status" text,
"fan" text,
"slots" integer,
"vendor" text,
"os" text,
"os_ver" text,
"log" text,
"snmp_ver" integer,
"snmp_comm" text,
"snmp_class" text,
"vtp_domain" text,
"last_discover" timestamp,
"last_macsuck" timestamp,
"last_arpnip" timestamp,
PRIMARY KEY ("ip")
);
--
-- Table: device_module.
--
DROP TABLE "device_module" CASCADE;
CREATE TABLE "device_module" (
"ip" inet NOT NULL,
"index" integer NOT NULL,
"description" text,
"type" text,
"parent" integer,
"name" text,
"class" text,
"pos" integer,
"hw_ver" text,
"fw_ver" text,
"sw_ver" text,
"serial" text,
"model" text,
"fru" boolean,
"creation" timestamp DEFAULT current_timestamp,
"last_discover" timestamp,
PRIMARY KEY ("ip", "index")
);
--
-- Table: device_port_log.
--
DROP TABLE "device_port_log" CASCADE;
CREATE TABLE "device_port_log" (
"id" serial NOT NULL,
"ip" inet,
"port" text,
"reason" text,
"log" text,
"username" text,
"userip" inet,
"action" text,
"creation" timestamp DEFAULT current_timestamp
);
--
-- Table: device_port_power.
--
DROP TABLE "device_port_power" CASCADE;
CREATE TABLE "device_port_power" (
"ip" inet NOT NULL,
"port" text NOT NULL,
"module" integer,
"admin" text,
"status" text,
"class" text,
"power" integer,
PRIMARY KEY ("port", "ip")
);
--
-- Table: device_port_ssid.
--
DROP TABLE "device_port_ssid" CASCADE;
CREATE TABLE "device_port_ssid" (
"ip" inet,
"port" text,
"ssid" text,
"broadcast" boolean
);
--
-- Table: device_port_wireless.
--
DROP TABLE "device_port_wireless" CASCADE;
CREATE TABLE "device_port_wireless" (
"ip" inet,
"port" text,
"channel" integer,
"power" integer
);
--
-- Table: device_power.
--
DROP TABLE "device_power" CASCADE;
CREATE TABLE "device_power" (
"ip" inet NOT NULL,
"module" integer NOT NULL,
"power" integer,
"status" text,
PRIMARY KEY ("ip", "module")
);
--
-- Table: device_route.
--
DROP TABLE "device_route" CASCADE;
CREATE TABLE "device_route" (
"ip" inet NOT NULL,
"network" cidr NOT NULL,
"creation" timestamp DEFAULT current_timestamp,
"dest" inet NOT NULL,
"last_discover" timestamp DEFAULT current_timestamp,
PRIMARY KEY ("ip", "network", "dest")
);
--
-- Table: log.
--
DROP TABLE "log" CASCADE;
CREATE TABLE "log" (
"id" serial NOT NULL,
"creation" timestamp DEFAULT current_timestamp,
"class" text,
"entry" text,
"logfile" text
);
--
-- Table: node_ip.
--
DROP TABLE "node_ip" CASCADE;
CREATE TABLE "node_ip" (
"mac" macaddr NOT NULL,
"ip" inet NOT NULL,
"active" boolean,
"time_first" timestamp DEFAULT current_timestamp,
"time_last" timestamp DEFAULT current_timestamp,
PRIMARY KEY ("mac", "ip")
);
--
-- Table: node_monitor.
--
DROP TABLE "node_monitor" CASCADE;
CREATE TABLE "node_monitor" (
"mac" macaddr NOT NULL,
"active" boolean,
"why" text,
"cc" text,
"date" timestamp DEFAULT current_timestamp,
PRIMARY KEY ("mac")
);
--
-- Table: node_nbt.
--
DROP TABLE "node_nbt" CASCADE;
CREATE TABLE "node_nbt" (
"mac" macaddr NOT NULL,
"ip" inet,
"nbname" text,
"domain" text,
"server" boolean,
"nbuser" text,
"active" boolean,
"time_first" timestamp DEFAULT current_timestamp,
"time_last" timestamp DEFAULT current_timestamp,
PRIMARY KEY ("mac")
);
--
-- Table: node_wireless.
--
DROP TABLE "node_wireless" CASCADE;
CREATE TABLE "node_wireless" (
"mac" macaddr NOT NULL,
"uptime" integer,
"maxrate" integer,
"txrate" integer,
"sigstrength" integer,
"sigqual" integer,
"rxpkt" integer,
"txpkt" integer,
"rxbyte" bigint,
"txbyte" bigint,
"time_last" timestamp DEFAULT current_timestamp,
PRIMARY KEY ("mac")
);
--
-- Table: oui.
--
DROP TABLE "oui" CASCADE;
CREATE TABLE "oui" (
"oui" character varying(8) NOT NULL,
"company" text,
PRIMARY KEY ("oui")
);
--
-- Table: process.
--
DROP TABLE "process" CASCADE;
CREATE TABLE "process" (
"controller" integer NOT NULL,
"device" inet NOT NULL,
"action" text NOT NULL,
"status" text,
"count" integer,
"creation" timestamp DEFAULT current_timestamp
);
--
-- Table: sessions.
--
DROP TABLE "sessions" CASCADE;
CREATE TABLE "sessions" (
"id" character(32) NOT NULL,
"creation" timestamp DEFAULT current_timestamp,
"a_session" text,
PRIMARY KEY ("id")
);
--
-- Table: subnets.
--
DROP TABLE "subnets" CASCADE;
CREATE TABLE "subnets" (
"net" cidr NOT NULL,
"creation" timestamp DEFAULT current_timestamp,
"last_discover" timestamp DEFAULT current_timestamp,
PRIMARY KEY ("net")
);
--
-- Table: user_log.
--
DROP TABLE "user_log" CASCADE;
CREATE TABLE "user_log" (
"entry" serial NOT NULL,
"username" character varying(50),
"userip" inet,
"event" text,
"details" text,
"creation" timestamp DEFAULT current_timestamp
);
--
-- Table: users.
--
DROP TABLE "users" CASCADE;
CREATE TABLE "users" (
"username" character varying(50) NOT NULL,
"password" text,
"creation" timestamp DEFAULT current_timestamp,
"last_on" timestamp,
"port_control" boolean DEFAULT false,
"ldap" boolean DEFAULT false,
"admin" boolean DEFAULT false,
"fullname" text,
"note" text,
PRIMARY KEY ("username")
);
--
-- Table: device_vlan.
--
DROP TABLE "device_vlan" CASCADE;
CREATE TABLE "device_vlan" (
"ip" inet NOT NULL,
"vlan" integer NOT NULL,
"description" text,
"creation" timestamp DEFAULT current_timestamp,
"last_discover" timestamp DEFAULT current_timestamp,
PRIMARY KEY ("ip", "vlan")
);
CREATE INDEX "device_vlan_idx_ip" on "device_vlan" ("ip");
--
-- Table: device_ip.
--
DROP TABLE "device_ip" CASCADE;
CREATE TABLE "device_ip" (
"ip" inet NOT NULL,
"alias" inet NOT NULL,
"subnet" cidr,
"port" text,
"dns" text,
"creation" timestamp DEFAULT current_timestamp,
PRIMARY KEY ("ip", "alias"),
CONSTRAINT "device_ip_alias" UNIQUE ("alias")
);
CREATE INDEX "device_ip_idx_ip" on "device_ip" ("ip");
CREATE INDEX "device_ip_idx_ip_port" on "device_ip" ("ip", "port");
--
-- Table: device_port.
--
DROP TABLE "device_port" CASCADE;
CREATE TABLE "device_port" (
"ip" inet NOT NULL,
"port" text NOT NULL,
"creation" timestamp DEFAULT current_timestamp,
"descr" text,
"up" text,
"up_admin" text,
"type" text,
"duplex" text,
"duplex_admin" text,
"speed" text,
"name" text,
"mac" macaddr,
"mtu" integer,
"stp" text,
"remote_ip" inet,
"remote_port" text,
"remote_type" text,
"remote_id" text,
"vlan" text,
"pvid" integer,
"lastchange" bigint,
PRIMARY KEY ("port", "ip")
);
CREATE INDEX "device_port_idx_ip" on "device_port" ("ip");
CREATE INDEX "device_port_idx_remote_ip" on "device_port" ("remote_ip");
--
-- Table: device_port_vlan.
--
DROP TABLE "device_port_vlan" CASCADE;
CREATE TABLE "device_port_vlan" (
"ip" inet NOT NULL,
"port" text NOT NULL,
"vlan" integer NOT NULL,
"native" boolean DEFAULT false NOT NULL,
"creation" timestamp DEFAULT current_timestamp,
"last_discover" timestamp DEFAULT current_timestamp,
PRIMARY KEY ("ip", "port", "vlan")
);
CREATE INDEX "device_port_vlan_idx_ip" on "device_port_vlan" ("ip");
CREATE INDEX "device_port_vlan_idx_ip_port" on "device_port_vlan" ("ip", "port");
CREATE INDEX "device_port_vlan_idx_ip_vlan" on "device_port_vlan" ("ip", "vlan");
--
-- Table: node.
--
DROP TABLE "node" CASCADE;
CREATE TABLE "node" (
"mac" macaddr NOT NULL,
"switch" inet NOT NULL,
"port" text NOT NULL,
"active" boolean,
"oui" character varying(8),
"time_first" timestamp DEFAULT current_timestamp,
"time_recent" timestamp DEFAULT current_timestamp,
"time_last" timestamp DEFAULT current_timestamp,
PRIMARY KEY ("mac", "switch", "port")
);
CREATE INDEX "node_idx_switch" on "node" ("switch");
CREATE INDEX "node_idx_switch_port" on "node" ("switch", "port");
CREATE INDEX "node_idx_oui" on "node" ("oui");
-- Not used in Netdisco, because they upset the legacy netdisco.pm code
--
-- --
-- -- Foreign Key Definitions
-- --
--
-- ALTER TABLE "device_vlan" ADD CONSTRAINT "device_vlan_fk_ip" FOREIGN KEY ("ip")
-- REFERENCES "device" ("ip") ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE;
--
-- ALTER TABLE "device_ip" ADD CONSTRAINT "device_ip_fk_ip" FOREIGN KEY ("ip")
-- REFERENCES "device" ("ip") ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE;
--
-- ALTER TABLE "device_ip" ADD CONSTRAINT "device_ip_fk_ip_port" FOREIGN KEY ("ip", "port")
-- REFERENCES "device_port" ("ip", "port") DEFERRABLE;
--
-- ALTER TABLE "device_port" ADD CONSTRAINT "device_port_fk_ip" FOREIGN KEY ("ip")
-- REFERENCES "device" ("ip") ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE;
--
-- ALTER TABLE "device_port" ADD CONSTRAINT "device_port_fk_remote_ip" FOREIGN KEY ("remote_ip")
-- REFERENCES "device_ip" ("alias") DEFERRABLE;
--
-- ALTER TABLE "device_port_vlan" ADD CONSTRAINT "device_port_vlan_fk_ip" FOREIGN KEY ("ip")
-- REFERENCES "device" ("ip") ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE;
--
-- ALTER TABLE "device_port_vlan" ADD CONSTRAINT "device_port_vlan_fk_ip_port" FOREIGN KEY ("ip", "port")
-- REFERENCES "device_port" ("ip", "port") DEFERRABLE;
--
-- ALTER TABLE "device_port_vlan" ADD CONSTRAINT "device_port_vlan_fk_ip_vlan" FOREIGN KEY ("ip", "vlan")
-- REFERENCES "device_vlan" ("ip", "vlan") DEFERRABLE;
--
-- ALTER TABLE "node" ADD CONSTRAINT "node_fk_switch" FOREIGN KEY ("switch")
-- REFERENCES "device" ("ip") DEFERRABLE;
--
-- ALTER TABLE "node" ADD CONSTRAINT "node_fk_switch_port" FOREIGN KEY ("switch", "port")
-- REFERENCES "device_port" ("ip", "port") ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE;
--
-- ALTER TABLE "node" ADD CONSTRAINT "node_fk_oui" FOREIGN KEY ("oui")
-- REFERENCES "oui" ("oui") DEFERRABLE;
--

View File

@@ -0,0 +1,464 @@
--
-- Created by SQL::Translator::Producer::PostgreSQL
-- Created on Wed Oct 10 15:38:36 2012
--
--
-- Table: admin.
--
DROP TABLE "admin" CASCADE;
CREATE TABLE "admin" (
"job" serial NOT NULL,
"entered" timestamp DEFAULT current_timestamp,
"started" timestamp,
"finished" timestamp,
"device" inet,
"port" text,
"action" text,
"subaction" text,
"status" text,
"username" text,
"userip" inet,
"log" text,
"debug" boolean
);
--
-- Table: device.
--
DROP TABLE "device" CASCADE;
CREATE TABLE "device" (
"ip" inet NOT NULL,
"creation" timestamp DEFAULT current_timestamp,
"dns" text,
"description" text,
"uptime" bigint,
"contact" text,
"name" text,
"location" text,
"layers" character varying(8),
"ports" integer,
"mac" macaddr,
"serial" text,
"model" text,
"ps1_type" text,
"ps2_type" text,
"ps1_status" text,
"ps2_status" text,
"fan" text,
"slots" integer,
"vendor" text,
"os" text,
"os_ver" text,
"log" text,
"snmp_ver" integer,
"snmp_comm" text,
"snmp_class" text,
"vtp_domain" text,
"last_discover" timestamp,
"last_macsuck" timestamp,
"last_arpnip" timestamp,
PRIMARY KEY ("ip")
);
--
-- Table: device_module.
--
DROP TABLE "device_module" CASCADE;
CREATE TABLE "device_module" (
"ip" inet NOT NULL,
"index" integer NOT NULL,
"description" text,
"type" text,
"parent" integer,
"name" text,
"class" text,
"pos" integer,
"hw_ver" text,
"fw_ver" text,
"sw_ver" text,
"serial" text,
"model" text,
"fru" boolean,
"creation" timestamp DEFAULT current_timestamp,
"last_discover" timestamp,
PRIMARY KEY ("ip", "index")
);
--
-- Table: device_port_log.
--
DROP TABLE "device_port_log" CASCADE;
CREATE TABLE "device_port_log" (
"id" serial NOT NULL,
"ip" inet,
"port" text,
"reason" text,
"log" text,
"username" text,
"userip" inet,
"action" text,
"creation" timestamp DEFAULT current_timestamp
);
--
-- Table: device_port_power.
--
DROP TABLE "device_port_power" CASCADE;
CREATE TABLE "device_port_power" (
"ip" inet NOT NULL,
"port" text NOT NULL,
"module" integer,
"admin" text,
"status" text,
"class" text,
"power" integer,
PRIMARY KEY ("port", "ip")
);
--
-- Table: device_port_ssid.
--
DROP TABLE "device_port_ssid" CASCADE;
CREATE TABLE "device_port_ssid" (
"ip" inet,
"port" text,
"ssid" text,
"broadcast" boolean,
"bssid" macaddr
);
--
-- Table: device_port_wireless.
--
DROP TABLE "device_port_wireless" CASCADE;
CREATE TABLE "device_port_wireless" (
"ip" inet,
"port" text,
"channel" integer,
"power" integer
);
--
-- Table: device_power.
--
DROP TABLE "device_power" CASCADE;
CREATE TABLE "device_power" (
"ip" inet NOT NULL,
"module" integer NOT NULL,
"power" integer,
"status" text,
PRIMARY KEY ("ip", "module")
);
--
-- Table: device_route.
--
DROP TABLE "device_route" CASCADE;
CREATE TABLE "device_route" (
"ip" inet NOT NULL,
"network" cidr NOT NULL,
"creation" timestamp DEFAULT current_timestamp,
"dest" inet NOT NULL,
"last_discover" timestamp DEFAULT current_timestamp,
PRIMARY KEY ("ip", "network", "dest")
);
--
-- Table: log.
--
DROP TABLE "log" CASCADE;
CREATE TABLE "log" (
"id" serial NOT NULL,
"creation" timestamp DEFAULT current_timestamp,
"class" text,
"entry" text,
"logfile" text
);
--
-- Table: node_ip.
--
DROP TABLE "node_ip" CASCADE;
CREATE TABLE "node_ip" (
"mac" macaddr NOT NULL,
"ip" inet NOT NULL,
"active" boolean,
"time_first" timestamp DEFAULT current_timestamp,
"time_last" timestamp DEFAULT current_timestamp,
"dns" text,
PRIMARY KEY ("mac", "ip")
);
--
-- Table: node_monitor.
--
DROP TABLE "node_monitor" CASCADE;
CREATE TABLE "node_monitor" (
"mac" macaddr NOT NULL,
"active" boolean,
"why" text,
"cc" text,
"date" timestamp DEFAULT current_timestamp,
PRIMARY KEY ("mac")
);
--
-- Table: node_nbt.
--
DROP TABLE "node_nbt" CASCADE;
CREATE TABLE "node_nbt" (
"mac" macaddr NOT NULL,
"ip" inet,
"nbname" text,
"domain" text,
"server" boolean,
"nbuser" text,
"active" boolean,
"time_first" timestamp DEFAULT current_timestamp,
"time_last" timestamp DEFAULT current_timestamp,
PRIMARY KEY ("mac")
);
--
-- Table: node_wireless.
--
DROP TABLE "node_wireless" CASCADE;
CREATE TABLE "node_wireless" (
"mac" macaddr NOT NULL,
"uptime" integer,
"maxrate" integer,
"txrate" integer,
"sigstrength" integer,
"sigqual" integer,
"rxpkt" integer,
"txpkt" integer,
"rxbyte" bigint,
"txbyte" bigint,
"time_last" timestamp DEFAULT current_timestamp,
PRIMARY KEY ("mac")
);
--
-- Table: oui.
--
DROP TABLE "oui" CASCADE;
CREATE TABLE "oui" (
"oui" character varying(8) NOT NULL,
"company" text,
PRIMARY KEY ("oui")
);
--
-- Table: process.
--
DROP TABLE "process" CASCADE;
CREATE TABLE "process" (
"controller" integer NOT NULL,
"device" inet NOT NULL,
"action" text NOT NULL,
"status" text,
"count" integer,
"creation" timestamp DEFAULT current_timestamp
);
--
-- Table: sessions.
--
DROP TABLE "sessions" CASCADE;
CREATE TABLE "sessions" (
"id" character(32) NOT NULL,
"creation" timestamp DEFAULT current_timestamp,
"a_session" text,
PRIMARY KEY ("id")
);
--
-- Table: subnets.
--
DROP TABLE "subnets" CASCADE;
CREATE TABLE "subnets" (
"net" cidr NOT NULL,
"creation" timestamp DEFAULT current_timestamp,
"last_discover" timestamp DEFAULT current_timestamp,
PRIMARY KEY ("net")
);
--
-- Table: topology.
--
DROP TABLE "topology" CASCADE;
CREATE TABLE "topology" (
"dev1" inet NOT NULL,
"port1" text NOT NULL,
"dev2" inet NOT NULL,
"port2" text NOT NULL
);
--
-- Table: user_log.
--
DROP TABLE "user_log" CASCADE;
CREATE TABLE "user_log" (
"entry" serial NOT NULL,
"username" character varying(50),
"userip" inet,
"event" text,
"details" text,
"creation" timestamp DEFAULT current_timestamp
);
--
-- Table: users.
--
DROP TABLE "users" CASCADE;
CREATE TABLE "users" (
"username" character varying(50) NOT NULL,
"password" text,
"creation" timestamp DEFAULT current_timestamp,
"last_on" timestamp,
"port_control" boolean DEFAULT false,
"ldap" boolean DEFAULT false,
"admin" boolean DEFAULT false,
"fullname" text,
"note" text,
PRIMARY KEY ("username")
);
--
-- Table: device_vlan.
--
DROP TABLE "device_vlan" CASCADE;
CREATE TABLE "device_vlan" (
"ip" inet NOT NULL,
"vlan" integer NOT NULL,
"description" text,
"creation" timestamp DEFAULT current_timestamp,
"last_discover" timestamp DEFAULT current_timestamp,
PRIMARY KEY ("ip", "vlan")
);
CREATE INDEX "device_vlan_idx_ip" on "device_vlan" ("ip");
--
-- Table: device_ip.
--
DROP TABLE "device_ip" CASCADE;
CREATE TABLE "device_ip" (
"ip" inet NOT NULL,
"alias" inet NOT NULL,
"subnet" cidr,
"port" text,
"dns" text,
"creation" timestamp DEFAULT current_timestamp,
PRIMARY KEY ("ip", "alias"),
CONSTRAINT "device_ip_alias" UNIQUE ("alias")
);
CREATE INDEX "device_ip_idx_ip" on "device_ip" ("ip");
CREATE INDEX "device_ip_idx_ip_port" on "device_ip" ("ip", "port");
--
-- Table: device_port.
--
DROP TABLE "device_port" CASCADE;
CREATE TABLE "device_port" (
"ip" inet NOT NULL,
"port" text NOT NULL,
"creation" timestamp DEFAULT current_timestamp,
"descr" text,
"up" text,
"up_admin" text,
"type" text,
"duplex" text,
"duplex_admin" text,
"speed" text,
"name" text,
"mac" macaddr,
"mtu" integer,
"stp" text,
"remote_ip" inet,
"remote_port" text,
"remote_type" text,
"remote_id" text,
"vlan" text,
"pvid" integer,
"lastchange" bigint,
PRIMARY KEY ("port", "ip")
);
CREATE INDEX "device_port_idx_ip" on "device_port" ("ip");
CREATE INDEX "device_port_idx_remote_ip" on "device_port" ("remote_ip");
--
-- Table: device_port_vlan.
--
DROP TABLE "device_port_vlan" CASCADE;
CREATE TABLE "device_port_vlan" (
"ip" inet NOT NULL,
"port" text NOT NULL,
"vlan" integer NOT NULL,
"native" boolean DEFAULT false NOT NULL,
"creation" timestamp DEFAULT current_timestamp,
"last_discover" timestamp DEFAULT current_timestamp,
"vlantype" text,
PRIMARY KEY ("ip", "port", "vlan")
);
CREATE INDEX "device_port_vlan_idx_ip" on "device_port_vlan" ("ip");
CREATE INDEX "device_port_vlan_idx_ip_port" on "device_port_vlan" ("ip", "port");
CREATE INDEX "device_port_vlan_idx_ip_vlan" on "device_port_vlan" ("ip", "vlan");
--
-- Table: node.
--
DROP TABLE "node" CASCADE;
CREATE TABLE "node" (
"mac" macaddr NOT NULL,
"switch" inet NOT NULL,
"port" text NOT NULL,
"active" boolean,
"oui" character varying(8),
"time_first" timestamp DEFAULT current_timestamp,
"time_recent" timestamp DEFAULT current_timestamp,
"time_last" timestamp DEFAULT current_timestamp,
PRIMARY KEY ("mac", "switch", "port")
);
CREATE INDEX "node_idx_switch" on "node" ("switch");
CREATE INDEX "node_idx_switch_port" on "node" ("switch", "port");
CREATE INDEX "node_idx_oui" on "node" ("oui");
-- Not used in Netdisco, because they upset the legacy netdisco.pm code
--
-- --
-- -- Foreign Key Definitions
-- --
--
-- ALTER TABLE "device_vlan" ADD CONSTRAINT "device_vlan_fk_ip" FOREIGN KEY ("ip")
-- REFERENCES "device" ("ip") ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE;
--
-- ALTER TABLE "device_ip" ADD CONSTRAINT "device_ip_fk_ip" FOREIGN KEY ("ip")
-- REFERENCES "device" ("ip") ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE;
--
-- ALTER TABLE "device_ip" ADD CONSTRAINT "device_ip_fk_ip_port" FOREIGN KEY ("ip", "port")
-- REFERENCES "device_port" ("ip", "port") DEFERRABLE;
--
-- ALTER TABLE "device_port" ADD CONSTRAINT "device_port_fk_ip" FOREIGN KEY ("ip")
-- REFERENCES "device" ("ip") ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE;
--
-- ALTER TABLE "device_port" ADD CONSTRAINT "device_port_fk_remote_ip" FOREIGN KEY ("remote_ip")
-- REFERENCES "device_ip" ("alias") DEFERRABLE;
--
-- ALTER TABLE "device_port_vlan" ADD CONSTRAINT "device_port_vlan_fk_ip" FOREIGN KEY ("ip")
-- REFERENCES "device" ("ip") ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE;
--
-- ALTER TABLE "device_port_vlan" ADD CONSTRAINT "device_port_vlan_fk_ip_port" FOREIGN KEY ("ip", "port")
-- REFERENCES "device_port" ("ip", "port") DEFERRABLE;
--
-- ALTER TABLE "device_port_vlan" ADD CONSTRAINT "device_port_vlan_fk_ip_vlan" FOREIGN KEY ("ip", "vlan")
-- REFERENCES "device_vlan" ("ip", "vlan") DEFERRABLE;
--
-- ALTER TABLE "node" ADD CONSTRAINT "node_fk_switch" FOREIGN KEY ("switch")
-- REFERENCES "device" ("ip") DEFERRABLE;
--
-- ALTER TABLE "node" ADD CONSTRAINT "node_fk_switch_port" FOREIGN KEY ("switch", "port")
-- REFERENCES "device_port" ("ip", "port") ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE;
--
-- ALTER TABLE "node" ADD CONSTRAINT "node_fk_oui" FOREIGN KEY ("oui")
-- REFERENCES "oui" ("oui") DEFERRABLE;
--

View File

@@ -0,0 +1,23 @@
use utf8;
package App::Netdisco::Daemon::DB;
use strict;
use warnings;
use base 'DBIx::Class::Schema';
__PACKAGE__->load_namespaces;
our $VERSION = 1; # schema version used for upgrades, keep as integer
use Path::Class;
use File::Basename;
my (undef, $libpath, undef) = fileparse( $INC{ 'App/Netdisco/Daemon/DB.pm' } );
our $schema_versions_dir = Path::Class::Dir->new($libpath)
->subdir("DB", "schema_versions")->stringify;
__PACKAGE__->load_components(qw/Schema::Versioned/);
__PACKAGE__->upgrade_directory($schema_versions_dir);
1;

View File

@@ -0,0 +1,37 @@
use utf8;
package App::Netdisco::Daemon::DB::Result::Admin;
use strict;
use warnings;
use base 'DBIx::Class::Core';
__PACKAGE__->table("admin");
__PACKAGE__->add_columns(
"job",
{
data_type => "integer",
is_nullable => 0,
},
"started",
{ data_type => "timestamp", is_nullable => 1 },
"finished",
{ data_type => "timestamp", is_nullable => 1 },
"device",
{ data_type => "inet", is_nullable => 1 },
"port",
{ data_type => "text", is_nullable => 1 },
"action",
{ data_type => "text", is_nullable => 1 },
"subaction",
{ data_type => "text", is_nullable => 1 },
"status",
{ data_type => "text", is_nullable => 1 },
"log",
{ data_type => "text", is_nullable => 1 },
"debug",
{ data_type => "boolean", is_nullable => 1 },
);
__PACKAGE__->set_primary_key("job");
1;

View File

@@ -0,0 +1,111 @@
package App::Netdisco::Daemon::Worker::Interactive;
use Dancer qw/:moose :syntax :script/;
use Dancer::Plugin::DBIC 'schema';
use Try::Tiny;
use Role::Tiny;
use namespace::clean;
# add dispatch methods for interactive actions
with 'App::Netdisco::Daemon::Worker::Interactive::DeviceActions',
'App::Netdisco::Daemon::Worker::Interactive::PortActions';
sub worker_body {
my $self = shift;
# get all pending jobs
my $rs = schema('daemon')->resultset('Admin')->search({
action => [qw/location contact portcontrol portname vlan power/],
status => 'queued',
});
while (1) {
while (my $job = $rs->next) {
my $target = 'set_'. $job->action;
next unless $self->can($target);
# mark job as running
next unless $self->lock_job($job);
# do job
my ($status, $log);
try {
($status, $log) = $self->$target($job);
}
catch { warn "error running job: $_\n" };
# revert to queued status if we failed to action the job
if (not $status) {
$self->revert_job($job->job);
}
else {
# update job state to done/error with log
$self->close_job($job->job, $status, $log);
}
}
# reset iterator so ->next() triggers another DB query
$rs->reset;
$self->gd_sleep( setting('daemon_sleep_time') || 5 );
}
}
sub lock_job {
my ($self, $job) = @_;
my $happy = 1;
# lock db table, check job state is still queued, update to running
try {
my $status_updated = schema('daemon')->txn_do(sub {
my $row = schema('daemon')->resultset('Admin')->find(
{job => $job->job},
{for => 'update'}
);
$happy = 0 if $row->status ne 'queued';
$row->update({status => "running-$$", started => \"datetime('now')" });
});
$happy = 0 if not $status_updated;
}
catch {
warn "error locking job: $_\n";
$happy = 0;
};
return $happy;
}
sub revert_job {
my ($self, $id) = @_;
try {
schema('daemon')->resultset('Admin')
->find($id)
->update({status => 'queued', started => undef});
}
catch { warn "error reverting job: $_\n" };
}
sub close_job {
my ($self, $id, $status, $log) = @_;
try {
my $local = schema('daemon')->resultset('Admin')->find($id);
schema('netdisco')->resultset('Admin')
->find($id)
->update({
status => $status,
log => $log,
started => $local->started,
finished => \'now()',
});
$local->delete;
}
catch { warn "error closing job: $_\n" };
}
1;

View File

@@ -0,0 +1,49 @@
package App::Netdisco::Daemon::Worker::Interactive::DeviceActions;
use App::Netdisco::Util::Connect qw/snmp_connect get_device/;
use App::Netdisco::Daemon::Worker::Interactive::Util ':all';
use Role::Tiny;
use namespace::clean;
sub set_location {
my ($self, $job) = @_;
return _set_device_generic($job->device, 'location', $job->subaction);
}
sub set_contact {
my ($self, $job) = @_;
return _set_device_generic($job->device, 'contact', $job->subaction);
}
sub _set_device_generic {
my ($ip, $slot, $data) = @_;
$data ||= '';
# snmp connect using rw community
my $info = snmp_connect($ip)
or return error("Failed to connect to device [$ip] to update $slot");
my $method = 'set_'. $slot;
my $rv = $info->$method($data);
if (!defined $rv) {
return error(sprintf 'Failed to set %s on [%s]: %s',
$slot, $ip, ($info->error || ''));
}
# confirm the set happened
$info->clear_cache;
my $new_data = ($info->$slot || '');
if ($new_data ne $data) {
return error("Verify of $slot update failed on [$ip]: $new_data");
}
# update netdisco DB
my $device = get_device($ip);
$device->update({$slot => $data});
return done("Updated $slot on [$ip] to [$data]");
}
1;

View File

@@ -0,0 +1,136 @@
package App::Netdisco::Daemon::Worker::Interactive::PortActions;
use App::Netdisco::Util::Connect ':all';
use App::Netdisco::Util::Permissions ':all';
use App::Netdisco::Daemon::Worker::Interactive::Util ':all';
use Role::Tiny;
use namespace::clean;
sub set_portname {
my ($self, $job) = @_;
return _set_port_generic($job, 'alias', 'name');
}
sub set_portcontrol {
my ($self, $job) = @_;
my $port = get_port($job->device, $job->port)
or return error(sprintf "Unknown port name [%s] on device [%s]",
$job->port, $job->device);
my $reconfig_check = port_reconfig_check($port);
return error("Cannot alter port: $reconfig_check")
if length $reconfig_check;
return _set_port_generic($job, 'up_admin');
}
sub set_vlan {
my ($self, $job) = @_;
my $port = get_port($job->device, $job->port)
or return error(sprintf "Unknown port name [%s] on device [%s]",
$job->port, $job->device);
my $port_reconfig_check = port_reconfig_check($port);
return error("Cannot alter port: $port_reconfig_check")
if length $port_reconfig_check;
my $vlan_reconfig_check = vlan_reconfig_check($port);
return error("Cannot alter vlan: $vlan_reconfig_check")
if length $vlan_reconfig_check;
return _set_port_generic($job, 'vlan');
}
sub _set_port_generic {
my ($job, $slot, $column) = @_;
$column ||= $slot;
my $ip = $job->device;
my $pn = $job->port;
(my $data = $job->subaction) =~ s/-\w+//;
my $port = get_port($ip, $pn)
or return error("Unknown port name [$pn] on device [$ip]");
# snmp connect using rw community
my $info = snmp_connect($ip)
or return error("Failed to connect to device [$ip] to control port");
my $iid = get_iid($info, $port)
or return error("Failed to get port ID for [$pn] from [$ip]");
my $method = 'set_i_'. $slot;
my $rv = $info->$method($data, $iid);
if (!defined $rv) {
return error(sprintf 'Failed to set [%s] %s to [%s] on [%s]: %s',
$pn, $slot, $data, $ip, ($info->error || ''));
}
# confirm the set happened
$info->clear_cache;
my $check_method = 'i_'. $slot;
my $state = ($info->$check_method($iid) || '');
if (ref {} ne ref $state or $state->{$iid} ne $data) {
return error("Verify of [$pn] $slot failed on [$ip]");
}
# update netdisco DB
$port->update({$column => $data});
return done("Updated [$pn] $slot status on [$ip] to [$data]");
}
sub set_power {
my ($self, $job) = @_;
my $port = get_port($job->device, $job->port)
or return error(sprintf "Unknown port name [%s] on device [%s]",
$job->port, $job->device);
return error("No PoE service on port [%s] on device [%s]")
unless $port->power;
my $reconfig_check = port_reconfig_check($port);
return error("Cannot alter port: $reconfig_check")
if length $reconfig_check;
my $ip = $job->device;
my $pn = $job->port;
(my $data = $job->subaction) =~ s/-\w+//;
# snmp connect using rw community
my $info = snmp_connect($ip)
or return error("Failed to connect to device [$ip] to control port");
my $powerid = get_powerid($info, $port)
or return error("Failed to get power ID for [$pn] from [$ip]");
my $rv = $info->set_peth_port_admin($data, $powerid);
if (!defined $rv) {
return error(sprintf 'Failed to set [%s] power to [%s] on [%s]: %s',
$pn, $data, $ip, ($info->error || ''));
}
# confirm the set happened
$info->clear_cache;
my $state = ($info->peth_port_admin($powerid) || '');
if (ref {} ne ref $state or $state->{$powerid} ne $data) {
return error("Verify of [$pn] power failed on [$ip]");
}
# update netdisco DB
$port->power->update({
admin => $data,
status => ($data eq 'false' ? 'disabled' : 'searching'),
});
return done("Updated [$pn] power status on [$ip] to [$data]");
}
1;

View File

@@ -0,0 +1,15 @@
package App::Netdisco::Daemon::Worker::Interactive::Util;
# support utilities for Daemon Actions
use base 'Exporter';
our @EXPORT = ();
our @EXPORT_OK = qw/ done error /;
our %EXPORT_TAGS = (
all => [qw/ done error /],
);
sub done { return ('done', shift) }
sub error { return ('error', shift) }
1;

View File

@@ -0,0 +1,123 @@
package App::Netdisco::Daemon::Worker::Manager;
use Dancer qw/:moose :syntax :script/;
use Dancer::Plugin::DBIC 'schema';
use App::Netdisco::Util::DeviceProperties 'is_discoverable';
use Try::Tiny;
use Role::Tiny;
use namespace::clean;
sub worker_begin {
my $self = shift;
my $daemon = schema('daemon');
# deploy local db if not already done
try {
$daemon->storage->dbh_do(sub {
my ($storage, $dbh) = @_;
$dbh->selectrow_arrayref("SELECT * FROM admin WHERE 0 = 1");
});
}
catch { $daemon->txn_do( $daemon->deploy ) };
$daemon->storage->disconnect;
if ($daemon->get_db_version < $daemon->schema_version) {
$daemon->txn_do( $daemon->upgrade );
}
# on start, any jobs previously grabbed by a daemon on this host
# will be reset to "queued", which is the simplest way to restart them.
my $rs = schema('netdisco')->resultset('Admin')->search({
status => "running-$self->{nd_host}"
});
if ($rs->count > 0) {
$daemon->resultset('Admin')->delete;
$rs->update({status => 'queued', started => undef});
}
}
sub worker_body {
my $self = shift;
# get all pending jobs
my $rs = schema('netdisco')->resultset('Admin')
->search({status => 'queued'});
while (1) {
while (my $job = $rs->next) {
# filter for discover_*
next unless is_discoverable($job->device);
# mark job as running
next unless $self->lock_job($job);
# copy job to local queue
$self->copy_job($job)
or $self->revert_job($job->job);
}
# reset iterator so ->next() triggers another DB query
$rs->reset;
$self->gd_sleep( setting('daemon_sleep_time') || 5 );
}
}
sub lock_job {
my ($self, $job) = @_;
my $happy = 1;
# lock db table, check job state is still queued, update to running
try {
my $status_updated = schema('netdisco')->txn_do(sub {
my $row = schema('netdisco')->resultset('Admin')->find(
{job => $job->job},
{for => 'update'}
);
$happy = 0 if $row->status ne 'queued';
$row->update({
status => "running-$self->{nd_host}",
started => \'now()'
});
});
$happy = 0 if not $status_updated;
}
catch {
warn "error locking job: $_\n";
$happy = 0;
};
return $happy;
}
sub copy_job {
my ($self, $job) = @_;
try {
my %data = $job->get_columns;
delete $data{$_} for qw/entered username userip/;
schema('daemon')->resultset('Admin')->update_or_create({
%data, status => 'queued', started => undef,
});
}
catch { warn "error copying job: $_\n" };
}
sub revert_job {
my ($self, $id) = @_;
try {
schema('netdisco')->resultset('Admin')
->find($id)
->update({status => 'queued', started => undef});
}
catch { warn "error reverting job: $_\n" };
}
1;

View File

@@ -0,0 +1,151 @@
package App::Netdisco::Util::Connect;
use Dancer qw/:syntax :script/;
use Dancer::Plugin::DBIC 'schema';
use SNMP::Info;
use Try::Tiny;
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;
COMMUNITY: foreach my $c (@{ setting('community_rw') || []}) {
try {
$info = SNMP::Info->new(%snmp_args, Community => $c);
last COMMUNITY if (
$info
and (not defined $info->error)
and length $info->uptime
);
};
}
return $info;
}
sub _build_mibdirs {
# FIXME: make this cross-platform (Path::Class?)
return map { setting('mibhome') .'/'. $_ }
@{ setting('mibdirs') || [] };
}
1;

View File

@@ -0,0 +1,94 @@
package App::Netdisco::Util::DeviceProperties;
use Dancer qw/:syntax :script/;
use Dancer::Plugin::DBIC 'schema';
use NetAddr::IP::Lite;
use base 'Exporter';
our @EXPORT = ();
our @EXPORT_OK = qw/
is_discoverable
is_vlan_interface port_has_phone
/;
our %EXPORT_TAGS = (
all => [qw/
is_discoverable
is_vlan_interface port_has_phone
/],
);
=head1 App::Netdisco::Util::DeviceProperties;
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 is_discoverable( $ip )
Given an IP address, returns C<true> if Netdisco on this host is permitted to
discover its configuration by the local configuration.
The configuration items C<discover_no> and C<discover_only> are checked
against the given IP.
Returns false if the host is not permitted to discover the target device.
=cut
sub is_discoverable {
my $ip = shift;
my $device = NetAddr::IP::Lite->new($ip) or return 0;
my $discover_no = setting('discover_no') || [];
my $discover_only = setting('discover_only') || [];
if (scalar @$discover_no) {
foreach my $item (@$discover_no) {
my $ip = NetAddr::IP::Lite->new($item) or return 0;
return 0 if $ip->contains($device);
}
}
if (scalar @$discover_only) {
my $okay = 0;
foreach my $item (@$discover_only) {
my $ip = NetAddr::IP::Lite->new($item) or return 0;
++$okay if $ip->contains($device);
}
return 0 if not $okay;
}
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;

View File

@@ -0,0 +1,74 @@
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,117 @@
package App::Netdisco::Util::Web;
use base 'Exporter';
our @EXPORT = ();
our @EXPORT_OK = qw/
sort_port
/;
our %EXPORT_TAGS = (
all => [qw/
sort_port
/],
);
=head1 App::Netdisco::Util::Web
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 sort_port( $a, $b )
Sort port names of various types used by device vendors. Interface is as
Perl's own C<sort> - two input args and an integer return value.
=cut
sub sort_port {
my ($aval, $bval) = @_;
# hack for foundry "10GigabitEthernet" -> cisco-like "TenGigabitEthernet"
$aval = "Ten$1" if $aval =~ qr/^10(GigabitEthernet.+)$/;
$bval = "Ten$1" if $bval =~ qr/^10(GigabitEthernet.+)$/;
my $numbers = qr{^(\d+)$};
my $numeric = qr{^([\d\.]+)$};
my $dotted_numeric = qr{^(\d+)\.(\d+)$};
my $letter_number = qr{^([a-zA-Z]+)(\d+)$};
my $wordcharword = qr{^([^:\/.]+)[\ :\/\.]+([^:\/.]+)(\d+)?$}; #port-channel45
my $netgear = qr{^Slot: (\d+) Port: (\d+) }; # "Slot: 0 Port: 15 Gigabit - Level"
my $ciscofast = qr{^
# Word Number slash (Gigabit0/)
(\D+)(\d+)[\/:]
# Groups of symbol float (/5.5/5.5/5.5), separated by slash or colon
([\/:\.\d]+)
# Optional dash (-Bearer Channel)
(-.*)?
$}x;
my @a = (); my @b = ();
if ($aval =~ $dotted_numeric) {
@a = ($1,$2);
} elsif ($aval =~ $letter_number) {
@a = ($1,$2);
} elsif ($aval =~ $netgear) {
@a = ($1,$2);
} elsif ($aval =~ $numbers) {
@a = ($1);
} elsif ($aval =~ $ciscofast) {
@a = ($2,$1);
push @a, split(/[:\/]/,$3), $4;
} elsif ($aval =~ $wordcharword) {
@a = ($1,$2,$3);
} else {
@a = ($aval);
}
if ($bval =~ $dotted_numeric) {
@b = ($1,$2);
} elsif ($bval =~ $letter_number) {
@b = ($1,$2);
} elsif ($bval =~ $netgear) {
@b = ($1,$2);
} elsif ($bval =~ $numbers) {
@b = ($1);
} elsif ($bval =~ $ciscofast) {
@b = ($2,$1);
push @b, split(/[:\/]/,$3),$4;
} elsif ($bval =~ $wordcharword) {
@b = ($1,$2,$3);
} else {
@b = ($bval);
}
# Equal until proven otherwise
my $val = 0;
while (scalar(@a) or scalar(@b)){
# carried around from the last find.
last if $val != 0;
my $a1 = shift @a;
my $b1 = shift @b;
# A has more components - loses
unless (defined $b1){
$val = 1;
last;
}
# A has less components - wins
unless (defined $a1) {
$val = -1;
last;
}
if ($a1 =~ $numeric and $b1 =~ $numeric){
$val = $a1 <=> $b1;
} elsif ($a1 ne $b1) {
$val = $a1 cmp $b1;
}
}
return $val;
}
1;

View File

@@ -0,0 +1,42 @@
package App::Netdisco::Web;
use Dancer ':syntax';
use Dancer::Plugin::Ajax;
use Dancer::Plugin::DBIC;
use Socket6 (); # to ensure dependency is met
use HTML::Entities (); # to ensure dependency is met
use URI::QueryParam (); # part of URI, to add helper methods
use App::Netdisco::Web::AuthN;
use App::Netdisco::Web::Search;
use App::Netdisco::Web::Device;
use App::Netdisco::Web::PortControl;
use App::Netdisco::Web::Inventory;
hook 'before_template' => sub {
my $tokens = shift;
# allow portable static content
$tokens->{uri_base} = request->base->path
if request->base->path ne '/';
# allow portable dynamic content
$tokens->{uri_for} = \&uri_for;
# allow very long lists of ports
$Template::Directive::WHILE_MAX = 10_000;
};
get '/' => sub {
template 'index';
};
any qr{.*} => sub {
var('notfound' => true);
status 'not_found';
template 'index';
};
true;

View File

@@ -0,0 +1,44 @@
package App::Netdisco::Web::AuthN;
use Dancer ':syntax';
use Dancer::Plugin::DBIC;
use Digest::MD5 ();
hook 'before' => sub {
if (! session('user') && request->path ne uri_for('/login')->path) {
if (setting('no_auth')) {
session(user => 'guest');
}
else {
request->path_info('/');
}
}
if (session('user') && session->id) {
var(user => schema('netdisco')->resultset('User')
->find(session('user')));
}
};
post '/login' => sub {
if (param('username') and param('password')) {
my $user = schema('netdisco')->resultset('User')->find(param('username'));
if ($user) {
my $sum = Digest::MD5::md5_hex(param('password'));
if ($sum and $sum eq $user->password) {
session(user => $user->username);
redirect uri_for('/inventory');
return;
}
}
}
redirect uri_for('/', {failed => 1});
};
get '/logout' => sub {
session->destroy;
redirect uri_for('/', {logout => 1});
};
true;

View File

@@ -0,0 +1,305 @@
package App::Netdisco::Web::Device;
use Dancer ':syntax';
use Dancer::Plugin::Ajax;
use Dancer::Plugin::DBIC;
use NetAddr::IP::Lite ':lower';
use App::Netdisco::Util::Web (); # for sort_port
hook 'before' => sub {
# list of port detail columns
var('port_columns' => [
{ name => 'c_admin', label => 'Port Control', default => '' },
{ name => 'c_port', label => 'Port', default => 'on' },
{ name => 'c_descr', label => 'Description', default => '' },
{ name => 'c_type', label => 'Type', default => '' },
{ name => 'c_duplex', label => 'Duplex', default => '' },
{ name => 'c_lastchange', label => 'Last Change', default => '' },
{ name => 'c_name', label => 'Name', default => 'on' },
{ name => 'c_speed', label => 'Speed', default => '' },
{ name => 'c_mac', label => 'Port MAC', default => '' },
{ name => 'c_mtu', label => 'MTU', default => '' },
{ name => 'c_vlan', label => 'Native VLAN', default => 'on' },
{ name => 'c_vmember', label => 'Tagged VLANs', default => 'on' },
{ name => 'c_power', label => 'PoE', default => '' },
{ name => 'c_nodes', label => 'Connected Nodes', default => '' },
{ name => 'c_neighbors', label => 'Connected Devices', default => 'on' },
{ name => 'c_stp', label => 'Spanning Tree', default => '' },
{ name => 'c_up', label => 'Status', default => '' },
]);
# view settings for port connected devices
var('connected_properties' => [
{ name => 'n_age', label => 'Age Stamp', default => '' },
{ name => 'n_ip', label => 'IP Address', default => 'on' },
{ name => 'n_archived', label => 'Archived Data', default => '' },
]);
# new searches will use these defaults in their sidebars
var('device_ports' => uri_for('/device', {
tab => 'ports',
age_num => 3,
age_unit => 'months',
}));
foreach my $col (@{ var('port_columns') }) {
next unless $col->{default} eq 'on';
var('device_ports')->query_param($col->{name}, 'checked');
}
foreach my $col (@{ var('connected_properties') }) {
next unless $col->{default} eq 'on';
var('device_ports')->query_param($col->{name}, 'checked');
}
if (request->path eq uri_for('/device')->path
or index(request->path, uri_for('/ajax/content/device')->path) == 0) {
foreach my $col (@{ var('port_columns') }) {
next unless $col->{default} eq 'on';
params->{$col->{name}} = 'checked'
if not param('tab') or param('tab') ne 'ports';
}
foreach my $col (@{ var('connected_properties') }) {
next unless $col->{default} eq 'on';
params->{$col->{name}} = 'checked'
if not param('tab') or param('tab') ne 'ports';
}
if (not param('tab') or param('tab') ne 'ports') {
params->{'age_num'} = 3;
params->{'age_unit'} = 'months';
}
# for templates to link to same page with modified query but same options
my $self_uri = uri_for(request->path, scalar params);
$self_uri->query_param_delete('q');
$self_uri->query_param_delete('f');
var('self_options' => $self_uri->query_form_hash);
}
};
ajax '/ajax/content/device/:thing' => sub {
return "<p>Hello, this is where the ". param('thing') ." content goes.</p>";
};
ajax '/ajax/content/device/netmap' => sub {
content_type('text/html');
template 'ajax/device/netmap.tt', {}, { layout => undef };
};
sub _get_name {
my $ip = shift;
my $domain = quotemeta( setting('domain_suffix') || '' );
(my $dns = (var('devices')->{$ip} || '')) =~ s/$domain$//;
return ($dns || $ip);
}
sub _add_children {
my ($ptr, $childs) = @_;
my @legit = ();
foreach my $c (@$childs) {
next if exists var('seen')->{$c};
var('seen')->{$c}++;
push @legit, $c;
push @{$ptr}, { name => _get_name($c), ip => $c };
}
for (my $i = 0; $i < @legit; $i++) {
$ptr->[$i]->{children} = [];
_add_children($ptr->[$i]->{children}, var('links')->{$legit[$i]});
}
}
# d3 seems not to use proper json semantics, so get instead of ajax
get '/ajax/data/device/netmap' => sub {
my $start = param('q');
return unless $start;
my @devices = schema->resultset('Device')->search({}, {
result_class => 'DBIx::Class::ResultClass::HashRefInflator',
columns => ['ip', 'dns'],
})->all;
var(devices => { map { $_->{ip} => $_->{dns} } @devices });
var(links => {});
my $rs = schema->resultset('Virtual::DeviceLinks')->search({}, {
result_class => 'DBIx::Class::ResultClass::HashRefInflator',
});
while (my $l = $rs->next) {
var('links')->{ $l->{left_ip} } ||= [];
push @{ var('links')->{ $l->{left_ip} } }, $l->{right_ip};
}
my %tree = (
ip => $start,
name => _get_name($start),
children => [],
);
var(seen => {$start => 1});
_add_children($tree{children}, var('links')->{$start});
content_type('application/json');
return to_json(\%tree);
};
ajax '/ajax/data/device/alldevicelinks' => sub {
my @devices = schema->resultset('Device')->search({}, {
result_class => 'DBIx::Class::ResultClass::HashRefInflator',
columns => ['ip', 'dns'],
})->all;
var(devices => { map { $_->{ip} => $_->{dns} } @devices });
my $rs = schema->resultset('Virtual::DeviceLinks')->search({}, {
result_class => 'DBIx::Class::ResultClass::HashRefInflator',
});
my %tree = ();
while (my $l = $rs->next) {
push @{ $tree{ _get_name($l->{left_ip} )} },
_get_name($l->{right_ip});
}
content_type('application/json');
return to_json(\%tree);
};
# device interface addresses
ajax '/ajax/content/device/addresses' => sub {
my $ip = param('q');
return unless $ip;
my $set = schema('netdisco')->resultset('DeviceIp')
->search({ip => $ip}, {order_by => 'alias'});
return unless $set->count;
content_type('text/html');
template 'ajax/device/addresses.tt', {
results => $set,
}, { layout => undef };
};
# device ports with a description (er, name) matching
ajax '/ajax/content/device/ports' => sub {
my $ip = param('q');
return unless $ip;
my $set = schema('netdisco')->resultset('DevicePort')
->search({'me.ip' => $ip});
# refine by ports if requested
my $q = param('f');
if ($q) {
if ($q =~ m/^\d+$/) {
$set = $set->search({'me.vlan' => $q});
return unless $set->count;
}
else {
$q =~ s/\*/%/g if index($q, '*') >= 0;
$q =~ s/\?/_/g if index($q, '?') >= 0;
$q = { '-ilike' => $q };
if ($set->search({'me.port' => $q})->count) {
$set = $set->search({'me.port' => $q});
}
else {
$set = $set->search({'me.name' => $q});
return unless $set->count;
}
}
}
# filter for free ports if asked
my $free_filter = (param('free') ? 'only_free_ports' : 'with_is_free');
$set = $set->$free_filter({
age_num => (param('age_num') || 3),
age_unit => (param('age_unit') || 'months')
});
# make sure query asks for formatted timestamps when needed
$set = $set->with_times if param('c_lastchange');
# get number of vlans on the port to control whether to list them or not
$set = $set->with_vlan_count if param('c_vmember');
# what kind of nodes are we interested in?
my $nodes_name = (param('n_archived') ? 'nodes' : 'active_nodes');
$nodes_name .= '_with_age' if param('c_nodes') and param('n_age');
# retrieve active/all connected nodes, if asked for
$set = $set->search_rs({}, { prefetch => [{$nodes_name => 'ips'}] })
if param('c_nodes');
# retrieve neighbor devices, if asked for
$set = $set->search_rs({}, { prefetch => [{neighbor_alias => 'device'}] })
if param('c_neighbors');
# sort ports (empty set would be a 'no records' msg)
my $results = [ sort { &App::Netdisco::Util::Web::sort_port($a->port, $b->port) } $set->all ];
return unless scalar @$results;
content_type('text/html');
template 'ajax/device/ports.tt', {
results => $results,
nodes => $nodes_name,
device => $ip,
}, { layout => undef };
};
# device details table
ajax '/ajax/content/device/details' => sub {
my $ip = param('q');
return unless $ip;
my $device = schema('netdisco')->resultset('Device')
->with_times()->find($ip);
return unless $device;
content_type('text/html');
template 'ajax/device/details.tt', {
d => $device,
}, { layout => undef };
};
# support typeahead with simple AJAX query for device names
ajax '/ajax/data/device/typeahead' => sub {
my $q = param('query');
my $set = schema('netdisco')->resultset('Device')->search_fuzzy($q);
content_type 'application/json';
return to_json [map {$_->dns || $_->name || $_->ip} $set->all];
};
get '/device' => sub {
my $ip = NetAddr::IP::Lite->new(param('q'));
if (! $ip) {
redirect uri_for('/', {nosuchdevice => 1});
return;
}
my $device = schema('netdisco')->resultset('Device')->find($ip->addr);
if (! $device) {
redirect uri_for('/', {nosuchdevice => 1});
return;
}
# list of tabs
var('tabs' => [
{ id => 'details', label => 'Details' },
{ id => 'ports', label => 'Ports' },
{ id => 'modules', label => 'Modules' },
{ id => 'netmap', label => 'Neighbors' },
{ id => 'addresses', label => 'Addresses' },
]);
params->{'tab'} ||= 'details';
template 'device', { d => $device };
};
true;

View File

@@ -0,0 +1,18 @@
package App::Netdisco::Web::Inventory;
use Dancer ':syntax';
use Dancer::Plugin::DBIC;
get '/inventory' => sub {
my $models = schema('netdisco')->resultset('Device')->get_models();
my $releases = schema('netdisco')->resultset('Device')->get_releases();
var(nav => 'inventory');
template 'inventory', {
models => $models,
releases => $releases,
};
};
true;

View File

@@ -0,0 +1,72 @@
package App::Netdisco::Web::PortControl;
use Dancer ':syntax';
use Dancer::Plugin::Ajax;
use Dancer::Plugin::DBIC;
use Try::Tiny;
ajax '/ajax/portcontrol' => sub {
try {
my $log = sprintf 'd:[%s] p:[%s] f:[%s]. a:[%s] v[%s]',
param('device'), (param('port') || ''), param('field'),
(param('action') || ''), (param('value') || '');
my %action_map = (
'location' => 'location',
'contact' => 'contact',
'c_port' => 'portcontrol',
'c_name' => 'portname',
'c_vlan' => 'vlan',
'c_power' => 'power',
);
my $action = $action_map{ param('field') };
my $subaction = ($action =~ m/^(?:power|portcontrol)/
? (param('action') ."-other")
: param('value'));
schema('netdisco')->resultset('Admin')->create({
device => param('device'),
port => param('port'),
action => $action,
subaction => $subaction,
status => 'queued',
username => session('user'),
userip => request->remote_address,
log => $log,
});
}
catch {
send_error('Failed to parse params or add DB record');
};
content_type('application/json');
to_json({});
};
ajax '/ajax/userlog' => sub {
my $user = session('user');
send_error('No username') unless $user;
my $rs = schema('netdisco')->resultset('Admin')->search({
username => $user,
action => [qw/location contact portcontrol portname vlan power/],
finished => { '>' => \"(now() - interval '5 seconds')" },
});
my %status = (
'done' => [
map {s/\[\]/&lt;empty&gt;/; $_}
$rs->search({status => 'done'})->get_column('log')->all
],
'error' => [
map {s/\[\]/&lt;empty&gt;/; $_}
$rs->search({status => 'error'})->get_column('log')->all
],
);
content_type('application/json');
to_json(\%status);
};
true;

View File

@@ -0,0 +1,250 @@
package App::Netdisco::Web::Search;
use Dancer ':syntax';
use Dancer::Plugin::Ajax;
use Dancer::Plugin::DBIC;
use NetAddr::IP::Lite ':lower';
use Net::MAC ();
use List::MoreUtils ();
hook 'before' => sub {
# view settings for node options
var('node_options' => [
{ name => 'stamps', label => 'Time Stamps', default => 'on' },
]);
# view settings for device options
var('device_options' => [
{ name => 'matchall', label => 'Match All Options', default => 'on' },
]);
# new searches will use these defaults in their sidebars
var('search_node' => uri_for('/search', {tab => 'node'}));
var('search_device' => uri_for('/search', {tab => 'device'}));
foreach my $col (@{ var('node_options') }) {
next unless $col->{default} eq 'on';
var('search_node')->query_param($col->{name}, 'checked');
}
foreach my $col (@{ var('device_options') }) {
next unless $col->{default} eq 'on';
var('search_device')->query_param($col->{name}, 'checked');
}
if (request->path eq uri_for('/search')->path
or index(request->path, uri_for('/ajax/content/search')->path) == 0) {
foreach my $col (@{ var('node_options') }) {
next unless $col->{default} eq 'on';
params->{$col->{name}} = 'checked'
if not param('tab') or param('tab') ne 'node';
}
foreach my $col (@{ var('device_options') }) {
next unless $col->{default} eq 'on';
params->{$col->{name}} = 'checked'
if not param('tab') or param('tab') ne 'device';
}
# used in the device search sidebar template to set selected items
foreach my $opt (qw/model vendor os_ver/) {
my $p = (ref [] eq ref param($opt) ? param($opt)
: (param($opt) ? [param($opt)] : []));
var("${opt}_lkp" => { map { $_ => 1 } @$p });
}
}
};
# device with various properties or a default match-all
ajax '/ajax/content/search/device' => sub {
my $has_opt = List::MoreUtils::any {param($_)}
qw/name location dns ip description model os_ver vendor/;
my $set;
if ($has_opt) {
$set = schema('netdisco')->resultset('Device')->search_by_field(scalar params);
}
else {
my $q = param('q');
return unless $q;
$set = schema('netdisco')->resultset('Device')->search_fuzzy($q);
}
return unless $set->count;
content_type('text/html');
template 'ajax/search/device.tt', {
results => $set,
}, { layout => undef };
};
# nodes matching the param as an IP or DNS hostname or MAC
ajax '/ajax/content/search/node' => sub {
my $node = param('q');
return unless $node;
content_type('text/html');
my $mac = Net::MAC->new(mac => $node, 'die' => 0, verbose => 0);
my @active = (param('archived') ? () : (-bool => 'active'));
if (! $mac->get_error) {
my $sightings = schema('netdisco')->resultset('Node')
->search_by_mac({mac => $mac->as_IEEE, @active});
my $ips = schema('netdisco')->resultset('NodeIp')
->search_by_mac({mac => $mac->as_IEEE, @active});
my $ports = schema('netdisco')->resultset('DevicePort')
->search({mac => $mac->as_IEEE});
return unless $sightings->count
or $ips->count
or $ports->count;
template 'ajax/search/node_by_mac.tt', {
ips => $ips,
sightings => $sightings,
ports => $ports,
}, { layout => undef };
}
else {
my $set;
if (my $ip = NetAddr::IP::Lite->new($node)) {
# search_by_ip() will extract cidr notation if necessary
$set = schema('netdisco')->resultset('NodeIp')
->search_by_ip({ip => $ip, @active});
}
else {
if (param('partial')) {
$node = "\%$node\%";
}
elsif (setting('domain_suffix')) {
$node .= setting('domain_suffix')
if index($node, setting('domain_suffix')) == -1;
}
$set = schema('netdisco')->resultset('NodeIp')
->search_by_dns({dns => $node, @active});
}
return unless $set and $set->count;
template 'ajax/search/node_by_ip.tt', {
macs => $set,
archive_filter => {@active},
}, { layout => undef };
}
};
# devices carrying vlan xxx
ajax '/ajax/content/search/vlan' => sub {
my $q = param('q');
return unless $q;
my $set;
if ($q =~ m/^\d+$/) {
$set = schema('netdisco')->resultset('Device')->carrying_vlan({vlan => $q});
}
else {
$set = schema('netdisco')->resultset('Device')->carrying_vlan_name({name => $q});
}
return unless $set->count;
content_type('text/html');
template 'ajax/search/vlan.tt', {
results => $set,
}, { layout => undef };
};
# device ports with a description (er, name) matching
ajax '/ajax/content/search/port' => sub {
my $q = param('q');
return unless $q;
my $set;
if ($q =~ m/^\d+$/) {
$set = schema('netdisco')->resultset('DevicePort')->search({vlan => $q});
}
else {
$set = schema('netdisco')->resultset('DevicePort')->search({name => $q});
}
return unless $set->count;
content_type('text/html');
template 'ajax/search/port.tt', {
results => $set,
}, { layout => undef };
};
get '/search' => sub {
my $q = param('q');
if (not param('tab')) {
if (not $q) {
redirect uri_for('/');
}
# pick most likely tab for initial results
if ($q =~ m/^\d+$/) {
params->{'tab'} = 'vlan';
}
else {
my $s = schema('netdisco');
if ($q =~ m{^[a-f0-9.:/]+$}i) {
my $ip = NetAddr::IP::Lite->new($q);
my $nd = $s->resultset('Device')->search_by_field({ip => $q});
if ($ip and $nd->count) {
if ($nd->count == 1) {
# redirect to device details for the one device
redirect uri_for('/device',
{tab => 'details', q => $q, f => ''});
}
params->{'tab'} = 'device';
}
else {
# this will match for MAC addresses
# and partial IPs (subnets?)
params->{'tab'} = 'node';
}
}
else {
my $nd = $s->resultset('Device')->search({dns => { '-ilike' => "\%$q\%" }});
if ($nd->count) {
if ($nd->count == 1) {
# redirect to device details for the one device
redirect uri_for('/device',
{tab => 'details', q => $nd->first->ip, f => ''});
}
params->{'tab'} = 'device';
}
elsif ($s->resultset('DevicePort')
->search({name => "\%$q\%"})->count) {
params->{'tab'} = 'port';
}
}
params->{'tab'} ||= 'node';
}
}
# used in the device search sidebar to populate select inputs
var('model_list' => [
schema('netdisco')->resultset('Device')->get_distinct_col('model')
]);
var('os_ver_list' => [
schema('netdisco')->resultset('Device')->get_distinct_col('os_ver')
]);
var('vendor_list' => [
schema('netdisco')->resultset('Device')->get_distinct_col('vendor')
]);
# list of tabs
var('tabs' => [
{ id => 'device', label => 'Device' },
{ id => 'node', label => 'Node' },
{ id => 'vlan', label => 'VLAN' },
{ id => 'port', label => 'Port' },
]);
template 'search';
};
true;