1package App::Netdisco::Worker::Plugin::Discover::Entities;
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';
9use Encode;
10
11my $clean = sub {
12  my $device = shift;
13
14  my $gone = $device->modules->delete;
15  debug sprintf ' [%s] modules - removed %d chassis modules',
16    $device->ip, $gone;
17
18  $device->modules->update_or_create({
19    ip => $device->ip,
20    index => 1,
21    parent => 0,
22    name => 'chassis',
23    class => 'chassis',
24    pos => -1,
25    # too verbose and link doesn't work anyway
26    # description => $device->description,
27    sw_ver => $device->os_ver,
28    serial => $device->serial,
29    model => $device->model,
30    fru => \'false',
31    last_discover => \'now()',
32  });
33};
34
35register_worker({ phase => 'main', driver => 'snmp' }, sub {
36  my ($job, $workerconf) = @_;
37
38  my $device = $job->device;
39  return unless $device->in_storage;
40
41  if (not setting('store_modules')) {
42      schema('netdisco')->txn_do($clean, $device);
43      return Status->info(
44        sprintf ' [%s] modules - store_modules is disabled (added one pseudo for chassis)',
45        $device->ip);
46  }
47
48  my $snmp = App::Netdisco::Transport::SNMP->reader_for($device)
49    or return Status->defer("discover failed: could not SNMP connect to $device");
50  my $e_index = $snmp->e_index;
51
52  if (!defined $e_index) {
53      schema('netdisco')->txn_do($clean, $device);
54      return Status->info(
55        sprintf ' [%s] modules - 0 chassis components (added one pseudo for chassis)',
56        $device->ip);
57  }
58
59  my $e_descr   = $snmp->e_descr;
60  my $e_type    = $snmp->e_type;
61  my $e_parent  = $snmp->e_parent;
62  my $e_name    = $snmp->e_name;
63  my $e_class   = $snmp->e_class;
64  my $e_pos     = $snmp->e_pos;
65  my $e_hwver   = $snmp->e_hwver;
66  my $e_fwver   = $snmp->e_fwver;
67  my $e_swver   = $snmp->e_swver;
68  my $e_model   = $snmp->e_model;
69  my $e_serial  = $snmp->e_serial;
70  my $e_fru     = $snmp->e_fru;
71
72  # build device modules list for DBIC
73  my (@modules, %seen_idx);
74  foreach my $entry (keys %$e_index) {
75      next unless defined $e_index->{$entry};
76      next if $seen_idx{ $e_index->{$entry} }++;
77
78      if ($e_index->{$entry} !~ m/^[0-9]+$/) {
79          debug sprintf ' [%s] modules - index %s is not an integer',
80            $device->ip, $e_index->{$entry};
81          next;
82      }
83
84      push @modules, {
85          index  => $e_index->{$entry},
86          type   => $e_type->{$entry},
87          parent => $e_parent->{$entry},
88          name   => Encode::decode('UTF-8', $e_name->{$entry}),
89          class  => $e_class->{$entry},
90          pos    => $e_pos->{$entry},
91          hw_ver => Encode::decode('UTF-8', $e_hwver->{$entry}),
92          fw_ver => Encode::decode('UTF-8', $e_fwver->{$entry}),
93          sw_ver => Encode::decode('UTF-8', $e_swver->{$entry}),
94          model  => Encode::decode('UTF-8', $e_model->{$entry}),
95          serial => Encode::decode('UTF-8', $e_serial->{$entry}),
96          fru    => $e_fru->{$entry},
97          description => Encode::decode('UTF-8', $e_descr->{$entry}),
98          last_discover => \'now()',
99      };
100  }
101
102  foreach my $m (@modules){
103    unless ($seen_idx{$m->{parent}} || !$m->{parent}){
104      # Some combined devices like Nexus with FEX or ASR with Satellites can return invalid
105      # EntityMIB trees. This workaround relocates entitites with invalid parents to the root
106      # of the tree, so they are at least visible in the Modules tab (see #710)
107
108      info sprintf ' [%s] Entity %s (%s) has invalid parent %s - attaching as root entity instead',
109          $device->ip, $m->{index}, $m->{name}, $m->{parent};
110      $m->{parent} = undef;
111    }
112  }
113
114  schema('netdisco')->txn_do(sub {
115    my $gone = $device->modules->delete;
116    debug sprintf ' [%s] modules - removed %d chassis modules',
117      $device->ip, $gone;
118    $device->modules->populate(\@modules);
119
120    return Status->info(sprintf ' [%s] modules - added %d new chassis modules',
121      $device->ip, scalar @modules);
122  });
123});
124
125true;
126