Whamcloud - gitweb
a18f3111d3107d761e05760440a458cf2aba1bee
[fs/lustre-release.git] / lustre-iokit / ost-survey / ost-survey
1 #!/usr/bin/perl
2 # This script is to be run on a client machine and will test all the
3 # OSTs to determine which is the fastest and slowest
4 # The current test method is as follows:
5 #   -Create a directory for each OST
6 #   -Use 'lfs setstripe' to set the Lustre striping such that IO goes to
7 #     only one OST
8 #   -Use 'dd' to write and read a file of a specified size
9 #   -Compute the average, and Standard deviation 
10 #   -Find the slowest OST for read and write
11 #   -Find the Fastest OST for read and write
12
13 # GLOBALS
14 $pname = $0;                     # to hold program name
15 $OSTS = 0;                       # Number of OSTS we will loop over
16 $BSIZE = 1024 * 1024;            # Size of i/o block
17 $MNT = "/mnt/lustre";            # Location of Lustre file system
18 $FSIZE = 30;                     # Number of i/o blocks
19
20 # Usage
21 sub usage () {
22         print "Usage: $pname [-s <size>] [-h] <Lustre_Path>\n";
23         print "[OPTIONS]\n";
24         print "  -s: size of test file in MB (default $FSIZE MB)\n";
25         print "  -h: To display this help\n";
26         print "example : $pname /mnt/lustre\n";
27         exit 1;
28 }
29
30 # ost_count subroutine ets globle variable $OST with Number of OST's
31 # Also fills 1 for active OST indexes in ACTIVEOST_INX array.
32 sub ost_count () {
33         $OSTS = `lctl get_param -n lov.*-clilov-*.activeobd`;
34         if ( $? ) {
35                 die "Read lov.*-clilov-*.activeobd error: $?\n";
36         }
37         print "Number of Active OST devices : $OSTS";
38
39         $numost = `lctl get_param -n lov.*-clilov-*.numobd`;
40         if ( $? ) {
41                 die "Read lov.*-clilov-*.numobd error: $?\n";
42         }
43
44         if ( $numost != $OSTS ) {
45                 printf "Number of non active ots(s): %d\n", ( $numost - $OSTS );
46                 $OSTS = $numost;
47         }
48
49         $targets = `lctl get_param -n lov.*-clilov-*.target_obd`;
50         if ( $? ) {
51                 die "Read lov.*-clilov-*.target_obd error: $?\n";
52         }
53
54         my $count = 0;
55         foreach $line (split /\n/, $targets) {
56                 my ($ost_num, $ost_name, $ost_status) = split(/\s+/, $line);
57                 if ( $ost_status eq "ACTIVE" ) {
58                         $ACTIVEOST_INX[$count] = 1;
59                 }
60                 $count++;
61         }
62 }
63
64 sub cache_off () {
65         $CACHESZ = `lctl get_param -n llite.*.max_cached_mb`;
66         if ( $? ) {
67                 die "Read llite.*.max_cached_mb error: $?\n";
68         }
69
70         $CACHESZ = `echo "$CACHESZ" | grep max_cached_mb | awk '{print \$2}'`;
71
72         system("lctl set_param -n llite.*.max_cached_mb=0");
73 }
74
75 sub cache_on () {
76         system("lctl set_param -n llite.*.max_cached_mb=$CACHESZ");
77 }
78
79 # make_dummy subroutine creates a dummy file that will be used for read operation.
80 sub make_dummy () {
81         my $SIZE = $_[0];
82         my $tempfile = $_[1];
83         system ("dd of=$tempfile if=/dev/zero count=$SIZE bs=$BSIZE 2> /dev/null");
84 }
85
86 # run_test subroutine actually writes and reads data to/from dummy file
87 # and compute corresponding time taken for read and write operation and 
88 # byte transfer for the both operations.
89 # This subroutine also fill corresponding globle arrays with above information.
90 sub run_test () {
91         my $SIZE = $_[0];
92         my $INX=$_[1];
93         my $ACTION=$_[2];
94         my $tempfile = $_[3];
95
96         if ( !(-f $tempfile) && $ACTION eq "read" ) {
97                 &make_dummy($SIZE, $tempfile);
98         }
99         system("sync");
100         my ($ts0, $tu0) = gettimeofday();
101         $tu0 = $ts0 + ($tu0 / 1000000);
102         if ( $ACTION eq "write" ) {
103                 system("dd of=$tempfile if=/dev/zero count=$SIZE bs=$BSIZE 2> /dev/null");
104         } elsif ( $ACTION eq "read" ) {
105                 system("dd if=$tempfile of=/dev/null count=$SIZE bs=$BSIZE 2> /dev/null");
106         } else {
107                 print "Action is neither read nor write\n";
108                 exit 1;
109         }
110         system("sync");
111         my ($ts1, $tu1) = gettimeofday();
112         $tu1 = $ts1 + ($tu1/1000000);
113         my $tdelta = $tu1 - $tu0;
114         my $delta = ($SIZE * $BSIZE / ( $tu1 - $tu0 )) / (1024 * 1024);
115         if ( $ACTION eq "write" ) {
116                 $wTime[$INX] = $tdelta;
117                 $wMBs[$INX] = $delta;
118         } else {
119                 $rTime[$INX] = $tdelta;
120                 $rMBs[$INX] = $delta;
121         }
122 }
123
124 # calculate subroutine compute following things and displays them.
125 #  - Finds worst and best OST for both read and write operations.
126 #  - Compute average of read and write rate from all OSTS
127 #  - Compute Standard deviation for read and write form all OST's
128 sub calculate () {
129         my ($op, $MBs);
130         $op = $_[0];
131         @MBs = @_[1..$#_]; 
132         my $count = 0;
133         my $total = 0;
134         my $avg = 0;
135         my $sd = 0;
136         my $best_OST = 0;
137         my $worst_OST = 0;
138         my $max_mb = 0;
139         my $min_mb = 999999999;
140         while ($count < $OSTS ) {
141                 if ( $ACTIVEOST_INX[$count] ) {
142                         $total = $total + $MBs[$count];
143                         if ($max_mb < $MBs[$count] ) {
144                                 $max_mb = $MBs[$count];
145                                 $best_OST = $count; 
146                         }
147                         if ($min_mb > $MBs[$count] ) {
148                                 $min_mb = $MBs[$count];
149                                 $worst_OST = $count; 
150                         }
151                 }
152                 $count++;
153         }
154         $avg = $total/$OSTS;
155         $total = 0;
156         $count = 0;
157         while ($count < $OSTS ) {
158                 if ( $ACTIVEOST_INX[$count] ) {
159                         $total = $total + ($MBs[$count] - $avg) * ($MBs[$count] - $avg);
160                 }
161                 $count++;
162         }
163         $sd = sqrt($total/$OSTS);
164         printf "Worst  %s OST indx: %d speed: %f\n", $op, $worst_OST, $min_mb;
165         printf "Best   %s OST indx: %d speed: %f\n", $op, $best_OST, $max_mb;
166         printf "%s Average: %f +/- %f MB/s\n", $op, $avg, $sd;
167 }
168
169 # output_all_data subroutine displays speed and time information 
170 # for all OST's for both read and write operations.
171 sub output_all_data () {
172         my $count = 0;
173         print "Ost#  Read(MB/s)  Write(MB/s)  Read-time  Write-time\n";
174         print "----------------------------------------------------\n";
175         while ( $count < $OSTS ) {
176                 if ( $ACTIVEOST_INX[$count] ) { 
177                         printf "%d     %.3f       %.3f        %.3f      %.3f\n",$count, 
178                         $rMBs[$count], $wMBs[$count], $rTime[$count], $wTime[$count];
179                 } else {
180                         printf "%d     Inactive ost\n",$count; 
181                 }
182                 $count = $count + 1;
183         }
184 }
185
186 @rTime = ();
187 @wTime = ();
188 @rMBs = ();
189 @wMBs = ();
190 @ACTIVEOST_INX;
191
192 # Locals
193 my $filename = "";
194 my $dirpath = "";
195 my $flag = 0;
196
197 # Command line parameter parsing
198 use Getopt::Std;
199 getopts('s:h') or usage();
200 usage() if $opt_h;
201 $FSIZE = $opt_s if $opt_s;
202
203 my $i = 0;
204 foreach (@ARGV) {
205         $MNT = $_;
206         $i++;
207         if ($i > 1) {
208                 print "ERROR: extra argument $_\n";
209                 usage();
210         }       
211 }
212 #Check for Time::HiRes module 
213 my $CheckTimeHiRes = "require Time::HiRes";
214 eval ($CheckTimeHiRes) or die "You need to install the perl-Time-HiRes package to use this script\n";
215 my $LoadTimeHiRes = "use Time::HiRes qw(gettimeofday)";
216 eval ($LoadTimeHiRes);
217
218 use POSIX qw(strftime);
219 my $time_v = time();
220 my $hostname = `lctl list_nids | head -1` or die "You need to install lctl to use this script\n";
221 chop($hostname);
222 print "$pname: ", strftime("%D", localtime($time_v));
223 print " OST speed survey on $MNT from $hostname\n";
224
225 # get OST count
226 ost_count ();
227 # turn off local cache
228 cache_off ();
229
230 $dirpath = "$MNT/ost_survey_tmp";
231 eval { mkpath($dirpath) };
232 if ($@) {
233         print "Couldn't create $dirpath: $@";
234         exit 1;
235 }
236
237 use File::Path;
238 $CNT = 0;
239 while ($CNT < $OSTS) {
240         $filename = "$dirpath/file$CNT";
241         if ( $ACTIVEOST_INX[$CNT] ) {
242                 # set stripe for OST number $CNT
243                 system ("lfs setstripe $filename 0 $CNT 1");
244                 # Perform write for OST number $CNT
245                 &run_test($FSIZE,$CNT,"write",$filename);
246                 $flag++;
247         }
248         $CNT = $CNT + 1;
249 }
250 $CNT = 0;
251 while ($CNT < $OSTS) {
252         $filename = "$dirpath/file$CNT";
253         if ( $ACTIVEOST_INX[$CNT] ) {
254                 # Perform read for OST number $CNT
255                 &run_test($FSIZE,$CNT,"read",$filename);
256                 $flag++;
257         }
258         $CNT = $CNT + 1;
259 }
260
261 # if read or write performed on any OST then display information. 
262 if ( $flag ) {
263         if ( $flag > 1 ) {
264                 &calculate("Read",@rMBs);
265                 &calculate("Write",@wMBs);
266         }
267         output_all_data ();
268 } else {
269         print "There is no active OST's found\n";
270 }
271
272 cache_on ();
273
274 eval { rmtree($dirpath) };
275 if ($@) {
276         print "Warning: Couldn't remove $dirpath: $@";
277 }