1package App::Netdisco::Worker::Plugin::Discover::CanonicalIP;
2
3use Dancer ':syntax';
4use App::Netdisco::Worker::Plugin;
5use aliased 'App::Netdisco::Worker::Status';
6
7use App::Netdisco::Transport::SNMP ();
8use App::Netdisco::Util::Permission 'check_acl_only';
9use App::Netdisco::Util::DNS 'ipv4_from_hostname';
10use App::Netdisco::Util::Device 'is_discoverable';
11use Dancer::Plugin::DBIC 'schema';
12
13register_worker({ phase => 'main', driver => 'snmp' }, sub {
14  my ($job, $workerconf) = @_;
15
16  my $device = $job->device;
17  return unless $device->in_storage;
18  my $snmp = App::Netdisco::Transport::SNMP->reader_for($device)
19    or return Status->defer("discover failed: could not SNMP connect to $device");
20
21  my $old_ip = $device->ip;
22  my $new_ip = $old_ip;
23  my $revofname = ipv4_from_hostname($snmp->name);
24
25  if (setting('reverse_sysname') and $revofname) {
26    if (App::Netdisco::Transport::SNMP->test_connection( $new_ip )) {
27      $new_ip = $revofname;
28    }
29    else {
30      debug sprintf ' [%s] device - cannot renumber to %s - SNMP connect failed',
31        $old_ip, $revofname;
32    }
33  }
34
35  if (scalar @{ setting('device_identity') }) {
36    my @idmaps = @{ setting('device_identity') };
37    my $devips = $device->device_ips->order_by('alias');
38
39    ALIAS: while (my $alias = $devips->next) {
40      next if $alias->alias eq $old_ip;
41
42      foreach my $map (@idmaps) {
43        next unless ref {} eq ref $map;
44
45        foreach my $key (sort keys %$map) {
46          # lhs matches device, rhs matches device_ip
47          if (check_acl_only($device, $key)
48                and check_acl_only($alias, $map->{$key})) {
49
50            if (not is_discoverable( $alias->alias )) {
51              debug sprintf ' [%s] device - cannot renumber to %s - not discoverable',
52                $old_ip, $alias->alias;
53              next;
54            }
55
56            if (App::Netdisco::Transport::SNMP->test_connection( $alias->alias )) {
57              $new_ip = $alias->alias;
58              last ALIAS;
59            }
60            else {
61              debug sprintf ' [%s] device - cannot renumber to %s - SNMP connect failed',
62                $old_ip, $alias->alias;
63            }
64          }
65        }
66      }
67    } # ALIAS
68  }
69
70  return if $new_ip eq $old_ip;
71
72  schema('netdisco')->txn_do(sub {
73    my $existing = schema('netdisco')->resultset('Device')->search({
74      ip => $new_ip, vendor => $device->vendor, serial => $device->serial,
75    });
76
77    if (($job->subaction eq 'with-nodes') and $existing->count) {
78      $device->delete;
79      return $job->cancel(
80        " [$old_ip] device - cancelling fresh discover: already known as $new_ip");
81    }
82
83    # discover existing device but change IP, need to remove existing device
84    $existing->delete;
85
86    # if target device exists then this will die
87    $device->renumber($new_ip)
88      or die "cannot renumber to: $new_ip"; # rollback
89
90    # is not done in renumber, but required, otherwise confusing at job end!
91    schema('netdisco')->resultset('Admin')
92      ->find({job => $job->id})->update({device => $new_ip}) if $job->id;
93
94    return Status->info(sprintf ' [%s] device - changed IP to %s (%s)',
95      $old_ip, $device->ip, ($device->dns || ''));
96  });
97});
98
99true;
100