* Add macsuck worker to collect various PortAccessEntity (NAC) attributes * Incorporate PAE feedback on #937 * missing Result/Device.pm column added * pae_is... columns instead of pae_capabilities * moved most code to Util/PortAccessEntity.pm so the update can be done in discover and macsuck * Refactor PAE attributes during discover as separate Plugin * PortAccessEntity: don't use device->dns in log string * Fix "Experimental keys on scalar is now forbidden" test failure * Revamp pae_control and add missing attribute - device.pae_control (text) is now device.pae_is_enabled (bool) - also store pae_authconfig_port_control (port mode auto/force(un)Auth) * Fix "Experimental keys on scalar is now forbidden" test failure - ... again because of botched merge - at least perlgolfed away a set of curly braces * Update PortAccessEntity.pm * Incorporate @ollyg PR feedback * allow actions without transport to run when there are also no creds * initial refactor for separate gather, process, store phases for macsuck * factor out the vlan sanity check * additional help with log of action workers * cleanup logic in check macsuck * refactor to make main phases only * some fixes * implement file slurp. amazingly the whole thing works * remove outdated noop from test * treat error as critical, use cancel to suppress further drivers * big refactor to share mac sanity code to both paths * fix inverted logic on vlan sanity filter * some code tidy * fix error in default value * fix for vlan 0 nodes input from cli * ensure imported MACs are IEEE format * add api endpoint, no useful return status yet * exit status if error from nodes PUT * suppress other networked workers when direct workers are active * better log showing worker * fix status recording to get first error or last done message * implement arpnip API PUT * avoid package redeclaration error * make sure write API methods require admin status * add doc for passing JSON data to arpnip and macsuck * update manifest * remove option to do jobs in web handler; all by queue now * use job entry timestamp for offline queued jobs * fix store username and IP on api PUT * never de-duplicate user-submitted jobs; never reset DeviceSkip for offline jobs * myworker no longer needed * make logic cleaner Co-authored-by: Christian Ramseyer <ramseyer@netnea.com>
281 lines
8.7 KiB
Perl
281 lines
8.7 KiB
Perl
package App::Netdisco::Web::API::Objects;
|
|
|
|
use Dancer ':syntax';
|
|
use Dancer::Plugin::DBIC;
|
|
use Dancer::Plugin::Swagger;
|
|
use Dancer::Plugin::Auth::Extensible;
|
|
|
|
use App::Netdisco::JobQueue 'jq_insert';
|
|
use Try::Tiny;
|
|
|
|
swagger_path {
|
|
tags => ['Objects'],
|
|
path => (setting('api_base') || '').'/object/device/{ip}',
|
|
description => 'Returns a row from the device table',
|
|
parameters => [
|
|
ip => {
|
|
description => 'Canonical IP of the Device. Use Search methods to find this.',
|
|
required => 1,
|
|
in => 'path',
|
|
},
|
|
],
|
|
responses => { default => {} },
|
|
}, get '/api/v1/object/device/:ip' => require_role api => sub {
|
|
my $device = try { schema(vars->{'tenant'})->resultset('Device')
|
|
->find( params->{ip} ) } or send_error('Bad Device', 404);
|
|
return to_json $device->TO_JSON;
|
|
};
|
|
|
|
foreach my $rel (qw/device_ips vlans ports modules port_vlans wireless_ports ssids powered_ports/) {
|
|
swagger_path {
|
|
tags => ['Objects'],
|
|
path => (setting('api_base') || '')."/object/device/{ip}/$rel",
|
|
description => "Returns $rel rows for a given device",
|
|
parameters => [
|
|
ip => {
|
|
description => 'Canonical IP of the Device. Use Search methods to find this.',
|
|
required => 1,
|
|
in => 'path',
|
|
},
|
|
],
|
|
responses => { default => {} },
|
|
}, get "/api/v1/object/device/:ip/$rel" => require_role api => sub {
|
|
my $rows = try { schema(vars->{'tenant'})->resultset('Device')
|
|
->find( params->{ip} )->$rel } or send_error('Bad Device', 404);
|
|
return to_json [ map {$_->TO_JSON} $rows->all ];
|
|
};
|
|
}
|
|
|
|
foreach my $rel (qw/nodes active_nodes nodes_with_age active_nodes_with_age vlans logs/) {
|
|
swagger_path {
|
|
tags => ['Objects'],
|
|
description => "Returns $rel rows for a given port",
|
|
path => (setting('api_base') || '')."/object/device/{ip}/port/{port}/$rel",
|
|
parameters => [
|
|
ip => {
|
|
description => 'Canonical IP of the Device. Use Search methods to find this.',
|
|
required => 1,
|
|
in => 'path',
|
|
},
|
|
port => {
|
|
description => 'Name of the port. Use the ".../device/{ip}/ports" method to find these.',
|
|
required => 1,
|
|
in => 'path',
|
|
},
|
|
],
|
|
responses => { default => {} },
|
|
}, get qr{/api/v1/object/device/(?<ip>[^/]+)/port/(?<port>.+)/${rel}$} => require_role api => sub {
|
|
my $params = captures;
|
|
my $rows = try { schema(vars->{'tenant'})->resultset('DevicePort')
|
|
->find( $$params{port}, $$params{ip} )->$rel }
|
|
or send_error('Bad Device or Port', 404);
|
|
return to_json [ map {$_->TO_JSON} $rows->all ];
|
|
};
|
|
}
|
|
|
|
foreach my $rel (qw/power properties ssid wireless agg_master neighbor last_node/) {
|
|
swagger_path {
|
|
tags => ['Objects'],
|
|
description => "Returns the related $rel table entry for a given port",
|
|
path => (setting('api_base') || '')."/object/device/{ip}/port/{port}/$rel",
|
|
parameters => [
|
|
ip => {
|
|
description => 'Canonical IP of the Device. Use Search methods to find this.',
|
|
required => 1,
|
|
in => 'path',
|
|
},
|
|
port => {
|
|
description => 'Name of the port. Use the ".../device/{ip}/ports" method to find these.',
|
|
required => 1,
|
|
in => 'path',
|
|
},
|
|
],
|
|
responses => { default => {} },
|
|
}, get qr{/api/v1/object/device/(?<ip>[^/]+)/port/(?<port>.+)/${rel}$} => require_role api => sub {
|
|
my $params = captures;
|
|
my $row = try { schema(vars->{'tenant'})->resultset('DevicePort')
|
|
->find( $$params{port}, $$params{ip} )->$rel }
|
|
or send_error('Bad Device or Port', 404);
|
|
return to_json $row->TO_JSON;
|
|
};
|
|
}
|
|
|
|
# must come after the port methods above, so the route matches later
|
|
swagger_path {
|
|
tags => ['Objects'],
|
|
description => 'Returns a row from the device_port table',
|
|
path => (setting('api_base') || '').'/object/device/{ip}/port/{port}',
|
|
parameters => [
|
|
ip => {
|
|
description => 'Canonical IP of the Device. Use Search methods to find this.',
|
|
required => 1,
|
|
in => 'path',
|
|
},
|
|
port => {
|
|
description => 'Name of the port. Use the ".../device/{ip}/ports" method to find these.',
|
|
required => 1,
|
|
in => 'path',
|
|
},
|
|
],
|
|
responses => { default => {} },
|
|
}, get qr{/api/v1/object/device/(?<ip>[^/]+)/port/(?<port>.+)$} => require_role api => sub {
|
|
my $params = captures;
|
|
my $port = try { schema(vars->{'tenant'})->resultset('DevicePort')
|
|
->find( $$params{port}, $$params{ip} ) }
|
|
or send_error('Bad Device or Port', 404);
|
|
return to_json $port->TO_JSON;
|
|
};
|
|
|
|
swagger_path {
|
|
tags => ['Objects'],
|
|
path => (setting('api_base') || '').'/object/device/{ip}/nodes',
|
|
description => "Returns the nodes found on a given Device",
|
|
parameters => [
|
|
ip => {
|
|
description => 'Canonical IP of the Device. Use Search methods to find this.',
|
|
required => 1,
|
|
in => 'path',
|
|
},
|
|
active_only => {
|
|
description => 'Restrict results to active Nodes only',
|
|
type => 'boolean',
|
|
default => 'true',
|
|
in => 'query',
|
|
},
|
|
],
|
|
responses => { default => {} },
|
|
}, get '/api/v1/object/device/:ip/nodes' => require_role api => sub {
|
|
my $active = (params->{active_only} and ('true' eq params->{active_only})) ? 1 : 0;
|
|
my $rows = try { schema(vars->{'tenant'})->resultset('Node')
|
|
->search({ switch => params->{ip}, ($active ? (-bool => 'active') : ()) }) }
|
|
or send_error('Bad Device', 404);
|
|
return to_json [ map {$_->TO_JSON} $rows->all ];
|
|
};
|
|
|
|
swagger_path {
|
|
tags => ['Objects'],
|
|
path => (setting('api_base') || '').'/object/device/{ip}/nodes',
|
|
description => "Queue a job to store the nodes found on a given Device",
|
|
parameters => [
|
|
ip => {
|
|
description => 'Canonical IP of the Device. Use Search methods to find this.',
|
|
required => 1,
|
|
in => 'path',
|
|
},
|
|
nodes => {
|
|
description => 'List of node tuples (port, VLAN, MAC)',
|
|
default => '[]',
|
|
schema => {
|
|
type => 'array',
|
|
items => {
|
|
type => 'object',
|
|
properties => {
|
|
port => {
|
|
type => 'string'
|
|
},
|
|
vlan => {
|
|
type => 'integer',
|
|
default => '1'
|
|
},
|
|
mac => {
|
|
type => 'string'
|
|
}
|
|
}
|
|
}
|
|
},
|
|
in => 'body',
|
|
},
|
|
],
|
|
responses => { default => {} },
|
|
}, put '/api/v1/object/device/:ip/nodes' => require_role api_admin => sub {
|
|
|
|
jq_insert([{
|
|
action => 'macsuck',
|
|
device => params->{ip},
|
|
subaction => request->body,
|
|
username => session('logged_in_user'),
|
|
userip => request->remote_address,
|
|
}]);
|
|
|
|
return to_json {};
|
|
};
|
|
|
|
swagger_path {
|
|
tags => ['Objects'],
|
|
path => (setting('api_base') || '').'/object/vlan/{vlan}/nodes',
|
|
description => "Returns the nodes found in a given VLAN",
|
|
parameters => [
|
|
vlan => {
|
|
description => 'VLAN number',
|
|
type => 'integer',
|
|
required => 1,
|
|
in => 'path',
|
|
},
|
|
active_only => {
|
|
description => 'Restrict results to active Nodes only',
|
|
type => 'boolean',
|
|
default => 'true',
|
|
in => 'query',
|
|
},
|
|
],
|
|
responses => { default => {} },
|
|
}, get '/api/v1/object/vlan/:vlan/nodes' => require_role api => sub {
|
|
my $active = (params->{active_only} and ('true' eq params->{active_only})) ? 1 : 0;
|
|
my $rows = try { schema(vars->{'tenant'})->resultset('Node')
|
|
->search({ vlan => params->{vlan}, ($active ? (-bool => 'active') : ()) }) }
|
|
or send_error('Bad VLAN', 404);
|
|
return to_json [ map {$_->TO_JSON} $rows->all ];
|
|
};
|
|
|
|
swagger_path {
|
|
tags => ['Objects'],
|
|
path => (setting('api_base') || '').'/object/device/{ip}/arps',
|
|
description => "Queue a job to store the ARP entries found on a given Device",
|
|
parameters => [
|
|
ip => {
|
|
description => 'Canonical IP of the Device. Use Search methods to find this.',
|
|
required => 1,
|
|
in => 'path',
|
|
},
|
|
arps => {
|
|
description => 'List of arp tuples (MAC, IP, DNS?). IPs will be resolved to FQDN by Netdisco.',
|
|
default => '[]',
|
|
schema => {
|
|
type => 'array',
|
|
items => {
|
|
type => 'object',
|
|
properties => {
|
|
mac => {
|
|
type => 'string',
|
|
required => 1,
|
|
},
|
|
ip => {
|
|
type => 'string',
|
|
required => 1,
|
|
},
|
|
dns => {
|
|
type => 'string',
|
|
required => 0,
|
|
}
|
|
}
|
|
}
|
|
},
|
|
in => 'body',
|
|
},
|
|
],
|
|
responses => { default => {} },
|
|
}, put '/api/v1/object/device/:ip/arps' => require_role api_admin => sub {
|
|
|
|
jq_insert([{
|
|
action => 'arpnip',
|
|
device => params->{ip},
|
|
subaction => request->body,
|
|
username => session('logged_in_user'),
|
|
userip => request->remote_address,
|
|
}]);
|
|
|
|
return to_json {};
|
|
};
|
|
|
|
true;
|