File Coverage

lib/Slaughter/Info/linux.pm
Criterion Covered Total %
statement 97 118 82.2
branch 38 64 59.3
condition 6 15 40.0
subroutine 4 4 100.0
pod 2 2 100.0
total 147 203 72.4


line stmt bran cond sub pod time code
1             #!/usr/bin/perl -w
2              
3             =head1 NAME
4            
5             Slaughter::Info::linux - Determine information about a Linux host.
6            
7             =cut
8              
9             =head1 SYNOPSIS
10            
11             This module is the GNU/Linux version of the Slaughter information-gathering
12             module.
13            
14             Modules beneath the C<Slaughter::Info> namespace are loaded when slaughter
15             is executed, they are used to populate a hash with information about
16             the current host.
17            
18             This module is loaded only on linux systems, and will determine such details
19             as the local hostname, the free RAM, any IP addresses, etc.
20            
21             The correct information-gathering module is loaded at run-time via the use of the C<$^O> variable, and if no system-specific module is available then the generic L<Slaughter::Info::generic> module is used as a fall-back.
22            
23             The information discovered can be dumped by running C<slaughter>
24            
25             =for example begin
26            
27             ~# slaughter --dump
28            
29             =for example end
30            
31             Usage of this module is as follows:
32            
33             =for example begin
34            
35             use Slaughter::Info::linux;
36            
37             my $obj = Slaughter::Info::linux->new();
38             my $data = $obj->getInformation();
39            
40             # use info now ..
41             print "We have software RAID\n" if ( $data->{'softwareraid'} );
42            
43             =for example end
44            
45             When this module is used an attempt is also made to load the module
46             C<Slaughter::Info::Local::linux> - if that succeeds it will be used to
47             augment the information discovered and made available to slaughter
48             policies.
49            
50             =cut
51              
52             =head1 METHODS
53            
54             Now follows documentation on the available methods.
55            
56             =cut
57              
58              
59 1     1   705 use strict;
  1         1  
  1         35  
60 1     1   5 use warnings;
  1         2  
  1         1454  
61              
62              
63             package Slaughter::Info::linux;
64              
65              
66             #
67             # The version of our release.
68             #
69             our $VERSION = "3.0.5";
70              
71              
72             =head2 new
73            
74             Create a new instance of this object.
75            
76             =cut
77              
78             sub new
79             {
80 1     1 1 605     my ( $proto, %supplied ) = (@_);
81 1   33     5     my $class = ref($proto) || $proto;
82              
83 1         2     my $self = {};
84 1         2     bless( $self, $class );
85 1         3     return $self;
86              
87             }
88              
89              
90             =head2 getInformation
91            
92             This function retrieves meta-information about the current host.
93            
94             The return value is a hash-reference of data determined dynamically.
95            
96             =cut
97              
98             sub getInformation
99             {
100 1     1 1 841     my ($self) = (@_);
101              
102             #
103             # The data we will return.
104             #
105 1         1     my $ref;
106              
107             #
108             # Fully Qualified hostname
109             #
110             # 1. If we can find /etc/hostname, then use that.
111             #
112 1 50       29     if ( -e "/etc/hostname" )
113                 {
114 1 50       24         open( my $file, "<", "/etc/hostname" ) or
115                       die "Failed to read /etc/hostname - $!";
116 1         8         $ref->{ 'fqdn' } = <$file>;
117 1         3         chomp( $ref->{ 'fqdn' } );
118 1         7         close($file);
119                 }
120                 else
121                 {
122              
123             #
124             # Call "hostname".
125             #
126 0         0         $ref->{ 'fqdn' } = `hostname`;
127 0         0         chomp( $ref->{ 'fqdn' } );
128              
129             #
130             # If it is unqualified retry with --fqdn.
131             #
132 0 0       0         if ( $ref->{ 'fqdn' } !~ /\./ )
133                     {
134 0         0             $ref->{ 'fqdn' } = `hostname --fqdn`;
135 0         0             chomp( $ref->{ 'fqdn' } );
136                     }
137                 }
138              
139              
140             #
141             # Get the hostname and domain name as seperate strings.
142             #
143 1 50       3     if ( $ref->{ 'fqdn' } =~ /^([^.]+)\.(.*)$/ )
144                 {
145 0         0         $ref->{ 'hostname' } = $1;
146 0         0         $ref->{ 'domain' } = $2;
147                 }
148                 else
149                 {
150              
151             #
152             # Better than nothing, right?
153             #
154 1         2         $ref->{ 'hostname' } = $ref->{ 'fqdn' };
155 1         2         $ref->{ 'domain' } = $ref->{ 'fqdn' };
156                 }
157              
158             #
159             # This should be portable.
160             #
161 1         2     $ref->{ 'path' } = $ENV{ 'PATH' };
162              
163             #
164             # Is this a xen host, or guest?
165             #
166 1 50       28     $ref->{ 'xen' } = 1 if -d "/proc/xen/capabilities";
167              
168             #
169             # Detect virtualized CPU, as well as processor count, and architecture.
170             #
171 1 50       22     if ( open( my $cpu, "<", "/proc/cpuinfo" ) )
172                 {
173 1         5         $ref->{ 'cpu_count' } = -1;
174              
175 1         244         foreach my $line (<$cpu>)
176                     {
177 416         239             chomp($line);
178 416 100 100     580             $ref->{ 'kvm' } = 1 if ( $line =~ /model/ && $line =~ /qemu/i );
179              
180 416 100       421             if ( $line =~ /model name\s+: (.*)$/ )
181                         {
182 16         18                 $ref->{ 'cpumodel' } = $1;
183                         }
184 416 100       428             if ( $line =~ /processor\s+: (\d+)/ )
185                         {
186 16 50       38                 $ref->{ 'cpu_count' } = $1 if ( $ref->{ 'cpu_count' } < $1 );
187                         }
188 416 100       494             if ( $line =~ /flags\s+:(.*)/ )
189                         {
190 16         21                 my $flags = $1;
191 16 50       30                 if ( $flags =~ /lm/ )
192                             {
193 16         14                     $ref->{ 'arch' } = "amd64";
194 16         16                     $ref->{ 'bits' } = 64;
195                             }
196                             else
197                             {
198 0         0                     $ref->{ 'arch' } = "i386";
199 0         0                     $ref->{ 'bits' } = 32;
200                             }
201                         }
202                     }
203              
204 1         16         $ref->{ 'cpu_count' }++;
205 1         16         close($cpu);
206                 }
207              
208              
209             #
210             # Are we i386/amd64. This shouldn't be necessary since the information
211             # should have been read from /proc/cpuinfo
212             #
213 1 50       3     if ( !$ref->{ 'arch' } )
214                 {
215 0         0         my $type = `file /bin/ls`;
216 0 0       0         if ( $type =~ /64-bit/i )
217                     {
218 0         0             $ref->{ 'arch' } = "amd64";
219 0         0             $ref->{ 'bits' } = 64;
220                     }
221                     else
222                     {
223 0         0             $ref->{ 'arch' } = "i386";
224 0         0             $ref->{ 'bits' } = 32;
225                     }
226                 }
227              
228              
229             #
230             # Software RAID?
231             #
232 1 50 33     27     if ( ( -e "/proc/mdstat" ) &&
233                      ( -x "/sbin/mdadm" ) )
234                 {
235 0 0       0         if ( open( my $mdstat, "<", "/proc/mdstat" ) )
236                     {
237 0         0             foreach my $line (<$mdstat>)
238                         {
239 0 0 0     0                 if ( ( $line =~ /^md([0-9]+)/ ) &&
240                                  ( $line =~ /active/i ) )
241                             {
242 0         0                     $ref->{ 'softwareraid' } = 1;
243 0         0                     $ref->{ 'raid' } = "software";
244                             }
245                         }
246 0         0             close($mdstat);
247                     }
248                 }
249              
250              
251             #
252             # Memory total and memory free.
253             #
254 1 50       19     if ( open( my $mem, "<", "/proc/meminfo" ) )
255                 {
256 1         386         foreach my $line (<$mem>)
257                     {
258 42         26             chomp($line);
259 42 100       45             if ( $line =~ /MemTotal:\s+(\d+) kB/ )
260                         {
261 1         4                 $ref->{ 'memtotal' } = $1;
262                         }
263 42 100       49             if ( $line =~ /MemFree:\s+(\d+) kB/ )
264                         {
265 1         3                 $ref->{ 'memfree' } = $1;
266                         }
267                     }
268 1         8         close($mem);
269                 }
270              
271              
272             #
273             # Kernel version.
274             #
275 1         1946     $ref->{ 'kernel' } = `uname -r`;
276 1         9     chomp( $ref->{ 'kernel' } );
277              
278              
279             #
280             # IP address(es).
281             #
282 1         4     my $ip = undef;
283              
284 1 50       31     $ip = "/sbin/ip" if ( -x "/sbin/ip" );
285 1 50       11     $ip = "/bin/ip" if ( -x "/bin/ip" );
286              
287              
288 1 50       10     if ( defined($ip) )
289                 {
290              
291             #
292             # Two commands to find the IP addresses we have
293             #
294 1         6         my @cmd = ( " -o -f inet addr show scope global",
295                                 " -o -f inet6 addr show scope global"
296                               );
297              
298             #
299             # Run each
300             #
301 1         8         foreach my $cmd (@cmd)
302                     {
303 2         5             my $count = 1;
304 2         3             my $family = "ip";
305 2 100       16             $family = "ip6" if ( $cmd =~ /inet6/i );
306              
307 2         3608             foreach my $line ( split( /[\r\n]/, `$ip $cmd` ) )
308                         {
309 1 50 33     16                 next if ( !defined($line) || !length($line) );
310 1         10                 chomp($line);
311              
312             #
313             # This matches something like:
314             #
315             # eth0 inet 192.168.1.9/24 brd 192.168.1.255 scope global eth0
316             #
317             # or
318             # eth0 inet6 2001:41c8:1:5abb::62/64 scope global valid_lft forever preferred_lft forever
319             #
320             #
321 1 50       75                 if ( $line =~ /(inet|inet6)[ \t]+([^ \t+]+)[ \t]+/ )
322                             {
323 1         8                     my $proto = $1;
324 1         5                     my $ip = $2;
325              
326             #
327             # Strip off /24, /128, etc.
328             #
329 1         6                     $ip =~ s/\/.*//g;
330              
331             #
332             # Save away the IP address in "ip0", "ip1", "ip2" .. etc.
333             #
334 1         7                     $ref->{ $family . "_" . $count } = $ip;
335 1         6                     $count += 1;
336                             }
337                         }
338              
339 2 50       9             if ( $count > 0 )
340                         {
341 2         15                 $ref->{ $family . '_count' } = ( $count - 1 );
342                         }
343                     }
344                 }
345              
346             #
347             # Find the name of our release
348             #
349 1         6     my $version = "unknown";
350 1         2     my $distrib = "unknown";
351 1         3     my $release = "unknown";
352 1 50       26     if ( -x "/usr/bin/lsb_release" )
353                 {
354 1         60996         foreach
355                       my $line ( split( /[\r\n]/, `/usr/bin/lsb_release -a 2>/dev/null` ) )
356                     {
357 4         15             chomp $line;
358 4 100       32             if ( $line =~ /Distributor ID:\s*(.*)/ )
359                         {
360 1         15                 $distrib = $1;
361                         }
362 4 100       14             if ( $line =~ /Release:\s*(.*)/ )
363                         {
364 1         3                 $version = $1;
365                         }
366 4 100       12             if ( $line =~ /Codename:\s*(.*)/ )
367                         {
368 1         7                 $release = $1;
369                         }
370                     }
371                 }
372 1         12     $ref->{ 'version' } = $version;
373 1         7     $ref->{ 'distribution' } = $distrib;
374 1         5     $ref->{ 'release' } = $release;
375              
376              
377             #
378             # TODO: 3Ware RAID?
379             #
380              
381             #
382             # TODO: HP RAID?
383             #
384              
385             #
386             # Load Average
387             #
388 1         2321     my $uptime = `uptime`;
389 1         8     chomp($uptime);
390 1 50       18     if ( $uptime =~ /load average:[ \t]*(.*)/ )
391                 {
392 1         6         $uptime = $1;
393 1         7         $uptime =~ s/,//g;
394 1         4         $ref->{ 'load_average' } = $uptime;
395              
396             #
397             # Split into per-minute values.
398             #
399 1         12         my @avg = split( /[ \t]/, $ref->{ 'load_average' } );
400 1         5         $ref->{ 'load_average_1' } = $avg[0];
401 1         3         $ref->{ 'load_average_5' } = $avg[1];
402 1         5         $ref->{ 'load_average_15' } = $avg[2];
403              
404                 }
405              
406              
407 1         25     return ($ref);
408             }
409              
410              
411              
412             1;
413              
414              
415             =head1 AUTHOR
416            
417             Steve Kemp <steve@steve.org.uk>
418            
419             =cut
420              
421             =head1 LICENSE
422            
423             Copyright (c) 2010-2015 by Steve Kemp. All rights reserved.
424            
425             This module is free software;
426             you can redistribute it and/or modify it under
427             the same terms as Perl itself.
428             The LICENSE file contains the full text of the license.
429            
430             =cut
431