Whamcloud - gitweb
Branch:HEAD
[fs/lustre-release.git] / lustre-iokit / stats-collect / gather_stats_everywhere.sh
1 #!/bin/sh
2
3 #########################################################################
4 # gather_stats_everywhere:
5 # script on a selection of nodes and collect all the results into a single
6 # tar ball
7 #
8 # Copyright (c) 2007 - Cluster File Systems, Inc.
9 #########################################################################
10 error() {
11         echo "$0: $@"
12         exit 1
13 }
14
15 usage() {
16         printf $"Usage: gather_stats_everywhere [-help] config_file [start|stop|cleanup] <log_name>\n"
17         if [ x$1 = x-h ]
18         then
19                  printf $"
20 The distribution script will run on a single node.  It is parameterised 
21 with a set of target node names.  It may assume ssh/scp to these node 
22 names works without requiring a password.  It will run in 2 modes...
23
24 gather_stats_everywhere config_file start
25
26 ...will copy the script to /tmp everywhere described in
27 config_file running on all the target hosts.  And...
28
29 gather_stats_everywhere config_file stop log_name
30
31 ...will stop script running on all the hosts it started on and collect 
32 all the individual stats files into a single compressed tarball if the log_name is
33 provided.
34
35 The config file is just a list of shell variable assignments that can be
36 customised. 
37
38 Serveral variables must be set in the config file
39
40 Targets: the nodes where run the script.
41 "
42                  exit 0
43         else
44                  exit 1
45         fi
46 }
47
48 options=`getopt -o h --long help:: -- "$@"`
49
50 if [ $? -ne 0 ]
51 then 
52         usage
53 fi
54
55 eval set -- "$options"
56
57 while true
58 do
59         case "$1" in
60                 -h)
61                         usage -h ;;
62                 --help)
63                         usage -h ;;
64                 --)
65                         shift
66                         break ;;
67         esac
68 done
69
70 if [ $# != 2 -a $# != 3 ] ; then
71         usage
72 fi
73
74 CONFIG=$1
75 OPTION=$2
76 shift
77 shift
78
79 GLOBAL_TIMESTAMP=""
80
81 if [ ! -r $CONFIG ]; then
82         error "Config_file: $CONFIG does not exist "
83 fi
84
85 . $CONFIG
86
87 if [ -z "$SCRIPT" ]; then
88         error "SCRIPT in ${CONFIG} is empty"
89 fi      
90
91 if [ -z "$TARGETS" ]; then
92         error "TARGETS in ${CONFIG} is empty"
93 fi
94
95 #check nodes accessiable 
96 Check_nodes_avaible() {
97         local NODES_NOT_AVAIBLE=""
98
99         for TARGET in $TARGETS; do
100                 if ! ping -c 1 -w 3 $TARGET > /dev/null; then 
101                          NODES_NOT_AVAIBLE=$NODES_NOT_AVAIBLE$TARGET
102                 fi
103         done
104         if [ -z "$NODES_NOT_AVAIBLE" ]; then
105                 return 0
106         else
107                 echo "Nodes ${NODES_NOT_AVAIBLE} not respond to ping"
108                 return 1
109         fi
110 }
111
112 if ! Check_nodes_avaible;  then 
113         error "not all the nodes are availble"
114 fi
115
116 Check_nodes_are_clean() {
117         local NODES_NO_CLEAN=""
118
119         # check whether there are running threads on the targets
120         for TARGET in $TARGETS; do
121                 ps_str=`$DSH $TARGET "ps aux | grep -v grep | grep ${SCRIPT}-${TARGET}"`
122                 if [ -n "$ps_str" ]; then
123                         NODES_NO_CLEAN=${NODES_NO_CLEAN}$TARGET
124                 fi
125         done
126
127         if [ -n "$NODES_NO_CLEAN" ]; then
128                 return 1 
129         fi
130
131         return 0 
132 }
133
134 copy_target_script() {
135         local target=$1
136
137         #copy alex's run scripts to the target
138         copy_cmd="$DCP $SCRIPT ${USER}${target}:$TMP/${SCRIPT}-${target}"
139         ${copy_cmd} 1>/dev/null 2>&1 
140         if [ ${PIPESTATUS[0]} != 0 ]; then
141                 echo "copy command failed: ${copy_cmd}" 2>&1
142                 return 1
143         else
144                 echo "$SCRIPT copied to ${USER}${target} (into $TMP)"
145                 return 0
146         fi
147 }
148
149 start_target_script() {
150         local target=$1
151
152         if ! copy_target_script $target; then
153                 echo "copy_target_script $target failed." 2>&1
154                 return 1
155         fi
156
157         #run the script on the target
158         $DSH ${USER}${target} "VMSTAT_INTERVAL=${VMSTAT_INTERVAL} \
159                       SDIO_INTERVAL=${SDIO_INTERVAL} \
160                       SERVICE_INTERVAL=${SERVICE_INTERVAL} \
161                       BRW_INTERVAL=${BRW_INTERVAL}         \
162                       JBD_INTERVAL=${JBD_INTERVAL}         \
163                       IO_INTERVAL=${IO_INTERVAL}           \
164                       MBALLOC_INTERVAL=${MBALLOC_INTERVAL} \
165                       sh ${TMP}/${SCRIPT}-${target} start  \
166                       1> /dev/null 2>/dev/null </dev/null"
167
168         if [ ${PIPESTATUS[0]} != 0 ]; then
169                 echo "Start the ${SCRIPT} on ${target} failed"
170                 return 1
171         else    
172                 echo "Start the ${SCRIPT} on ${target} success"
173                 return 0
174         fi
175 }
176
177 stop_target_script() {
178         local target=$1
179
180         #stop the target script first
181         $DSH ${USER}${target} "sh ${TMP}/${SCRIPT}-${target} stop" 1>/dev/null 2>&1
182         if [ ${PIPESTATUS[0]} != 0 ]; then
183                 echo  "stop the collecting stats script on ${target} failed"
184                 return 1 
185         else    
186                 echo  "stop the collecting stats script on ${target} success"
187         fi
188
189         #remove those tmp file
190         $DSH ${USER}${target} "rm -rf $TMP/${SCRIPT}-${target}" 1>/dev/null 2>&1
191         echo "cleanup ${target} tmp file after stop "
192         return 0
193 }
194
195 generate_timestamp() {
196         if [ "X${GLOBAL_TIMESTAMP}" = "X" ]
197         then
198                 export GLOBAL_TIMESTAMP=`date +%F-%H.%M.%S`
199                 echo "Global Timestamp Created: ${GLOBAL_TIMESTAMP}"
200         fi
201 }
202
203 fetch_target_log() {
204         generate_timestamp
205         local target=$1
206         local date=${GLOBAL_TIMESTAMP}
207         local target_log_name="stats-${target}-${date}"
208
209         echo "Getting log: ${target_log_name}.tar.gz from ${target}"
210         $DSH ${USER}${target} "sh ${TMP}/${SCRIPT}-${target} fetch " \
211                       > $TMP/${target_log_name}.tar.gz
212         echo "Got log: ${target_log_name}.tar.gz from ${target}"
213
214         echo "Moving $TMP/${target_log_name}.tar.gz to $TMP/$log_name"
215         mv $TMP/${target_log_name}.tar.gz $TMP/$log_name
216 }
217
218 fetch_log() {
219         generate_timestamp
220         local log_name=${GLOBAL_TIMESTAMP}
221         local stat_tar_name=$1
222         local -a pids_array
223         local -a clients_array
224
225         if ! mkdir -p $TMP/$log_name ; then
226                 error "can not mkdir $log_name"
227         fi
228
229         #retrive the log_tarball from remote nodes background 
230         local n=0
231         for TARGET in $TARGETS; do
232                 (fetch_target_log ${TARGET}) & 
233                 pids_array[$n]=$!
234                 clients_array[$n]=$TARGET
235                 let n=$n+1
236         done
237         local num_pids=$n
238
239         #Waiting log fetch finished
240         for ((n=0; $n < $num_pids; n++)); do
241                 wait ${pids_array[$n]}
242         done
243
244         #compress the log tarball
245         cmd="$TAR ${stat_tar_name} $TMP/${log_name}"
246         echo "Creating compressed tar file ${stat_tar_name} from log files in  $TMP/${log_name}"
247         ${cmd} 1>/dev/null 2>&1 
248         if [ ${PIPESTATUS[0]} == 0 ]; then
249                 echo "removing temporary directory $TMP/${log_name}"
250                 rm -rf $TMP/${log_name}
251         else
252                 echo "Compressed logfiles are in $TMP/${stat_tar_name}"
253         fi
254 }
255
256 stop_targets_script() {
257         local -a pids_array
258         local -a clients_array
259         local n=0
260         for TARGET in $TARGETS; do
261                 (stop_target_script ${TARGET}) &
262                 pids_array[$n]=$!
263                 clients_array[$n]=$TARGET
264                 let n=$n+1
265         done
266         local num_pids=$n
267         
268         #Waiting log fetch finished
269         for ((n=0; $n < $num_pids; n++)); do
270                 if ! wait ${pids_array[$n]}; then
271                         echo "${clients_array[$n]}: can not stop stats collect"
272                 fi
273         done
274 }
275
276 gather_start() {
277         local -a pids_array
278         local -a clients_array
279         local n=0
280         
281         #check whether the collect scripts already start in some targets 
282         if ! Check_nodes_are_clean ; then
283                 error "$SCRIPT already running in some targets, please cleanup first"
284         fi
285         
286         for TARGET in $TARGETS; do
287                 (start_target_script ${TARGET}) &
288                 pids_array[$n]=$!
289                 clients_array[$n]=$TARGET
290                 let n=$n+1
291         done
292         local num_pids=$n
293
294         local RC=0      
295         #Waiting log fetch finished
296         for ((n=0; $n < $num_pids; n++)); do
297                 if ! wait ${pids_array[$n]}; then
298                         echo "${clients_array[$n]}: can not start stats collect"
299                         let RC=$RC+1
300                 fi
301         done
302
303         if [ $RC != 0 ]; then
304                 stop_targets_script
305         fi
306 }
307
308 gather_stop() {
309         if Check_nodes_are_clean ; then
310                 exit 0
311         fi
312         log=$1
313
314         if [ -n "$log" ]; then
315                 fetch_log $log
316         fi
317         stop_targets_script
318 }
319
320 get_end_line_num()
321 {
322         local log_name=$1
323
324         ln=`grep -n snapshot_time ${log_name}  | awk -F":" '{ln=$1;} END{print ln;}'`
325         total_ln=`wc ${log_name} | awk '{print $1}'`                    
326
327         local endlen=$((${total_ln} - ${ln}))
328         echo $endlen
329 }
330
331 get_csv()
332 {
333         local logdir=$1
334         local statf=$2
335
336         local statf_name=`basename ${statf}`
337         type_name=`echo ${statf_name} | awk -F "." '{print $3}'`
338         stat_name=`head -n 1 ${statf} | awk '{print $4}'`
339         stat_type=`head -n 1 ${statf} | awk '{print $1}'`
340
341         #currently, it can only analyse client application log
342         if [ "$stat_type" != "client" ]; then
343                 error "can not analyse ${statf} ......."
344         fi
345
346         #create the header
347         echo "${node_name}_${type_name}, ${stat_name}" \
348                         >> $logdir/analyse_${type_name}.csv
349
350         #get total stats collection
351         end_len=`get_end_line_num ${statf}`
352         if [ $end_len != 1 -a $end_len != 0 ]; then
353                 if [ "$type_name" != "osc-rpc_stats" ]; then
354                         tail -n $end_len ${statf} | awk '{print $1 "," $2}' \
355                                 >> $logdir/analyse_${type_name}.csv
356                 else
357                         tail -n $end_len ${statf} |                     \
358                         awk  '/^[[:digit:]]/{print $1","$2","$6}        \
359                               /^page/{print "page per rpc,read,write"}  \
360                               /^rpcs/{print "rpcs,read,write"}          \
361                               /^offset/{print "offset, read,write"}'    \
362                         >> $logdir/analyse_${type_name}.csv
363                 fi
364         fi
365 }
366
367 gather_analyse()
368 {
369         local log_tarball=$1
370         local option=$2
371
372         #validating option
373         if [ -z "$log_tarball" -o -r "$option" ]; then
374                 usage;
375         fi
376
377         if [ ! -r $log_tarball ]; then
378                 error " not exist $log_tarball "
379         fi
380
381         shift
382
383         local date=`date +%F-%H-%M`
384         local logdir="analyse-${date}" 
385
386         mkdir -p ${TMP}/${logdir}
387         mkdir -p ${TMP}/${logdir}/tmp
388
389         $UNTAR $log_tarball -C ${TMP}/${logdir}/tmp 1>/dev/null 2>&1
390         for log_file in `find $TMP/$logdir/tmp`; do
391                 if test -f $log_file; then
392                         #get the node name
393                         local file_name=`basename ${log_file}`
394                         node_name=`echo ${file_name} | awk -F "-" '{print $2}'`
395                         echo "analysing the sublog ...$log_file"
396                         mkdir -p ${TMP}/${logdir}/${node_name}
397                         mkdir -p ${TMP}/${logdir}/${node_name}/tmp
398
399                         $UNTAR $log_file -C ${TMP}/${logdir}/${node_name}/tmp 1>/dev/null 2>&1
400                         for statf in `find ${TMP}/${logdir}/${node_name}/tmp`; do
401                                 if test -f $statf ; then
402                                         if [ "$option" == "csv" ]; then
403                                                 get_csv "$TMP/$logdir/${node_name}" "$statf"
404                                         fi
405                                 fi
406                         done
407                         rm -rf ${TMP}/${logdir}/${node_name}/tmp
408                 fi
409         done
410
411         rm -rf ${TMP}/${logdir}/tmp
412         $TAR ${TMP}/${logdir}.tar.gz ${TMP}/${logdir} 1>/dev/null 2>&1
413
414         echo "create analysed tarball ${TMP}/${logdir}.tar.gz"
415 }
416
417 case $OPTION in
418         start) gather_start ;;
419         stop)  gather_stop $@;;
420         analyse) gather_analyse $@;;
421         *) error "Unknown option ${OPTION}"
422 esac