diff --git a/Changes b/Changes index ad91bd47..d7164a86 100644 --- a/Changes +++ b/Changes @@ -5,6 +5,7 @@ * Refactor to make less webby and more suitable to be a complete Netdisco app * Network Map now shows all device neighbors and allows click-through nav * Add new netdisco-daemon to handle port_control actions + * Add PoE control to port_control (+ up/down,vlan,name,location,contact) [ENHANCEMENTS] diff --git a/Netdisco/bin/netdisco-daemon b/Netdisco/bin/netdisco-daemon index dc3e4676..8ada75f2 100755 --- a/Netdisco/bin/netdisco-daemon +++ b/Netdisco/bin/netdisco-daemon @@ -26,7 +26,7 @@ sub gd_run_body { # get all pending jobs my $rs = schema('netdisco')->resultset('Admin')->search({ - action => [qw/location contact portcontrol portname vlan/], + action => [qw/location contact portcontrol portname vlan power/], status => 'queued', }); diff --git a/Netdisco/lib/Netdisco/Daemon/Actions/Port.pm b/Netdisco/lib/Netdisco/Daemon/Actions/Port.pm index 657d766a..200190e5 100644 --- a/Netdisco/lib/Netdisco/Daemon/Actions/Port.pm +++ b/Netdisco/lib/Netdisco/Daemon/Actions/Port.pm @@ -84,4 +84,53 @@ sub _set_port_generic { 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; diff --git a/Netdisco/lib/Netdisco/Util/Connect.pm b/Netdisco/lib/Netdisco/Util/Connect.pm index ee3dc8db..32eac2a3 100644 --- a/Netdisco/lib/Netdisco/Util/Connect.pm +++ b/Netdisco/lib/Netdisco/Util/Connect.pm @@ -9,11 +9,11 @@ use Try::Tiny; use base 'Exporter'; our @EXPORT = (); our @EXPORT_OK = qw/ - get_device get_port get_iid snmp_connect + get_device get_port get_iid get_powerid snmp_connect /; our %EXPORT_TAGS = ( all => [qw/ - get_device get_port get_iid snmp_connect + get_device get_port get_iid get_powerid snmp_connect /], ); @@ -78,6 +78,26 @@ sub get_iid { 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 instance configured for and diff --git a/Netdisco/lib/Netdisco/Web/PortControl.pm b/Netdisco/lib/Netdisco/Web/PortControl.pm index f00c1b59..312009f2 100644 --- a/Netdisco/lib/Netdisco/Web/PortControl.pm +++ b/Netdisco/lib/Netdisco/Web/PortControl.pm @@ -50,7 +50,7 @@ ajax '/ajax/userlog' => sub { my $rs = schema('netdisco')->resultset('Admin')->search({ username => $user, - action => [qw/location contact portcontrol portname vlan/], + action => [qw/location contact portcontrol portname vlan power/], finished => { '>' => \"(now() - interval '5 seconds')" }, });