Whamcloud - gitweb
19cdaf040be209272314d7d1eb64059c22f8d500
[fs/lustre-release.git] / lustre-iokit / obdfilter-survey / obdfilter-survey
1 #!/bin/bash
2 set -e
3 ######################################################################
4 # customize per survey
5
6 # specify obd instances to exercise
7 # these can be either...
8 # obdfilter instances (set 'ost_names')
9 # ...or...
10 # echo_client instances (set 'client_names')
11 # ... use 'host:name' for obd instances on other nodes.
12 # allow these to be passed in via string...
13 # OR
14 # one can specify only case=disk or case=network or case=netdisk through 
15 # command line.
16
17 # Perquisite: For "disk" case and "netdisk" case you need to have lustre setup
18 #             with one or more ost's. For "network" case  you need to have all
19 #             modules (those llmount.sh loades) loaded in kernel. And the 
20 #             'lctl dl' output must be blank.
21
22 # How to run test:
23 # case 1 (local disk):
24 #   $ nobjhi=2 thrhi=2 size=1024 case=disk sh obdfilter-survey
25 #   one can also run test with user defined targets as follows,
26 #   $ nobjhi=2 thrhi=2 size=1024 targets="lustre-OST0000 lustre-OST0001 ..." sh obdfilter-survey
27 # case 2 (network):
28 #   $ nobjhi=2 thrhi=2 size=1024 targets="<name/ip_of_server>" case=network sh obdfilter-survey
29 #   where, targets is name or ip address of system, which you want to 
30 #   set as server.
31 # case 3 (network and disk):
32 #   $ nobjhi=2 thrhi=2 size=1024 case=netdisk sh obdfilter-survey
33 #   one can also run test with user defined targets as follows,
34 #   $ nobjhi=2 thrhi=2 size=1024 targets="<osc_name> ..." sh obdfilter-survey
35 #[ NOTE: It is advised to have automated login (passwordless entry) between server and 
36 #  client systems on which this test runs.]
37
38 # include library 
39 source $(dirname $0)/libecho
40
41 # The following variables can be set in the environment, or on the
42 # command line
43 # result file prefix (date/time + hostname makes unique)
44 # NB ensure path to it exists
45 rslt_loc=${rslt_loc:-"/tmp"}
46 rslt=${rslt:-"$rslt_loc/obdfilter_survey_`date +%F@%R`_`uname -n`"}
47
48 # Set this true to check file contents
49 verify=${verify:-0}
50
51 # test targets
52 targets=${targets:-""}
53 # test case
54 case=${case:-"disk"}
55
56 # total size (MBytes) per obd instance
57 # large enough to avoid cache effects 
58 # and to make test startup/shutdown overhead insignificant
59 size=${size:-16384}
60
61 # record size (KBytes) ( 7168 max)
62 rszlo=${rszlo:-1024}
63 rszhi=${rszhi:-1024}
64
65 # number of objects per OST
66 nobjlo=${nobjlo:-1}
67 #was nobjhi=${nobjhi:-512}
68 nobjhi=${nobjhi:-16}
69
70 # threads per OST (1024 max)
71 thrlo=${thrlo:-1}
72 thrhi=${thrhi:-16}
73
74 export LC_ALL=POSIX
75
76 # End of variables
77
78 # create a set of objects, check there are 'n' contiguous ones and
79 # echo out the first or 'ERROR'
80 # parameter: 1. hostname
81 #            2. device number
82 #            3. number of object to be created (specified by user)
83 #            4. tempfile name
84 create_objects () {
85     local host=$1
86     local devno=$2
87     local nobj=$3
88     local rfile=$4
89     remote_shell $host $lctl --device $devno create $nobj > $rfile 2>&1
90     first=0
91     prev=0
92     count=0
93     error=0
94
95     # Count number of objects (lines containing " is object id "), and
96     # ensure that the objects numbers are sequential.
97     #
98     exec 3< $rfile
99     while read -u3 line; do
100         case "$line" in
101         ( *' is object id '* )
102             set -- $line
103             if test $(( count += 1 )) -gt 1 ; then
104                 (( $6 != prev + 1 )) && error=1
105             else
106                 first=$(( $6 + 0 ))
107             fi
108             prev=$6
109             ;;
110         esac
111     done
112     exec 3<&-
113
114     if [ $nobj -ne $count ]; then 
115         echo "ERROR: $nobj != $count" >&2 
116         cat $rfile >&2 
117         echo "ERROR" 
118     elif [ $error -ne 0 ]; then 
119         echo "ERROR: non contiguous objs found" >&2
120         echo ERROR
121     else 
122         echo $first 
123     fi
124     return $error
125 }
126
127 # destroys all objects created in create_objects routine
128 # parameter: 3. start obj id.
129 destroy_objects () {
130     local host=$1
131     local devno=$2
132     local obj0=$3
133     local nobj=$4
134     local rfile=$5
135     remote_shell $host $lctl --device $devno destroy $obj0 $nobj > $rfile 2>&1
136 }
137
138 get_stats () {
139     local rfile=$1
140     gawk < $rfile \
141         '/^Selected device [0-9]+$/ {n = 0; next}\
142         /error/ {n = -1; exit}\
143         /^Total/ {next}\
144         /^[0-9]+\/[0-9]+ Total: [0-9]+\.[0-9]+\/second$/ {n++; v=strtonum($3); \
145                                                           if (n == 1 || v < min) min = v;\
146                                                           if (n == 1 || v > max) max = v;\
147                                                           next}\
148         {if (n != 0) {n = -1; exit}}\
149         END {printf "%d %f %f\n", n, min, max}'
150 }
151
152 get_global_stats () {
153     local rfile=$1
154     awk < $rfile 'BEGIN {n = 0;}\
155                   {n++; if (n == 1) {err = $1; min = $2; max = $3} else\
156                                     {if ($1 < err) err = $1;\
157                                      if ($2 < min) min = $2;\
158                                      if ($3 > max) max = $3}}\
159                   END {if (n == 0) err = 0;\
160                        printf "%d %f %f\n", err, min, max}'
161 }
162
163 # enable or disable data check.
164 # parameter: 1. read/write
165 testname2type () {
166     # 'x' disables data check
167     if ((verify)); then
168         x=""
169     else
170         x="x"
171     fi
172     case $1 in
173         *write*)  echo "w$x";;
174         *)        echo "r$x";;
175     esac
176 }
177
178 # for "echo_client + obdfilter" case, "prep + commit" mode should be used
179 # for "echo_client + osc" case, "BRW" mode should be used
180 testcase2mode() {
181     case $case in
182     disk) echo "p$1";;
183     *)    echo "g";;
184     esac
185 }
186
187 print_summary () {
188     if [ "$1" = "-n" ]; then
189         minusn=$1; shift
190     else
191         minusn=""
192     fi
193     echo $minusn "$*" >> $rsltf
194     echo $minusn "$*"
195 }
196
197 # Customisation variables
198 #####################################################################
199 # One can change variable values in this section as per requirements
200
201 if [ -n "$targets" ]; then
202     declare -a ost_names
203     declare -a client_names
204     count=0
205     for name in $targets; do
206         if [ $case == "disk" ]; then
207             ost_names[$count]=$name
208         else
209             client_names[$count]=$name
210         fi
211         count=$((count+1))
212     done
213 fi
214
215 # what tests to run (first must be write)
216 tests_str=${tests_str:-""}
217 if [ -n "$tests_str" ]; then
218     declare -a tests
219     count=0
220     for name in $tests_str; do
221         tests[$count]=$name
222         count=$((count+1))
223     done
224 else
225     #tests=(write rewrite read reread rewrite_again)
226     tests=(write rewrite read)
227 fi
228
229 # restart from here iff all are defined
230 restart_rsz=
231 restart_thr=1
232 restart_nobj=1
233
234 # machine's page size (K)
235 if [ -z "$PAGE_SIZE" ]; then
236     if which python >/dev/null; then
237         PAGE_SIZE=`echo 'import resource; print resource.getpagesize()/1024;' |python`
238     fi
239 fi
240 PAGE_SIZE=${PAGE_SIZE:-4}
241
242 # max buffer_mem (total_threads * buffer size)
243 # (to avoid lctl ENOMEM problems)
244 max_buffer_mem=$((1024 * 1024))
245 snap=1
246 clean_srv_OSS=0
247 # Customisation variables ends here. 
248 #####################################################################
249 # leave the rest of this alone unless you know what you're doing...
250
251 # check and insert obdecho module
252 if ! lsmod | grep obdecho > /dev/null; then
253     modprobe obdecho
254 fi
255 if [ ${#tests[@]} -eq 0 -o "${tests[0]}" != "write" ]; then
256     echo "tests: ${tests[@]}"
257     echo "First test must be 'write'" 1>&2
258     exit 1
259 fi
260
261 rsltf="${rslt}.summary"
262 workf="${rslt}.detail"
263 cmdsf="${rslt}.script"
264 vmstatf="${rslt}.vmstat"
265 echo -n > $rsltf
266 echo -n > $workf
267
268 # hide a little trick to unset this from the command line
269 if [ "$lustre_root" == " " ]; then
270     unset lustre_root
271 fi
272
273 if [ -z "$lustre_root" ]; then
274     lctl=lctl
275 else
276     lctl=${lustre_root}/utils/lctl
277 fi
278
279 # split out hostnames from client/ost names
280 ndevs=0
281 for trgt in $targets; do
282     str=(`split_hostname $trgt`)
283     host_names[$ndevs]=${str[0]}
284     client_names[$ndevs]=${str[1]}
285     ndevs=$((ndevs+1))
286 done
287 if [ $case == "disk" ]; then
288     if [ $rszhi -gt 1024 ]; then
289         echo "Test disk case support maximum 1024KB IO data" \
290              "(rszhi=$rszhi is too big) please use a smaller value."
291         exit 1
292     fi
293     for ((i = 0; i < $ndevs; i++)); do
294         ost_names[$i]=${client_names[$i]}
295     done
296 fi
297 if [ $case == "netdisk" ]; then
298         if [ "$targets" ]; then
299             for ((i = 0; i < $ndevs; i++)); do
300                 setup_osc_for_remote_ost ${host_names[$i]} \
301                                          ${client_names[$i]} $i
302                 osc_name=${client_names[$i]}_osc
303                 ec_using_osc $osc_name
304                 cleanup_oscs="$cleanup_oscs $osc_name"
305             done
306         else
307             client_names_str=$($lctl dl | grep -v mdt | \
308                 awk '{if ($2 == "UP" && $3 == "osc") {print $4} }')
309             count=0;
310             for name in $client_names_str; do
311                 client_names[$count]=`echo $name | sed 's/-osc-.*$//'`
312                 count=$((count+1))
313             done
314
315             host_names_str=$($lctl dl -t | grep -v mdt | \
316                 awk '{if ($2 == "UP" && $3 == "osc") {print $7} }')
317             count=0;
318             for name in $host_names_str; do
319                 host_names[$count]=`echo $name | sed 's/@.*$//'`
320                 count=$((count+1))
321             done
322
323             for (( i = 0; i < $count; i++ )) do
324                 setup_osc_for_remote_ost ${host_names[$i]} \
325                                          ${client_names[$i]} $i
326                 osc_name=${client_names[$i]}_osc
327                 ec_using_osc $osc_name
328                 cleanup_oscs="$cleanup_oscs $osc_name"
329             done
330         fi
331
332         echo_clients=$($lctl dl | grep echo_client | awk "{if (\$2 == \"UP\" && \$3 == \"echo_client\") {print \$4} }")
333         cnt=0;
334         for name in $echo_clients; do
335             client_names[$cnt]=$name
336             host_names[$cnt]=localhost
337             cnt=$((cnt+1))
338         done
339         ndevs=${#client_names[@]}
340 fi
341 if [ $case == "network" ]; then
342     server_nid=$targets
343     if [ -z "$server_nid" ]; then
344         echo "Specify hostname or ip-address of server"
345         exit 1;
346     fi
347     # check for obdecho module on server
348     if ! dsh $server_nid root "lsmod | grep obdecho > /dev/null"; then
349         dsh $server_nid root "modprobe obdecho"
350     fi
351     # Now do the server setup
352     setup_srv_obd $server_nid "echo_srv"
353     oss_on_srv=`dsh $server_nid root "$lctl dl | grep OSS" | awk '{ print $4 }'`
354     if [ -z $oss_on_srv ]; then
355         setup_OSS $server_nid
356         clean_srv_OSS=1
357     fi
358     if ! dsh $server_nid root "$lctl dl | grep obdecho > /dev/null 2>&1"; then
359         echo "obdecho not setup on server"
360         exit 1
361     fi
362     if ! dsh $server_nid root "$lctl dl | grep ost > /dev/null 2>&1"; then
363         echo "ost not setup on server"
364         exit 1
365     fi
366     # Now start client setup
367     osc_names_str=$($lctl dl| grep osc | grep -v mdt | grep UP)
368     if [ -n "$osc_names_str" ]; then
369         echo "The existing setup must be cleaned";
370         exit 0;
371     fi
372     ec_using_srv_nid $server_nid "echotmp" "echotmp_UUID"
373     client_names[0]="echotmp_ecc"
374 fi
375 if [ -z "$targets" ]; then
376     if [ $case == "disk" ]; then
377         get_targets
378         ndevs=${#ost_names[@]}  
379     fi  
380 fi
381 # get vmstat started
382 # disable portals debug and get obdecho loaded on all relevant hosts
383 unique_hosts=(`unique ${host_names[@]}`)
384 load_obdechos
385 pidcount=0
386 for host in ${unique_hosts[@]}; do
387     host_vmstatf=${vmstatf}_${host}
388     echo -n > $host_vmstatf
389     remote_shell $host "vmstat 5 >> $host_vmstatf" &> /dev/null &
390     pid=$!
391     vmstatpids[$pidcount]=$pid
392     pidcount=$((pidcount+1))
393 done
394 # get all the echo_client device numbers and names
395 for ((i=0; i < $ndevs; i++)); do
396     host=${host_names[$i]}
397     devno=(`get_ec_devno $host "${client_names[$i]}" "${ost_names[$i]}"`)
398     if ((${#devno[@]} != 3)); then
399         exit 1
400     fi
401     devnos[$i]=${devno[0]}
402     client_names[$i]=${devno[1]}
403     do_teardown_ec[$i]=${devno[2]}
404 done
405 if (($ndevs <= 0 || ${#host_names[@]} <= 0)); then
406     echo "no devices or hosts specified"
407     cleanup 0 $clean_srv_OSS $cleanup_oscs
408 fi
409 # Buffers will be spread out among all hosts, so allow for that
410 max_buffer_mem=$(( ${max_buffer_mem} * ${#unique_hosts[@]} ))
411 print_summary "$(date) Obdfilter-survey for case=$case from $(hostname)"
412 for ((rsz = $rszlo; rsz <= $rszhi; rsz*=2)); do
413     for ((nobj = $nobjlo; nobj <= $nobjhi; nobj*=2)); do 
414         for ((thr = $thrlo; thr <= $thrhi; thr*=2)); do
415             if ((thr % nobj)); then
416                 continue
417             fi
418             # restart?
419             if [ -n "$restart_rsz" -a\
420                  -n "$restart_nobj" -a\
421                  -n "$restart_thr" ]; then
422                 if ((rsz < restart_rsz ||\
423                      (rsz == restart_rsz &&\
424                       (nobj < restart_nobj ||\
425                        (nobj == restart_nobj &&\
426                         thr < restart_thr))))); then
427                     continue;
428                 fi
429             fi
430             # compute parameters
431             total_thr=$((ndevs*thr))
432             total_nobj=$((ndevs*nobj))
433             pages=$((rsz/PAGE_SIZE))
434             actual_rsz=$((pages*PAGE_SIZE))
435             count=$((size*1024/(actual_rsz*thr)))
436             actual_size=$((actual_rsz*count*thr))
437             total_size=$((actual_size*ndevs))
438             # show computed parameters
439             str=`printf 'ost %2d sz %8dK rsz %4dK obj %4d thr %4d ' \
440                      $ndevs $total_size $actual_rsz $total_nobj $total_thr`
441             echo "=======================> $str" >> $workf
442             print_summary -n "$str"
443             if ((total_thr * actual_rsz > max_buffer_mem)); then
444                 print_summary "Too much buffer space"
445                 continue
446             fi
447             # create the objects
448             tmpf="${workf}_tmp"
449             for ((idx = 0; idx < $ndevs; idx++)); do
450                 host=${host_names[$idx]}
451                 devno=${devnos[$idx]}
452                 client_name="${host}:${client_names[$idx]}"
453                 echo "=============> Create $nobj on $client_name" >> $workf
454                 first_obj=`create_objects $host $devno $nobj $tmpf`
455                 cat $tmpf >> $workf
456                 rm $tmpf
457                 if [ $first_obj = "ERROR" ]; then
458                     print_summary "created object #s on $client_name not contiguous"
459                     exit 1
460                 fi
461                 first_objs[$idx]=$first_obj
462             done
463             # run tests
464             for test in ${tests[@]}; do
465                 declare -a pidarray
466                 for host in ${unique_hosts[@]}; do
467                     echo "starting run for test: $test rsz: $rsz threads: $thr objects: $nobj" >> ${vmstatf}_${host}
468                 done
469                 print_summary -n "$test "
470                 # create per-host script files
471                 for host in ${unique_hosts[@]}; do
472                     echo -n > ${cmdsf}_${host}
473                 done
474                 for ((idx = 0; idx < $ndevs; idx++)); do
475                     host=${host_names[$idx]}
476                     devno=${devnos[$idx]}
477                     tmpfi="${tmpf}_$idx"
478                     first_obj=${first_objs[$idx]}
479                     thr_per_obj=$((${thr}/${nobj}))
480                     echo >> ${cmdsf}_${host} \
481                         "$lctl > $tmpfi 2>&1 \\
482                          --threads $thr -$snap $devno \\
483                          test_brw $count `testname2type $test` q $pages \\
484                          ${thr_per_obj}t${first_obj} `testcase2mode $pages` &"
485                 done
486                 pidcount=0
487                 for host in ${unique_hosts[@]}; do
488                     echo "wait" >> ${cmdsf}_${host}
489                     pidarray[$pidcount]=0
490                     pidcount=$((pidcount+1))
491                 done
492                 # timed run of all the per-host script files
493                 t0=`date +%s.%N`
494                 pidcount=0
495                 for host in ${unique_hosts[@]}; do
496                     remote_shell $host bash < ${cmdsf}_${host} &
497                     pidarray[$pidcount]=$!
498                     pidcount=$((pidcount+1))
499                 done
500                 pidcount=0
501                 for host in ${unique_hosts[@]}; do
502                     wait ${pidarray[$pidcount]}
503                     pidcount=$((pidcount+1))
504                 done
505                 #wait
506                 t1=`date +%s.%N`
507                 # clean up per-host script files
508                 for host in ${unique_hosts[@]}; do
509                     rm ${cmdsf}_${host}
510                 done
511
512                 # compute bandwidth from total data / elapsed time
513                 str=`awk "BEGIN {printf \"%7.2f \",\
514                          $total_size / (( $t1 - $t0 ) * 1024)}"`
515                 print_summary -n "$str"
516                 # collect/check individual OST stats
517                 echo -n > $tmpf
518                 for ((idx = 0; idx < $ndevs; idx++)); do
519                     client_name="${host_names[$idx]}:${client_names[$idx]}"
520                     tmpfi="${tmpf}_$idx"
521                     echo "=============> $test $client_name" >> $workf
522                     host="${host_names[$idx]}"
523                     remote_shell $host cat $tmpfi > ${tmpfi}_local
524                     cat ${tmpfi}_local >> $workf
525                     get_stats ${tmpfi}_local >> $tmpf
526                     rm -f $tmpfi ${tmpfi}_local
527                 done
528                 # compute/display global min/max stats
529                 echo "=============> $test global" >> $workf
530                 cat $tmpf >> $workf
531                 stats=(`get_global_stats $tmpf`)
532                 rm $tmpf
533                 if ((stats[0] <= 0)); then
534                     if ((stats[0] < 0)); then
535                         str=`printf "%17s " ERROR`
536                     else
537                         str=`printf "%17s " SHORT`
538                     fi
539                 else
540                     str=`awk "BEGIN {printf \"[%7.2f,%7.2f] \",\
541                              (${stats[1]} * $actual_rsz)/1024,\
542                              (${stats[2]} * $actual_rsz)/1024; exit}"`
543                 fi
544                 print_summary -n "$str"
545             done
546             print_summary ""
547             # destroy objects we created
548             for ((idx = 0; idx < $ndevs; idx++)); do
549                 host=${host_names[$idx]}
550                 devno=${devnos[$idx]}
551                 client_name="${host}:${client_names[$idx]}"
552                 first_obj=${first_objs[$idx]}
553                 echo "=============> Destroy $nobj on $client_name" >> $workf
554                 destroy_objects $host $devno $first_obj $nobj $tmpf
555                 cat $tmpf >> $workf
556                 rm $tmpf
557             done
558         done
559     done
560 done
561 cleanup 0 $clean_srv_OSS $cleanup_oscs
562 exit 0