@@ -16,6 +16,10 @@ HA_NETWORK_V6_ARRAY[1]="2001:db8:20::/64"
1616BEHAVE_CASE_DIR=" $( dirname $0 ) /features/"
1717BEHAVE_CASE_EXCLUDE=" sbd|ocfs2"
1818
19+ declare -a hanode_list_to_form_cluster
20+ declare -a hanode_list_new_members
21+ declare -a hanode_list_current_cluster
22+
1923read -r -d ' ' SSHD_CONFIG_AZURE << EOM
2024PermitRootLogin no
2125AuthorizedKeysFile .ssh/authorized_keys
@@ -103,6 +107,13 @@ is_number() {
103107 test ! -z " $num " && test " $num " -eq " $num " 2> /dev/null && test " $num " -gt 0 2> /dev/null
104108}
105109
110+ is_num_or_seq__c_style () {
111+ echo $1 | grep -q -e ' ^[0-9]\+$'
112+ [ $? -eq 0 ] && [ " $1 " -gt 0 ] && return 1
113+ echo $1 | grep -q -e ' ^[0-9]\+\.\.[0-9]\+$'
114+ [ $? -eq 0 ] && return 2
115+ return 0 # return 0 means error here
116+ }
106117
107118check_docker_env () {
108119 # check if docker available
@@ -126,7 +137,7 @@ check_docker_env() {
126137 for network in ${HA_NETWORK_ARRAY[@]} ; do
127138 docker network ls| grep -q " $network "
128139 if [ " $? " -eq 0 ]; then
129- fatal " HA specific network \" $network \" already exists"
140+ warning " HA specific network \" $network \" already exists"
130141 fi
131142 done
132143}
@@ -160,23 +171,33 @@ The container image is based on Tumbleweed with preinstalled packages of the clu
160171Users can make the code change under crmsh.git including test cases. This tool will pick up the code change and "make install" to all running containers.
161172
162173OPTIONS:
163- -h, --help Show this help message and exit
164- -l List existing functional test cases and exit
165- -n NUM Only setup a cluster with NUM nodes(containers)
174+ -h, --help Show this help message and exit
175+ -l List existing functional test cases and exit
176+ -n < NUM|X..Y> NUM of nodes(containers) from hanode1 to hanode $NUM , or hanode $X to hanode $Y
166177 -x Don't config corosync on containers(with -n option)
167178 -d Cleanup the cluster containers
168179 -u Create normal users, and Azure like ssh environment
169180 -q Create a qnetd node(with -n and -x option)
170181
171182EXAMPLES:
183+
172184To launch 2 nodes with the running cluster with the very basic corosync.conf
173185# crmsh.git/test/run-functional-tests -n 2
174186
187+ To grow more nodes with a bigger number than '2' in the above example, and skip nodes if exist
188+ # crmsh.git/test/run-functional-tests -n 5
189+
190+ To grow more nodes with the given sequence, even a leap, and skip nodes if exist
191+ # crmsh.git/test/run-functional-tests -n 10..12
192+
175193To launch 2 nodes without the cluster stack running to play with "crm cluster init/join"
176- # crmsh.git/run-functional-tests -n 2 -x
194+ # crmsh.git/test/run-functional-tests -n 2 -x
195+
196+ To grow more nodes without configure the cluster stack
197+ # crmsh.git/test/run-functional-tests -n 3..5 -x
177198
178199To launch 2 nodes without the cluster stack running, and a qnetd node(named 'qnetd-node')
179- # crmsh.git/run-functional-tests -n 2 -x -q
200+ # crmsh.git/test/ run-functional-tests -n 2 -x -q
180201
181202To list the existing test cases. Users could add his own new test cases.
182203# crmsh.git/test/run-functional-tests -l
@@ -233,8 +254,12 @@ deploy_ha_node() {
233254
234255 info " Deploying \" $node_name \" ..."
235256 docker run --restart always $docker_options $DOCKER_IMAGE & > /dev/null
257+ if [ $? -ne 0 ]; then
258+ warning Likely $node_name already exists.
259+ return
260+ fi
236261 for network in ${HA_NETWORK_ARRAY[@]} ; do
237- docker network connect $network $node_name
262+ docker network connect $network $node_name & > /dev/null
238263 done
239264
240265 if [ " $node_name " != " qnetd-node" ]; then
@@ -245,29 +270,29 @@ deploy_ha_node() {
245270 docker_exec $node_name " echo 'StrictHostKeyChecking no' >> /etc/ssh/ssh_config"
246271
247272 if [ " $node_name " != " qnetd-node" ]; then
248- docker cp $PROJECT_PATH $node_name :/opt/crmsh
249- info " Building crmsh on \" $node_name \" ..."
250- docker_exec $node_name " $make_cmd " 1> /dev/null || \
273+ docker cp $PROJECT_PATH $node_name :/opt/crmsh
274+ info " Building crmsh on \" $node_name \" ..."
275+ docker_exec $node_name " $make_cmd " 1> /dev/null || \
251276 fatal " Building failed on $node_name !"
252- docker_exec $node_name " chown hacluster:haclient -R /var/log/crmsh"
253- docker_exec $node_name " chmod g+w -R /var/log/crmsh"
254- create_alice_bob_carol
255- if [ " $NORMAL_USER_FLAG " -eq 1 ]; then
256- set_sshd_config_like_in_azure $node_name
257- fi
277+ docker_exec $node_name " chown hacluster:haclient -R /var/log/crmsh"
278+ docker_exec $node_name " chmod g+w -R /var/log/crmsh"
279+ create_alice_bob_carol
280+ if [ " $NORMAL_USER_FLAG " -eq 1 ]; then
281+ set_sshd_config_like_in_azure $node_name
282+ fi
258283 else
259- docker_exec $node_name " useradd -m -s /bin/bash alice 2>/dev/null"
260- docker_exec $node_name " echo \" alice ALL=(ALL) NOPASSWD:ALL\" > /etc/sudoers.d/alice"
261- docker_exec $node_name " cp -r /root/.ssh ~alice/ && chown alice:users -R ~alice/.ssh"
262- info " Create user 'alice' on $node_name "
263- [ " $NORMAL_USER_FLAG " -eq 1 ] && set_sshd_config_like_in_azure $node_name
284+ docker_exec $node_name " useradd -m -s /bin/bash alice 2>/dev/null"
285+ docker_exec $node_name " echo \" alice ALL=(ALL) NOPASSWD:ALL\" > /etc/sudoers.d/alice"
286+ docker_exec $node_name " cp -r /root/.ssh ~alice/ && chown alice:users -R ~alice/.ssh"
287+ info " Create user 'alice' on $node_name "
288+ [ " $NORMAL_USER_FLAG " -eq 1 ] && set_sshd_config_like_in_azure $node_name
264289 fi
265290}
266291
267292
268293create_node () {
269294 info " Loading docker image $DOCKER_IMAGE ..."
270- docker pull $DOCKER_IMAGE & > /dev/null
295+ docker pull $DOCKER_IMAGE & > /dev/null
271296
272297 for index in ${! HA_NETWORK_ARRAY[@]} ; do
273298 network=${HA_NETWORK_ARRAY[$index]}
@@ -281,40 +306,63 @@ create_node() {
281306 wait
282307}
283308
309+ get_cluster_new_nodes () {
310+ hanode_list_to_form_cluster=($( docker ps -a --format ' {{.Names}}' | grep hanode| sort -n -k1.7| tr ' \r' ' ' ) )
311+ hanode_list_current_cluster=($( docker_exec hanode1 " crm node server 2>/dev/null" 2> /dev/null| sort -n -k1.7| tr ' \r' ' ' ) )
312+ hanode_list_new_members=()
313+ for element in " ${hanode_list_to_form_cluster[@]} " ; do
314+ if ! [[ " ${hanode_list_current_cluster[@]} " =~ " $element " ]]; then
315+ hanode_list_new_members+=(" $element " )
316+ fi
317+ done
318+ # echo hanode_list_to_form_cluster= ${hanode_list_to_form_cluster[@]}
319+ # echo hanode_list_current_cluster = ${hanode_list_current_cluster[@]}
320+ # echo ${#hanode_list_new_members[@]} new nodes "'${hanode_list_new_members[@]}'"
321+ }
284322
285323config_cluster () {
286- node_num=$#
287- insert_str=" "
288- container_ip_array=(` docker network inspect $HA_NETWORK_ARRAY -f ' {{range .Containers}}{{printf "%s " .IPv4Address}}{{end}}' ` )
324+ get_cluster_new_nodes
325+ info ${# hanode_list_new_members[@]} new node\( s\) " '${hanode_list_new_members[@]} '"
289326
290- for i in $( seq $node_num -1 1) ; do
291- ip=` echo ${container_ip_array[$((i-1))]} | awk -F/ ' {print $1}' `
327+ if [ ${# hanode_list_new_members[@]} -eq 0 ]; then
328+ return
329+ fi
330+
331+ insert_str=" "
332+ for i in $( seq 1 ${# hanode_list_to_form_cluster[@]} ) ; do
333+ node=${hanode_list_to_form_cluster[$((i-1))]}
334+ ip=$( docker network inspect " $HA_NETWORK_ARRAY " --format ' {{range .Containers}}{{if eq .Name "' " ${node} " ' "}}{{.IPv4Address}}{{end}}{{end}}' | awk -F/ ' {print $1}' )
292335 insert_str+=" \\ n\\ tnode {\n\t\tring0_addr: $ip \n\t\tnodeid: $i \n\t}"
293336 done
337+
294338 corosync_conf_str=$( sed " /nodelist/a \\ ${insert_str} " <( echo " $COROSYNC_CONF_TEMPLATE " ) )
295- if [ $node_num -eq 2 ]; then
339+ docker_exec " hanode1" " echo \" $corosync_conf_str \" > $COROSYNC_CONF "
340+ if [ ${# hanode_list_to_form_cluster[@]} -eq 2 ]; then
296341 corosync_conf_str=$( sed " /corosync_votequorum/a \\\\ ttwo_node: 1" <( echo " $corosync_conf_str " ) )
297342 fi
298343
299- info " Copy corosync.conf to $* "
300- for node in $* ; do
301- if [ $node == $1 ]; then
302- docker_exec $1 " echo \" $corosync_conf_str \" >> $COROSYNC_CONF "
303- docker_exec $1 " corosync-keygen -l -k $COROSYNC_AUTH &> /dev/null"
344+ info " Copy corosync.conf to ${hanode_list_to_form_cluster[@]} "
345+ for node in ${hanode_list_to_form_cluster[@]} ; do
346+ if [ $node == " hanode1" ]; then
347+ docker_exec " hanode1" " corosync-keygen -l -k $COROSYNC_AUTH &> /dev/null"
304348 else
305349 while :
306350 do
307- docker_exec $1 " ssh -T -o Batchmode=yes $node true &> /dev/null" && break
351+ docker_exec " hanode1 " " ssh -T -o Batchmode=yes $node true &> /dev/null" && break
308352 sleep 1
309353 done
310- docker_exec $1 " scp -p $COROSYNC_CONF $COROSYNC_AUTH $node :/etc/corosync &> /dev/null"
354+ docker_exec " hanode1 " " scp -p $COROSYNC_CONF $COROSYNC_AUTH $node :/etc/corosync &> /dev/null"
311355 fi
312356 done
313357}
314358
315-
316359start_cluster () {
317- for node in $* ; do
360+ if [ ${# hanode_list_current_cluster[@]} -ne 0 ] && [ ${# hanode_list_new_members[@]} -ne 0 ]; then
361+ docker_exec hanode1 " corosync-cfgtool -R > /dev/null"
362+ info Cluster exists at ' hanode1' : Reloading corosync.conf... Done
363+ fi
364+
365+ for node in ${hanode_list_new_members[@]} ; do
318366 docker_exec $node " crm cluster enable && crm cluster start" 1> /dev/null
319367 if [ " $? " -eq 0 ]; then
320368 info " Cluster service started on \" $node \" "
@@ -324,35 +372,51 @@ start_cluster() {
324372 done
325373}
326374
327-
328- container_already_exists () {
329- docker ps -a| grep -q " $1 "
330- if [ " $? " -eq 0 ]; then
331- fatal " Container \" $1 \" already running"
332- fi
375+ is_container_existing () {
376+ docker ps -a --format ' {{.Names}}' | grep -q " ^$1 $"
333377}
334378
335-
336379setup_cluster () {
337- hanodes_arry=()
338- is_number $1
339- if [ " $? " -eq 0 ]; then
340- for i in $( seq 1 $1 ) ; do
341- hanodes_arry+=(" hanode$i " )
380+ get_cluster_new_nodes
381+
382+ hanodes_array=()
383+ is_num_or_seq__c_style $1
384+ ret=$?
385+ if [ $ret -eq 1 ]; then
386+ # add more nodes after the last node, ordered by the node name
387+ if [ ${# hanode_list_to_form_cluster[@]} -gt 0 ]; then
388+ last_node_num=" ${hanode_list_to_form_cluster[-1]: 6} "
389+ else
390+ last_node_num=0
391+ fi
392+ num_of_new_nodes=$(( $1 - ${# hanode_list_to_form_cluster[@]} ))
393+ if [ " $num_of_new_nodes " -gt 0 ]; then
394+ for i in $( seq $(( last_node_num + 1 )) $(( last_node_num + num_of_new_nodes )) ) ; do
395+ hanodes_array+=(" hanode$i " )
396+ done
397+ else
398+ info 0 new node
399+ return
400+ fi
401+ elif [ $ret -eq 2 ]; then
402+ is_container_existing " hanode1" || hanodes_array=(" hanode1" )
403+ from_to=($( echo $1 | tr " .." " " ) )
404+ for i in $( seq ${from_to[0]} ${from_to[1]} ) ; do
405+ hanodes_array+=(" hanode$i " )
342406 done
343407 else
344- hanodes_arry =($* )
408+ hanodes_array =($* )
345409 fi
346410
347411 if [ $WITH_QNETD_NODE -eq 1 ]; then
348- create_node ${hanodes_arry [@]} " qnetd-node"
412+ create_node ${hanodes_array [@]} " qnetd-node"
349413 else
350- create_node ${hanodes_arry [@]}
414+ create_node ${hanodes_array [@]}
351415 fi
352416
353417 [ " $CONFIG_COROSYNC_FLAG " -eq 0 ] && return
354- config_cluster ${hanodes_arry[@]}
355- start_cluster ${hanodes_arry[@]}
418+ config_cluster
419+ start_cluster
356420 docker_exec " hanode1" " crm configure property stonith-enabled=false"
357421}
358422
@@ -431,7 +495,7 @@ run_origin_regression_test() {
431495
432496prepare_coverage_env () {
433497 for node in $* ; do
434- docker exec -t $node /bin/sh -c ' sed -i ' \' ' 1a\import coverage\nimport atexit\ncov=coverage.Coverage(config_file="/opt/crmsh/test/features/coveragerc")\natexit.register(lambda:(cov.stop(),cov.save()))\ncov.start()' \' ' /usr/sbin/crm'
498+ docker exec -t $node /bin/sh -c ' sed -i ' \' ' 1a\import coverage\nimport atexit\ncov=coverage.Coverage(config_file="/opt/crmsh/test/features/coveragerc")\natexit.register(lambda:(cov.stop(),cov.save()))\ncov.start()' \' ' /usr/sbin/crm'
435499 done
436500}
437501
@@ -479,7 +543,7 @@ case $1 in
479543-n)
480544 check_docker_env
481545 shift
482- is_number $1 || fatal " -n option need a number larger than 0"
546+ is_num_or_seq__c_style $1 && fatal " -n option need a number larger than 0, or a sequence like 5..10 "
483547 SETUP_N_NODES_CLUSTER=$1
484548 shift
485549 ;;
@@ -490,7 +554,7 @@ case $1 in
490554esac
491555done
492556
493- if [ $SETUP_N_NODES_CLUSTER -ge 1 ]; then
557+ if [ " $SETUP_N_NODES_CLUSTER " != " 0 " ]; then
494558 setup_cluster $SETUP_N_NODES_CLUSTER
495559 exit $?
496560fi
@@ -538,13 +602,13 @@ for case_num in $*;do
538602 setup_cluster ${node_arry[@]}
539603 adjust_test_case ${node_arry[0]} $case_file_in_container
540604 echo
541- prepare_coverage_env " ${node_arry[@]} "
605+ prepare_coverage_env " ${node_arry[@]} "
542606 if [ " $NORMAL_USER_FLAG " -eq 0 ]; then
543607 info " Running \" $case_file_in_container \" under 'root'..."
544608 docker_exec ${node_arry[0]} " behave --no-logcapture $case_file_in_container || exit 1" || exit 1
545609 else
546610 info " Running \" $case_file_in_container \" under normal user 'alice'..."
547- docker_exec ${node_arry[0]} " su - alice -c 'sudo behave --no-logcapture $case_file_in_container || exit 1'" || exit 1
611+ docker_exec ${node_arry[0]} " su - alice -c 'sudo behave --no-logcapture $case_file_in_container || exit 1'" || exit 1
548612 fi
549613 fetch_coverage_report " ${node_arry[@]} "
550614 echo
0 commit comments