File Coverage

blib/lib/FusionInventory/Agent/Task/Deploy/P2P.pm
Criterion Covered Total %
statement 91 123 73.9
branch 23 48 47.9
condition 13 22 59.0
subroutine 13 14 92.8
pod 0 2 0.0
total 140 209 66.9


line stmt bran cond sub pod time code
1             package FusionInventory::Agent::Task::Deploy::P2P;
2              
3 10     10   30535768 use strict;
  10         14  
  10         256  
4 10     10   34 use warnings;
  10         15  
  10         294  
5              
6 10     10   34 use English qw(-no_match_vars);
  10         37  
  10         69  
7 10     10   8919 use Net::IP;
  10         296975  
  10         1266  
8 10     10   5678 use Net::Ping;
  10         66656  
  10         486  
9 10     10   4360 use Parallel::ForkManager;
  10         102611  
  10         115  
10              
11 10     10   227 use UNIVERSAL::require;
  10         11  
  10         59  
12              
13 10     10   624 use FusionInventory::Agent::Logger;
  10         11  
  10         9018  
14              
15             sub new {
16 9     9 0 54 my ($class, %params) = @_;
17              
18             my $self = {
19             logger => $params{logger} ||
20             FusionInventory::Agent::Logger->new(),
21             max_workers => $params{max_workers} || 10,
22             cache_timeout => $params{cache_timeout} || 600,
23             scan_timeout => $params{scan_timeout} || 5,
24             max_peers => $params{max_peers} || 512,
25 9   33     153 max_size => $params{max_size} || 5000,
      50        
      50        
      50        
      50        
      50        
26             cache_time => 0,
27             cache => []
28             };
29              
30 9         18 bless $self, $class;
31              
32 9         18 return $self;
33             }
34              
35             sub findPeers {
36 0     0 0 0 my ($self, $port) = @_;
37              
38             # $self->{logger}->debug("cachedate: ".$cache{date});
39 0         0 $self->{logger}->info("looking for a peer in the network");
40 0         0 return @{$self->{cache}}
41 0 0       0 if time - $self->{cache_time} < $self->{cache_timeout};
42              
43 0         0 my @interfaces;
44              
45 0 0       0 if ($OSNAME eq 'linux') {
    0          
46 0         0 FusionInventory::Agent::Tools::Linux->require();
47 0         0 @interfaces = FusionInventory::Agent::Tools::Linux::getInterfacesFromIfconfig();
48              
49             } elsif ($OSNAME eq 'MSWin32') {
50 0         0 FusionInventory::Agent::Tools::Win32->require();
51 0         0 @interfaces = FusionInventory::Agent::Tools::Win32::getInterfaces();
52             }
53              
54 0 0       0 if (!@interfaces) {
55 0         0 $self->{logger}->info("No network interfaces found");
56 0         0 return;
57             }
58              
59 0         0 my @addresses;
60              
61 0         0 foreach my $interface (@interfaces) {
62             #if interface has both ip and netmask setup then push the address
63 0 0       0 next unless $interface->{IPADDRESS};
64 0 0       0 next unless $interface->{IPMASK};
65 0 0       0 next unless lc($interface->{STATUS}) eq 'up';
66              
67             push @addresses, {
68             ip => $interface->{IPADDRESS},
69             mask => $interface->{IPMASK}
70 0         0 };
71             }
72              
73 0 0       0 if (!@addresses) {
74 0         0 $self->{logger}->info("No local address found");
75 0         0 return;
76             }
77              
78 0         0 my @potential_peers;
79              
80 0         0 foreach my $address (@addresses) {
81 0         0 push @potential_peers, $self->_getPotentialPeers($address);
82             }
83              
84 0 0       0 if (!@potential_peers) {
85 0         0 $self->{logger}->info("No neighbour address found");
86 0         0 return;
87             }
88              
89 0         0 $self->{cache_time} = time;
90 0         0 $self->{cache} = [ $self->_scanPeers($port, @potential_peers) ];
91              
92 0         0 return @{$self->{cache}};
  0         0  
93             }
94              
95             sub _getPotentialPeers {
96 72     72   115443 my ($self, $address, $limit) = @_;
97              
98 72 50       171 $limit = $self->{max_peers} unless defined $limit;
99              
100 72         243 my @ip_bytes = split(/\./, $address->{ip});
101 72         198 my @mask_bytes = split(/\./, $address->{mask});
102 72 100       216 return if $ip_bytes[0] == 127; # Ignore 127.x.x.x addresses
103 63 50       144 return if $ip_bytes[0] == 169; # Ignore 169.x.x.x range too
104              
105             # compute range
106 63         72 my @start;
107             my @end;
108              
109 63         144 foreach my $idx (0..3) {
110             ## no critic (ProhibitBitwise)
111 252         315 push @start, $ip_bytes[$idx] & (255 & $mask_bytes[$idx]);
112 252         261 push @end, $ip_bytes[$idx] | (255 - $mask_bytes[$idx]);
113             }
114              
115 63         198 my $ipStart = join('.', @start);
116 63         135 my $ipEnd = join('.', @end);
117 63 50       117 return if $ipStart eq $ipEnd;
118              
119             # Get ip interval before this interface ip
120             my $ipIntervalBefore = Net::IP->new($ipStart.' - '.$address->{ip})
121 63 50       252 or die Net::IP::Error();
122             # Get ip interval after this interface ip
123 63 50       112770 my $ipIntervalAfter = Net::IP->new($address->{ip}.' - '.$ipEnd)
124             or die Net::IP::Error();
125              
126 63         95742 my $beforeCount = int($limit/2);
127 63         63 my $afterCount = $beforeCount ;
128              
129 63         162 my $size = $ipIntervalBefore->size() + $ipIntervalAfter->size() - 1 ;
130 63 100       605070 if ($size > $self->{max_size}) {
131             $self->{logger}->debug(
132 9         567 "Range too large: $size (max $self->{max_size})"
133             );
134 9         117 return;
135             }
136              
137             # Handle the case ip range is bigger than expected
138 54 50       2880 if ($ipIntervalBefore->size() + $ipIntervalAfter->size() > $limit) {
139             $self->{logger}->debug(
140 54         19125 "Have to limit ip range between ".$ipStart." and ".$ipEnd
141             );
142             # Update counts in the case we are too close from a range limit
143 54 100       117 if ( $ipIntervalBefore->size() - 1 < $beforeCount ) {
    100          
144 9         2421 $beforeCount = $ipIntervalBefore->size() ;
145 9         1224 $afterCount = $limit - $beforeCount + 1;
146             } elsif ( $ipIntervalAfter->size() < $afterCount ) {
147 9         4023 $afterCount = $ipIntervalAfter->size();
148 9         1206 $beforeCount = $limit - $afterCount + 1 ;
149             }
150             # Forget too far before ips
151 54 100 66     19242 if (defined($ipIntervalBefore) && $ipIntervalBefore->size() > $beforeCount+1) {
152 45         8712 $ipIntervalBefore += $ipIntervalBefore->size() - $beforeCount - 1 ;
153             }
154             }
155 54         73440 $ipStart = $ipIntervalBefore->ip();
156 54         261 $beforeCount = $ipIntervalBefore->size() - 1;
157              
158             # Now add ips before
159 54         220833 my @peers;
160 54   66     279 while (defined($ipIntervalBefore) && $beforeCount-->0) {
161 171         432441 push @peers, $ipIntervalBefore->ip() ;
162 171         711 ++$ipIntervalBefore ;
163             }
164              
165             # Then add ips after
166 54   100     289368 while (defined(++$ipIntervalAfter) and $afterCount-->0) {
167 153         704007 $ipEnd = $ipIntervalAfter->ip() ;
168 153         648 push @peers, $ipEnd ;
169             }
170              
171 54         330993 $self->{logger}->debug("Scanning from ".$ipStart." to ".$ipEnd);
172              
173 54         522 return @peers;
174             }
175              
176             sub _scanPeers {
177 14     14   9029602 my ($self, $port, @addresses) = @_;
178              
179             $self->{logger}->debug(
180 14         329 "Scanning from $addresses[0] to $addresses[-1]"
181             );
182              
183 14         569 _fisher_yates_shuffle(\@addresses);
184              
185 14         370 my $ping = Net::Ping->new('tcp', $self->{scan_timeout});
186 14         4563 $ping->{port_num} = $port;
187 14         142 $ping->service_check(1);
188              
189 14         56 my @found;
190              
191 14         273 my $manager = Parallel::ForkManager->new($self->{max_workers});
192             $manager->run_on_finish(sub {
193 24     24   6009040 my ($pid, $exit_code, $address) = @_;
194 24 100       143 push @found, $address if $exit_code;
195 14         7816 });
196              
197 14         184 foreach my $address (@addresses) {
198 44 100       25534 $manager->start($address) and next;
199 8 100       26242 $manager->finish($ping->ping($address) ? 1 : 0);
200             }
201              
202 6         4155 $manager->wait_all_children();
203              
204 6         140 return @found;
205             }
206              
207             sub _fisher_yates_shuffle {
208 14     14   59 my $deck = shift; # $deck is a reference to an array
209              
210 14 50       132 return unless @$deck; # must not be empty!
211              
212 14         73 my $i = @$deck;
213 14         195 while (--$i) {
214 42         115 my $j = int rand ($i+1);
215 42         139 @$deck[$i,$j] = @$deck[$j,$i];
216             }
217             }
218              
219             1;