File Coverage

blib/lib/Device/RAID/Poller/Backends/ZFS.pm
Criterion Covered Total %
statement 8 79 10.1
branch 0 34 0.0
condition 0 30 0.0
subroutine 3 6 50.0
pod 3 3 100.0
total 14 152 9.2


line stmt bran cond sub pod time code
1             package Device::RAID::Poller::Backends::ZFS;
2              
3 1     1   75395 use 5.006;
  1         14  
4 1     1   5 use strict;
  1         2  
  1         36  
5 1     1   6 use warnings;
  1         2  
  1         1067  
6              
7             =head1 NAME
8              
9             Device::RAID::Poller::Backends::ZFS - ZFS zpool backend.
10              
11             =head1 VERSION
12              
13             Version 0.0.1
14              
15             =cut
16              
17             our $VERSION = '0.0.1';
18              
19              
20             =head1 SYNOPSIS
21              
22             use Device::RAID::Poller::Backends::ZFS;
23            
24             my $backend = Device::RAID::Poller::Backends::ZFS;
25            
26             my $usable=$backend->usable;
27             my %return_hash;
28             if ( $usable ){
29             %return_hash=$backend->run;
30             }
31              
32             =head1 METHODS
33              
34             =head2 new
35              
36             Initiates the backend object.
37              
38             my $backend = Device::RAID::Poller::Backends::FBSD_gmirror;
39              
40             =cut
41              
42             sub new {
43 0     0 1   my $self = {
44             usable=>0,
45             };
46 0           bless $self;
47              
48 0           return $self;
49             }
50              
51             =head2 run
52              
53             Runs the poller backend and report the results.
54              
55             If nothing is nothing is loaded, load will be called.
56              
57             my $usable=$backend->usable;
58            
59              
60             =cut
61              
62             sub run {
63 0     0 1   my $self=$_[0];
64              
65 0           my %return_hash=(
66             'status'=>0,
67             'devices'=>{},
68             );
69              
70             # if not usable, no point in continuing
71 0 0         if ( ! $self->{usable} ){
72 0           return %return_hash;
73             }
74              
75             # zpool status notes for config section
76             # good...
77             # INUSE = spare in use
78             # ONLINE = working as intented
79             # bad...
80             # DEGRADED = device failed or is replacing one
81             # OFFLINE = administrative or being replaced
82             # FAULTED = failed disk
83             # UNAVAIL = missing disk
84             # spares...
85             # AVAIL = available spare
86              
87             # Fetch the raw gmirror status.
88 0           my $raw=`/sbin/zpool list`;
89 0 0         if ( $? != 0 ){
90 0           return %return_hash;
91             }
92              
93             # split it and shift the header line off
94 0           my @raw_split=split(/\n/, $raw);
95 0           shift @raw_split;
96              
97 0           my @devs;
98              
99             # find out what devs we have and build the return hash
100 0           foreach my $line (@raw_split){
101 0           my @line_split=split( /[\t ]+/, $line);
102 0           my $dev=$line_split[0];
103 0           push(@devs, $dev);
104 0           $dev='ZFS '.$dev;
105              
106 0           $return_hash{devices}{$dev}={
107             'backend'=>'ZFS',
108             'name'=>$dev,
109             'good'=>[],
110             'bad'=>[],
111             'spare'=>[],
112             'type'=>'ZFS',
113             'BBUstatus'=>'na',
114             'status'=>'unknown',
115             };
116              
117 0 0         if ( $line_split[9] eq "ONLINE" ){
118 0           $return_hash{devices}{$dev}{status}='good';
119             }else{
120 0           $return_hash{devices}{$dev}{status}='bad';
121             }
122             }
123              
124             # process each pool and for disk status info
125 0           foreach my $pool (@devs){
126 0           my $dev='ZFS '.$pool;
127              
128 0           $raw=`/sbin/zpool status $pool`;
129 0           my @raw_split=split(/\n/, $raw);
130              
131             # only begin processing ocne we find the line after name in the config section
132 0           my $config_found=0;
133 0           my $name_found=0;
134 0           foreach my $line (@raw_split){
135 0           $line=~s/^[\t ]+//;
136              
137             # Check if we are at the config section or not at ever section change.
138 0 0         if ( $line =~ /^config\:/ ){
    0          
139 0           $config_found=1;
140             }elsif( $line =~ /^[A-Za-z0-1]+:/ ){
141 0           $config_found=0;
142             }
143              
144 0 0         if ( $config_found){
145             # If we are in the config, begin processing after the NAME line.
146 0           my $process=0;
147 0 0         if ( $line =~ /^NAME/ ){
    0          
148 0           $name_found=1;
149             }elsif( $name_found ){
150 0           $process=1;
151             }
152              
153             # We are inally in the are with the info we want.
154 0 0         if ( $process ){
155 0           my @line_split=split(/[\t ]+/, $line);
156             # Ignore vdev lines
157             # The mirror line must include - or it will accidentally match geom_mirror devices.
158 0 0 0       if (
      0        
      0        
      0        
      0        
      0        
159             ( defined( $line_split[0] ) ) &&
160             ( defined( $line_split[1] ) ) &&
161             ( $line_split[0] ne $pool ) &&
162             ( $line_split[0] !~ /^mirror\-/ ) &&
163             ( $line_split[0] !~ /^spare/ ) &&
164             ( $line_split[0] !~ /^log/ ) &&
165             ( $line_split[0] !~ /^cache/ )
166             ){
167             # We are at a drive line, figure out the drive status.
168 0 0 0       if (
    0          
169             ( $line_split[1] eq 'ONLINE' ) ||
170             ( $line_split[1] eq 'INUSE' )
171             ){
172 0           push(@{ $return_hash{devices}{$dev}{good} }, $line_split[0]);
  0            
173             }elsif( $line_split[1] eq 'AVAIL' ){
174 0           push(@{ $return_hash{devices}{$dev}{spare} }, $line_split[0]);
  0            
175             }else{
176 0           push(@{ $return_hash{devices}{$dev}{bar} }, $line_split[0]);
  0            
177             }
178             }
179             }
180             }
181             }
182             }
183              
184 0           $return_hash{status}=1;
185              
186 0           return %return_hash;
187             }
188              
189             =head2 usable
190              
191             Returns a perl boolean for if it is usable or not.
192              
193             my $usable=$backend->usable;
194             if ( ! $usable ){
195             print "This backend is not usable.\n";
196             }
197              
198             =cut
199              
200             sub usable {
201 0     0 1   my $self=$_[0];
202              
203             # Make sure we are on a OS on which ZFS is usable on.
204 0 0 0       if (
      0        
      0        
205             ( $^O !~ 'freebsd' ) &&
206             ( $^O !~ 'solaris' ) &&
207             ( $^O !~ 'netbsd' ) &&
208             ( $^O !~ 'linux' )
209             ){
210 0           $self->{usable}=0;
211 0           return 0;
212             }
213              
214             # If this is FreeBSD, make sure ZFS is laoded.
215             # If we don't do this, the pool test will result in
216             # it being loaded, which we don't want to do.
217 0 0         if ( $^O =~ /freebsd/ ){
218             # Test for this via this method as 'kldstat -q -n zfs' will error if it is compiled in
219 0           system('/sbin/sysctl -q kstat.zfs.misc.arcstats.hits > /dev/null');
220 0 0         if ( $? != 0 ){
221 0           $self->{usable}=0;
222 0           return 0;
223             }
224             }
225              
226             # make sure we can locate zpool
227             # Written like this as which on some Linux distros such as CentOS 7 is broken.
228 0           my $zpool_bin=`/bin/sh -c 'which zpool 2> /dev/null'`;
229 0 0         if ( $? != 0 ){
230 0           $self->{usable}=0;
231 0           return 0;
232             }
233 0           chomp($zpool_bin);
234 0           $self->{zpool_bin}=$zpool_bin;
235              
236             # No zpools on this device.
237 0           my $pool_test=`$zpool_bin list`;
238 0 0         if ( $pool_test !~ /^NAME/ ){
239 0           $self->{usable}=0;
240 0           return 0;
241             }
242              
243 0           $self->{usable}=1;
244 0           return 1;
245             }
246              
247             =head1 AUTHOR
248              
249             Zane C. Bowers-Hadley, C<< >>
250              
251             =head1 BUGS
252              
253             Please report any bugs or feature requests to C, or through
254             the web interface at L. I will be
255             notified, and then you'll automatically be notified of progress on your bug as I make changes.
256              
257              
258              
259              
260             =head1 SUPPORT
261              
262             You can find documentation for this module with the perldoc command.
263              
264             perldoc Device::RAID::Poller
265              
266              
267             You can also look for information at:
268              
269             =over 4
270              
271             =item * RT: CPAN's request tracker (report bugs here)
272              
273             L
274              
275             =item * AnnoCPAN: Annotated CPAN documentation
276              
277             L
278              
279             =item * CPAN Ratings
280              
281             L
282              
283             =item * Search CPAN
284              
285             L
286              
287             =back
288              
289              
290             =head1 ACKNOWLEDGEMENTS
291              
292              
293             =head1 LICENSE AND COPYRIGHT
294              
295             This software is Copyright (c) 2019 by Zane C. Bowers-Hadley.
296              
297             This is free software, licensed under:
298              
299             The Artistic License 2.0 (GPL Compatible)
300              
301              
302             =cut
303              
304             1; # End of Device::RAID::Poller