1package App::Netdisco::Worker::Plugin::Discover::PortPower;
2
3use Dancer ':syntax';
4use App::Netdisco::Worker::Plugin;
5use aliased 'App::Netdisco::Worker::Status';
6
7use App::Netdisco::Transport::SNMP ();
8use Dancer::Plugin::DBIC 'schema';
9
10register_worker({ phase => 'main', driver => 'snmp' }, sub {
11  my ($job, $workerconf) = @_;
12
13  my $device = $job->device;
14  return unless $device->in_storage;
15  my $snmp = App::Netdisco::Transport::SNMP->reader_for($device)
16    or return Status->defer("discover failed: could not SNMP connect to $device");
17
18  my $p_watts  = $snmp->peth_power_watts;
19  my $p_status = $snmp->peth_power_status;
20
21  if (!defined $p_watts) {
22      return Status->info(sprintf ' [%s] power - 0 power modules', $device->ip);
23  }
24
25  # build device module power info suitable for DBIC
26  my @devicepower;
27  foreach my $entry (keys %$p_watts) {
28      push @devicepower, {
29          module => $entry,
30          power  => $p_watts->{$entry},
31          status => $p_status->{$entry},
32      };
33  }
34
35  # cache the device ports to save hitting the database for many single rows
36  my $device_ports = vars->{'device_ports'}
37    || { map {($_->port => $_)} $device->ports->all };
38
39  my $interfaces = $snmp->interfaces;
40  my $p_ifindex  = $snmp->peth_port_ifindex;
41  my $p_admin    = $snmp->peth_port_admin;
42  my $p_pstatus  = $snmp->peth_port_status;
43  my $p_class    = $snmp->peth_port_class;
44  my $p_power    = $snmp->peth_port_power;
45
46  # build device port power info suitable for DBIC
47  my @portpower;
48  foreach my $entry (keys %$p_ifindex) {
49      # WRT #475 this is SAFE because we check against known ports below
50      my $port = $interfaces->{ $p_ifindex->{$entry} } or next;
51
52      if (!defined $device_ports->{$port}) {
53          debug sprintf ' [%s] power - local port %s already skipped, ignoring',
54            $device->ip, $port;
55          next;
56      }
57
58      my ($module) = split m/\./, $entry;
59      push @portpower, {
60          port   => $port,
61          module => $module,
62          admin  => $p_admin->{$entry},
63          status => $p_pstatus->{$entry},
64          class  => $p_class->{$entry},
65          power  => $p_power->{$entry},
66
67      };
68  }
69
70  schema('netdisco')->txn_do(sub {
71    my $gone = $device->power_modules->delete;
72    debug sprintf ' [%s] power - removed %d power modules',
73      $device->ip, $gone;
74    $device->power_modules->populate(\@devicepower);
75    debug sprintf ' [%s] power - added %d new power modules',
76      $device->ip, scalar @devicepower;
77  });
78
79  schema('netdisco')->txn_do(sub {
80    my $gone = $device->powered_ports->delete;
81    debug sprintf ' [%s] power - removed %d PoE capable ports',
82      $device->ip, $gone;
83    $device->powered_ports->populate(\@portpower);
84
85    return Status->info(sprintf ' [%s] power - added %d new PoE capable ports',
86      $device->ip, scalar @portpower);
87  });
88});
89
90true;
91