Whamcloud - gitweb
Branch b1_4_mountconf
[fs/lustre-release.git] / lustre / utils / mass_config.sh
1 #!/bin/bash
2 #
3 # mass_config.sh - spreadsheet parsing for massive parallel config
4 #
5 ########################################################################
6
7 # Usage
8 usage() {
9         echo -e >&2 $"\nUsage: `basename $0` <csv file>"
10         cat >&2 <<EOF
11
12 Each line in the csv file represents one Lustre target.
13 The format of it is:
14 hostname,networks,device name,device type,fsname/poolname,mgmtnid,index,format options,mkfs options,mount options,failovers
15
16 Sample 1 for csv file:
17 lustre-mgs,options lnet networks=tcp,/r/tmp/mgmt,mgs,,,,--device_size 10240,-J size=4,,lustre-mgs@tcp0
18 lustre-ost,options lnet networks=tcp,/r/tmp/ost1,ost,lustre1,lustre-mgs@tcp0,0001,--device_size 10240,-J size=4,"extents,mballoc",lustre-mgs@tcp0
19 lustre-mdt,options lnet networks=tcp,/r/tmp/mdt1,mdt,lustre1,lustre-mgs@tcp0,0001,--device_size 10240,-J size=4,,lustre-mgs@tcp0
20
21 Sample 2 for csv file:
22 lustre-mgs,options lnet 'networks="tcp,elan"' \n options ost 'numthreads=23',/dev/sda,mgs,,,,,,,
23 lustre-ost,options lnet networks=tcp,/dev/sda,ost,,lustre-mgs@tcp0,,,,,
24 lustre-mdt,options lnet networks=tcp,/dev/sda,mdt,,lustre-mgs@tcp0,,,,,
25
26 EOF
27         exit 1
28 }
29
30 # Check argument
31 if [ $# -eq 0 ]; then
32         usage
33 fi
34
35 # Check the csv file
36 check_file() {
37         # Check argument
38         if [ $# -eq 0 ]; then
39                 echo >&2 $"check_file() error: Lack argument for function check_file()!"
40                 return 1
41         fi
42
43         CSV_FILE=$1
44         if [ ! -s ${CSV_FILE} ]; then
45                 echo >&2 $"check_file() error: ${CSV_FILE} does not exist or is empty!"
46                 return 1
47         fi
48
49         return 0
50 }
51
52 # Parse a line in the csv file
53 parse_line() {
54         # Check argument
55         if [ $# -eq 0 ]; then
56                 echo >&2 $"parse_line() error: Lack argument for function parse_line()!"
57                 return 1
58         fi
59
60         declare -i i=0
61         declare -i length=0 
62         declare -i idx=0
63         declare -i s_quote_flag=0 
64         declare -i d_quote_flag=0
65         local TMP_LETTER LINE
66  
67         LINE=$*
68
69         # Initialize the CONFIG_ITEM array
70         for ((i = 0; i < ${#CONFIG_ITEM[@]}; i++)); do
71                 CONFIG_ITEM[i]=$""
72         done
73
74         # Get the length of the line
75         length=${#LINE}
76
77         i=0
78         while [ ${idx} -lt ${length} ]; do
79                 # Get a letter from the line
80                 TMP_LETTER=${LINE:${idx}:1}
81
82                 case "${TMP_LETTER}" in
83                 ",")
84                         if [ ${s_quote_flag} -eq 1 ] || [ ${d_quote_flag} -eq 1 ]; then
85                                 CONFIG_ITEM[i]=${CONFIG_ITEM[i]}${TMP_LETTER}
86                         else
87                                 i=$i+1
88                         fi
89                         idx=${idx}+1
90                         continue
91                         ;;
92                 "'")
93                         if [ ${s_quote_flag} -eq 0 ]; then
94                                 s_quote_flag=1
95                         else
96                                 s_quote_flag=0
97                         fi
98                         ;;
99                 "\"")
100                         if [ ${d_quote_flag} -eq 0 ]; then
101                                 d_quote_flag=1
102                         else
103                                 d_quote_flag=0
104                         fi
105
106                         if [ ${i} -eq 1 ]; then
107                                 CONFIG_ITEM[i]=${CONFIG_ITEM[i]}$"\\"${TMP_LETTER}
108                                 idx=${idx}+1
109                                 continue
110                         fi
111                         ;;
112                 "\r")
113                         idx=${idx}+1
114                         continue
115                         ;;
116                 *)
117                         ;;
118                 esac
119                 CONFIG_ITEM[i]=${CONFIG_ITEM[i]}${TMP_LETTER}
120                 idx=${idx}+1
121         done
122         return 0
123 }
124
125 # Check the elements required for OSTs, MDTs and MGS
126 #
127 # When formatting an OST, the following elements: hostname, networks,
128 # device name, device type and mgmtnid, cannot have null value.
129 #
130 # When formatting an MDT or MGS, the following elements: hostname,
131 # networks, device name and device type, cannot have null value.
132 check_element() {
133         # Check hostname, networks, device name and device type
134         if [ -z "${HOST_NAME}" ]||[ -z "${NETWORKS}" ]||[ -z "${DEVICE_NAME}" ]\
135            ||[ -z "${DEVICE_TYPE}" ]; then
136                 echo >&2 $"check_element() error: Some required element has null value!"
137                 echo >&2 $"check_element() info:  Check hostname, networks, device name and device type!"
138                 return 1
139         fi
140
141         # Check mgmtnid
142         if [ "${DEVICE_TYPE}" == "ost" ]&&[ -z "${MGMT_NID}" ]; then
143                 echo >&2 $"check_element() error: OST's mgmtnid element has null value!"
144                 return 1
145         fi
146
147         return 0
148 }
149
150 # Check the number of MGS.
151 # There should be no more than one MGS specified in the entire csv file.
152 check_mgs() {
153         # Check the number of explicit MGS
154         if [ "${DEVICE_TYPE#*mgs*}" != "${DEVICE_TYPE}" ]; then 
155                 ex_mgs_count=${ex_mgs_count}+1
156         fi
157
158         if [ ${ex_mgs_count} -gt 1 ]; then
159                 echo >&2 $"check_mgs() error: More than one explicit MGS in the csv file!"
160                 return 1
161         fi
162
163         # Check the number of implicit MGS
164         if [ "${DEVICE_TYPE}" == "mdt" ]&&[ -z "${MGMT_NID}" ]; then
165                 im_mgs_count=${im_mgs_count}+1
166         fi
167
168         if [ `expr ${im_mgs_count} + ${ex_mgs_count}` -gt 1 ]; then
169                 echo >&2 $"check_mgs() error: More than one MGS in the csv file!"
170                 return 1
171         fi
172         
173         return 0
174 }
175
176 # Construct the command line of mkfs.lustre
177 construct_mkfs_cmdline() {
178         MKFS_CMD=$"mkfs.lustre "
179
180         case "${DEVICE_TYPE}" in
181         "ost")
182                 MKFS_CMD=${MKFS_CMD}$"--ost "
183                 ;;
184         "mdt")
185                 MKFS_CMD=${MKFS_CMD}$"--mdt "
186                 ;;
187         "mgs")
188                 MKFS_CMD=${MKFS_CMD}$"--mgmt "
189                 ;;
190         "mdt|mgs")
191                 MKFS_CMD=${MKFS_CMD}$"--mdt --mgmt "
192                 ;;
193         "mgs|mdt")
194                 MKFS_CMD=${MKFS_CMD}$"--mdt --mgmt "
195                 ;;
196         *)
197                 echo >&2 $"construct_mkfs_cmdline() error: Invalid device type - \"${DEVICE_TYPE}\""
198                 return 1
199                 ;;
200         esac
201
202         if [ -n "${FS_NAME}" ]; then
203                 MKFS_CMD=${MKFS_CMD}$"--fsname="${FS_NAME}$" "
204         fi
205
206         if [ -n "${MGMT_NID}" ]; then
207                 MKFS_CMD=${MKFS_CMD}$"--mgmtnid="${MGMT_NID}$" "
208         fi
209
210         if [ -n "${INDEX}" ]; then
211                 MKFS_CMD=${MKFS_CMD}$"--index="${INDEX}$" "
212         fi
213
214         if [ -n "${FORMAT_OPTIONS}" ]; then
215                 MKFS_CMD=${MKFS_CMD}${FORMAT_OPTIONS}$" "
216         fi
217
218         if [ -n "${MKFS_OPTIONS}" ]; then
219                 MKFS_OPTIONS=`echo "${MKFS_OPTIONS}" | sed 's/^"//' | sed 's/"$//'`
220                 MKFS_CMD=${MKFS_CMD}$"--mkfsoptions="$"\""${MKFS_OPTIONS}$"\""$" "
221         fi
222
223         if [ -n "${MOUNT_OPTIONS}" ]; then
224                 MOUNT_OPTIONS=`echo "${MOUNT_OPTIONS}" | sed 's/^"//' | sed 's/"$//'`
225                 MKFS_CMD=${MKFS_CMD}$"--mountfsoptions="$"\""${MOUNT_OPTIONS}$"\""$" "
226         fi
227
228         if [ -n "${FAILOVERS}" ]; then
229                 MKFS_CMD=${MKFS_CMD}$"--failover="${FAILOVERS}$" "
230         fi
231
232         MKFS_CMD=${MKFS_CMD}${DEVICE_NAME}
233         return 0
234
235
236 # Execute pdsh commands to add lnet options lines to remote nodes'
237 # modprobe.conf/modules.conf and format(mkfs.lustre) Lustre targets
238 mass_config() {
239         # Check argument
240         if [ $# -eq 0 ]; then
241                 echo >&2 $"mass_config() error: Lack argument for function mass_config()!"
242                 return 1
243         fi
244
245         CSV_FILE=$1
246         local LINE COMMAND
247         declare -a CONFIG_ITEM
248         declare -a PDSH_PID 
249         declare -a PDSH_CMD 
250         declare -i ex_mgs_count=0
251         declare -i im_mgs_count=0
252         declare -i line_num=1
253         declare -i pid_num=0
254
255         ADD_LNET_OPTIONS=$"/usr/bin/add_lnet_options.sh"
256
257         while read -r LINE; do
258                 # Get rid of the empty line
259                 if [ -z "`echo ${LINE} | awk '/[[:alnum:]]/{print $0}'`" ]; then
260                         line_num=${line_num}+1
261                         continue
262                 fi
263
264                 # Parse the config line into CONFIG_ITEM
265                 if ! parse_line $LINE; then
266                         return 1        
267                 fi
268
269                 HOST_NAME=${CONFIG_ITEM[0]}
270                 NETWORKS=${CONFIG_ITEM[1]}
271                 DEVICE_NAME=${CONFIG_ITEM[2]}
272                 DEVICE_TYPE=${CONFIG_ITEM[3]}
273                 FS_NAME=${CONFIG_ITEM[4]}
274                 MGMT_NID=${CONFIG_ITEM[5]}
275                 INDEX=${CONFIG_ITEM[6]}
276                 FORMAT_OPTIONS=${CONFIG_ITEM[7]}
277                 MKFS_OPTIONS=${CONFIG_ITEM[8]}
278                 MOUNT_OPTIONS=${CONFIG_ITEM[9]}
279                 FAILOVERS=${CONFIG_ITEM[10]}
280
281                 # Check some required elements
282                 if ! check_element; then
283                         echo >&2 $"check_element() error: Occurred on line ${line_num}."
284                         return 1        
285                 fi
286                 
287                 # Check the number of MGS
288                 if ! check_mgs; then
289                         echo >&2 $"check_mgs() error: Occurred on line ${line_num}."
290                         return 1
291                 fi
292
293                 # Execute pdsh command to add lnet options lines to modprobe.conf/modules.conf
294                 COMMAND=$"echo \"${NETWORKS}\"|${ADD_LNET_OPTIONS}"
295                 pdsh -w ${HOST_NAME} ${COMMAND} >&2 &
296                 PDSH_PID[${pid_num}]=$!
297                 PDSH_CMD[${pid_num}]="pdsh -w ${HOST_NAME} ${COMMAND}"
298                 pid_num=${pid_num}+1
299
300                 # Construct the command line of mkfs.lustre
301                 if ! construct_mkfs_cmdline; then
302                         echo >&2 $"construct_mkfs_cmdline() error: Occurred on line ${line_num}."
303                         return 1        
304                 fi
305
306                 # Execute pdsh command to format Lustre target
307                 pdsh -w ${HOST_NAME} ${MKFS_CMD} >&2 &  
308                 PDSH_PID[${pid_num}]=$!
309                 PDSH_CMD[${pid_num}]="pdsh -w ${HOST_NAME} ${MKFS_CMD}"
310                 pid_num=${pid_num}+1
311
312                 line_num=${line_num}+1
313         done < ${CSV_FILE}
314
315         # Wait for the exit status of the background pdsh command
316         echo "Waiting......"
317         for ((pid_num = 0; pid_num < ${#PDSH_PID[@]}; pid_num++)); do
318                 wait ${PDSH_PID[${pid_num}]}
319                 if [ $? -ne 0 ]; then
320                         echo >&2 "mass_config() error: Fail to execute \"${PDSH_CMD[${pid_num}]}\"!"
321                 fi
322         done    
323
324         return 0
325 }
326
327 # Main flow
328 if ! check_file $1; then
329         exit 1  
330 fi
331
332 if ! mass_config ${CSV_FILE}; then
333         exit 1
334 fi
335 exit 0