| blib/lib/Lustre/Info.pm | |||
|---|---|---|---|
| Criterion | Covered | Total | % |
| statement | 37 | 129 | 28.6 |
| branch | 4 | 54 | 7.4 |
| condition | 0 | 3 | 0.0 |
| subroutine | 14 | 24 | 58.3 |
| pod | 11 | 12 | 91.6 |
| total | 66 | 222 | 29.7 |
| line | stmt | bran | cond | sub | pod | time | code |
|---|---|---|---|---|---|---|---|
| 1 | # | ||||||
| 2 | # Lutre::Info Mainclass | ||||||
| 3 | # | ||||||
| 4 | # (C) 2010 Adrian Ulrich - |
||||||
| 5 | # | ||||||
| 6 | # This program is free software; you can redistribute it and/or | ||||||
| 7 | # modify it under the same terms as Perl itself. | ||||||
| 8 | # | ||||||
| 9 | |||||||
| 10 | package Lustre::Info; | ||||||
| 11 | |||||||
| 12 | 1 | 1 | 788 | use strict; | |||
| 1 | 1 | ||||||
| 1 | 32 | ||||||
| 13 | 1 | 1 | 5 | use warnings; | |||
| 1 | 1 | ||||||
| 1 | 30 | ||||||
| 14 | 1 | 1 | 663 | use Lustre::Info::OST; | |||
| 1 | 1 | ||||||
| 1 | 24 | ||||||
| 15 | 1 | 1 | 446 | use Lustre::Info::Export; | |||
| 1 | 2 | ||||||
| 1 | 24 | ||||||
| 16 | 1 | 1 | 608 | use Lustre::Info::MDT; | |||
| 1 | 3 | ||||||
| 1 | 31 | ||||||
| 17 | |||||||
| 18 | require Exporter; | ||||||
| 19 | 1 | 1 | 4 | use vars qw($VERSION @ISA @EXPORT @EXPORT_OK); | |||
| 1 | 2 | ||||||
| 1 | 5278 | ||||||
| 20 | |||||||
| 21 | @ISA = qw(Exporter); | ||||||
| 22 | @EXPORT = qw(); | ||||||
| 23 | @EXPORT_OK = qw(); | ||||||
| 24 | $VERSION = '0.02'; | ||||||
| 25 | |||||||
| 26 | 1 | 1 | 7 | use constant PROCFS_LUSTRE => "/proc/fs/lustre"; | |||
| 1 | 188 | ||||||
| 1 | 603 | ||||||
| 27 | 1 | 1 | 6 | use constant PROCFS_OBDFILTER => "/proc/fs/lustre/obdfilter"; | |||
| 1 | 1 | ||||||
| 1 | 53 | ||||||
| 28 | 1 | 1 | 5 | use constant PROCFS_MDS => "/proc/fs/lustre/mds"; | |||
| 1 | 2 | ||||||
| 1 | 5382 | ||||||
| 29 | |||||||
| 30 | ########################################################################## | ||||||
| 31 | # Creates a new Spy Object | ||||||
| 32 | sub new { | ||||||
| 33 | 1 | 1 | 1 | 920 | my($class,%args) = @_; | ||
| 34 | 1 | 4 | my $self = {}; | ||||
| 35 | 1 | 5 | bless($self,$class); | ||||
| 36 | 1 | 5 | return $self; | ||||
| 37 | } | ||||||
| 38 | |||||||
| 39 | ########################################################################## | ||||||
| 40 | # Return (uncached) list of all OBD Objects | ||||||
| 41 | sub get_ost_list { | ||||||
| 42 | 1 | 1 | 1 | 452 | my($self) = @_; | ||
| 43 | 1 | 2 | my @list = (); | ||||
| 44 | 1 | 50 | 41 | opendir(OBD, PROCFS_OBDFILTER) or return \@list; | |||
| 45 | 0 | 0 | while(defined(my $dirent = readdir(OBD))) { | ||||
| 46 | 0 | 0 | 0 | next if $dirent =~ /^\./; # dotfile | |||
| 47 | 0 | 0 | 0 | next if ! -d join("/",PROCFS_OBDFILTER, $dirent); | |||
| 48 | 0 | 0 | push(@list, $dirent); | ||||
| 49 | } | ||||||
| 50 | 0 | 0 | closedir(OBD); | ||||
| 51 | 0 | 0 | return \@list; | ||||
| 52 | } | ||||||
| 53 | |||||||
| 54 | ########################################################################## | ||||||
| 55 | # Returns (uncached) list of all exports known to all OSTs | ||||||
| 56 | sub get_export_list { | ||||||
| 57 | 0 | 0 | 1 | 0 | my($self) = @_; | ||
| 58 | 0 | 0 | my $list = {}; | ||||
| 59 | 0 | 0 | my @osts = $self->get_ost_list; | ||||
| 60 | |||||||
| 61 | 0 | 0 | foreach my $this_ost (@{$self->get_ost_list}) { | ||||
| 0 | 0 | ||||||
| 62 | 0 | 0 | my $export_dir = PROCFS_OBDFILTER."/$this_ost/exports/"; | ||||
| 63 | 0 | 0 | 0 | opendir(EXP, $export_dir) or next; | |||
| 64 | 0 | 0 | while(defined(my $dirent = readdir(EXP))) { | ||||
| 65 | 0 | 0 | 0 | next if $dirent =~ /^\./; # dotfile; | |||
| 66 | 0 | 0 | 0 | next if ! -d $export_dir.$dirent; | |||
| 67 | 0 | 0 | $list->{$dirent}++; | ||||
| 68 | } | ||||||
| 69 | 0 | 0 | closedir(EXP); | ||||
| 70 | } | ||||||
| 71 | 0 | 0 | my @exports = keys(%$list); | ||||
| 72 | 0 | 0 | return \@exports; | ||||
| 73 | } | ||||||
| 74 | |||||||
| 75 | ########################################################################## | ||||||
| 76 | # Return (unchaced) list of all MTD Objects | ||||||
| 77 | sub get_mdt_list { | ||||||
| 78 | 0 | 0 | 1 | 0 | my @list = (); | ||
| 79 | 0 | 0 | 0 | opendir(OBD, PROCFS_MDS) or return \@list; | |||
| 80 | 0 | 0 | while(defined(my $dirent = readdir(OBD))) { | ||||
| 81 | 0 | 0 | 0 | next if $dirent =~ /^\./; # dotfile | |||
| 82 | 0 | 0 | 0 | next if ! -d join("/",PROCFS_MDS, $dirent); | |||
| 83 | 0 | 0 | push(@list, $dirent); | ||||
| 84 | } | ||||||
| 85 | 0 | 0 | closedir(OBD); | ||||
| 86 | 0 | 0 | return \@list; | ||||
| 87 | } | ||||||
| 88 | |||||||
| 89 | ########################################################################## | ||||||
| 90 | # Returns TRUE if current host is acting as an OST | ||||||
| 91 | sub is_ost { | ||||||
| 92 | 1 | 50 | 1 | 1 | 55 | return ( -d PROCFS_LUSTRE."/ost" ? 1 : 0 ); | |
| 93 | } | ||||||
| 94 | |||||||
| 95 | ########################################################################## | ||||||
| 96 | # Returns TRUE if current host is acting as an MDS | ||||||
| 97 | sub is_mds { | ||||||
| 98 | 1 | 50 | 1 | 1 | 529 | return ( -d PROCFS_LUSTRE."/mds" ? 1 : 0 ); | |
| 99 | } | ||||||
| 100 | |||||||
| 101 | ########################################################################## | ||||||
| 102 | # Returns TRUE if current host is acting as an MDT | ||||||
| 103 | sub is_mdt { | ||||||
| 104 | 1 | 50 | 1 | 1 | 506 | return ( -d PROCFS_LUSTRE."/mdt" ? 1 : 0 ); | |
| 105 | } | ||||||
| 106 | |||||||
| 107 | ########################################################################## | ||||||
| 108 | # Return object to __PACKAGE__::OST Class | ||||||
| 109 | sub get_ost { | ||||||
| 110 | 0 | 0 | 1 | my($self,$ostname) = @_; | |||
| 111 | 0 | return Lustre::Info::OST->new(super=>$self, ostname=>$ostname); | |||||
| 112 | } | ||||||
| 113 | |||||||
| 114 | ########################################################################## | ||||||
| 115 | # Returns object to __PACKAGE__::Export Class | ||||||
| 116 | sub get_export { | ||||||
| 117 | 0 | 0 | 1 | my($self, $expname) = @_; | |||
| 118 | 0 | return Lustre::Info::Export->new(super=>$self, export=>$expname); | |||||
| 119 | } | ||||||
| 120 | |||||||
| 121 | ########################################################################## | ||||||
| 122 | # Returns object to __PACKAGE__::MDT | ||||||
| 123 | sub get_mdt { | ||||||
| 124 | 0 | 0 | 1 | my($self, $mdtname) = @_; | |||
| 125 | 0 | return Lustre::Info::MDT->new(super=>$self, mdtname=>$mdtname); | |||||
| 126 | } | ||||||
| 127 | |||||||
| 128 | ########################################################################## | ||||||
| 129 | # Return current lustre version (undef if lustre is not loaded) | ||||||
| 130 | sub get_lustre_version { | ||||||
| 131 | 0 | 0 | 1 | my $ver = undef; | |||
| 132 | 0 | 0 | open(LF, PROCFS_LUSTRE."/version") or return $ver; | ||||
| 133 | 0 | while( |
|||||
| 134 | 0 | 0 | if($_ =~ /^lustre: (\d.+)$/) { $ver = $1 } | ||||
| 0 | |||||||
| 135 | } | ||||||
| 136 | 0 | close(LF); | |||||
| 137 | 0 | return $ver; | |||||
| 138 | } | ||||||
| 139 | |||||||
| 140 | |||||||
| 141 | |||||||
| 142 | |||||||
| 143 | ########################################################################## | ||||||
| 144 | # Try to parse a lustre statistics file created by lprocfs | ||||||
| 145 | sub _parse_stats_file { | ||||||
| 146 | 0 | 0 | my($self,$fname) = @_; | ||||
| 147 | |||||||
| 148 | 0 | my $data = {}; | |||||
| 149 | 0 | my $snap = 0; | |||||
| 150 | 0 | 0 | open(P, $fname) or return undef; | ||||
| 151 | 0 | while( ) { |
|||||
| 152 | # req_waittime 21932809 samples [usec] 3 1047811 9446315012 4121280741355958 (<-- sqcount) | ||||||
| 153 | 0 | 0 | if(my($name,$samples,$format,$min,$max,$count) = $_ =~ /^(\S+)\s+(\d+) samples \[(.+)\]\s+(\d+)\s+(\d+)\s+(\d+)[^\d]/) { # note: sqcount is not used | ||||
| 0 | |||||||
| 0 | |||||||
| 154 | 0 | $data->{$name} = { format=>$format, samples=>$samples, count=>$count }; | |||||
| 155 | } | ||||||
| 156 | elsif(my($rqname,$rqx,$rqformat) = $_ =~ /^(\S+)\s+(\d+) samples \[(.+)\]/) { | ||||||
| 157 | 0 | $data->{$rqname} = { format=>$rqformat, samples=>$rqx, count=>$rqx }; | |||||
| 158 | } | ||||||
| 159 | elsif($_ =~ /^snapshot_time\s+([0-9.]+) /) { | ||||||
| 160 | 0 | $snap = $1; | |||||
| 161 | } | ||||||
| 162 | } | ||||||
| 163 | 0 | close(P); | |||||
| 164 | 0 | return({ timestamp=>$snap, data=>$data }); | |||||
| 165 | } | ||||||
| 166 | |||||||
| 167 | ########################################################################## | ||||||
| 168 | # Try to parse the per-export 'brw' statistics file | ||||||
| 169 | sub _parse_brw_file { | ||||||
| 170 | 0 | 0 | my($self,$fname) = @_; | ||||
| 171 | |||||||
| 172 | 0 | my $ctx = ''; | |||||
| 173 | 0 | my $r = {}; | |||||
| 174 | 0 | 0 | open(P, $fname) or return undef; | ||||
| 175 | 0 | while( ) { |
|||||
| 176 | 0 | 0 | 0 | if($_ =~ /^snapshot_time:\s+([0-9.]+) /) { | |||
| 0 | |||||||
| 0 | |||||||
| 0 | |||||||
| 177 | 0 | $r->{timestamp} = $1; | |||||
| 178 | } | ||||||
| 179 | elsif($_ =~ /([^:]+?)(\s+)ios .+ ios/) { | ||||||
| 180 | 0 | $ctx = lc($1); | |||||
| 181 | 0 | $ctx =~ tr/a-z0-9/_/c; | |||||
| 182 | } | ||||||
| 183 | elsif($_ =~ /^$/) { | ||||||
| 184 | 0 | $ctx = ''; | |||||
| 185 | } | ||||||
| 186 | elsif($ctx && $_ =~ /^(.+):\s+(\d+)\s+\d+\s+\d+\s+\|\s*(\d+)\s+\d+/) { | ||||||
| 187 | 0 | $r->{data}->{$ctx}->{$1} = { read=>$2, write=>$3 }; | |||||
| 188 | } | ||||||
| 189 | } | ||||||
| 190 | 0 | close(P); | |||||
| 191 | 0 | return $r; | |||||
| 192 | } | ||||||
| 193 | |||||||
| 194 | |||||||
| 195 | ########################################################################## | ||||||
| 196 | # Quick'n'dirty 'generic' parser | ||||||
| 197 | sub _parse_generic_file { | ||||||
| 198 | 0 | 0 | my($self,$fname) = @_; | ||||
| 199 | |||||||
| 200 | 0 | my $r = {}; | |||||
| 201 | 0 | 0 | open(P, $fname) or return undef; | ||||
| 202 | 0 | while( ) { |
|||||
| 203 | 0 | 0 | if(my($k,$v) = $_ =~ /^(\S+):\s+(.+)$/) { | ||||
| 204 | 0 | 0 | if($v =~ /^(\d+)\/(\d+)$/) { | ||||
| 205 | 0 | $v = [$1,$2]; | |||||
| 206 | } | ||||||
| 207 | 0 | $r->{$k} = $v; | |||||
| 208 | } | ||||||
| 209 | } | ||||||
| 210 | 0 | close(P); | |||||
| 211 | 0 | return $r; | |||||
| 212 | } | ||||||
| 213 | |||||||
| 214 | ########################################################################## | ||||||
| 215 | # Perform an addition on multiple deep hashrefs | ||||||
| 216 | # This could also be implemented by using recursion but perl is somewhat | ||||||
| 217 | # slow wehn it comes to call sub()'s, so eval should be faster for very | ||||||
| 218 | # 'deep' hashes. | ||||||
| 219 | sub sum_up_deep { | ||||||
| 220 | 0 | 0 | 0 | my($self, $size, @reflist) = @_; | |||
| 221 | |||||||
| 222 | 0 | my $to_eval_h = ''; | |||||
| 223 | 0 | my $to_eval_m = ''; | |||||
| 224 | 0 | my $to_eval_b = ''; | |||||
| 225 | 0 | my $eval_code = undef; | |||||
| 226 | 0 | my $res = {}; | |||||
| 227 | 0 | foreach my $tnum (0..$size) { | |||||
| 228 | 0 | my $nnum = $tnum+1; | |||||
| 229 | 0 | my $xtab = ( " " x $tnum ); | |||||
| 230 | 0 | $to_eval_h .= "${xtab}foreach my \$l$nnum (keys(%{\$lroot$to_eval_m})) {\n"; | |||||
| 231 | 0 | $to_eval_b = "$xtab}\n$to_eval_b"; | |||||
| 232 | 0 | $to_eval_m .= "->{\$l$nnum}"; | |||||
| 233 | } | ||||||
| 234 | |||||||
| 235 | # Assemble perl-loop-code: | ||||||
| 236 | 0 | $eval_code = "$to_eval_h\t\t\$res$to_eval_m += \$lroot$to_eval_m\n".$to_eval_b; | |||||
| 237 | |||||||
| 238 | #..and execute for each given hashref | ||||||
| 239 | 0 | foreach my $lroot (@reflist) { | |||||
| 240 | 0 | eval $eval_code; | |||||
| 241 | 0 | 0 | return undef if $@; # error? -> most likely caused by an invalid $size setting! | ||||
| 242 | } | ||||||
| 243 | 0 | return $res; | |||||
| 244 | } | ||||||
| 245 | |||||||
| 246 | |||||||
| 247 | 1; | ||||||
| 248 | __END__ |