这两天做了一个需求,因为上一个版本的/tmp空间默认只分配了5G,而升级程序上传解压路径也是/tmp,且解压过程要占用5G左右的空间,这样就导致/tmp空间占满,解压失败,升级无法进行。后来想了一个办法,就是从/var建了一个软链接到/tmp,这样实际上升级文件上传解压到/var路径下了,解决了/tmp路径空间不足的问题。但是因为客户那里是一个cluster环境,由6个node组成,最好能够在一个机器上执行一次补丁,所有node都生效。
经过几次尝试,写了两个shell脚本,一个run.sh负责调度和分发补丁,另外一个doFix.sh,真正给系统打补丁的脚本。这里只介绍调度分发的部分。
基本思路就是从主node上将doFix.sh脚本分发到其他各个node上,然后再从主node上ssh到各node执行补丁脚本。
调度脚本run.sh:
#!/bin/bash
#check version
cat /etc/issue | grep "V3.5"
if [ $? -ne 0 ]; then
echo "This is not V3.5, exit!"
exit
fi
ISFABRIC=0
ADMPW=""
echo
read -rsn1 -p "Is this a fabric with same admin password? [y/n]"
echo
if [ "$REPLY" = "y" -o "$REPLY" = "Y" ]; then
ISFABRIC=1
read -esp "Please input the password: " ADMPW
else
echo "Please run the script doFix.sh manually"
exit
fi
echo
echo
echo "Patch current node..."
sh ./doFix.sh
NODEIP=$(ifconfig eth0 | grep 'inet addr' | cut -d':' -f2 | awk '{print $1}')
echo "Begin to patch other nodes..."
echo
egrep "MAGICNODE-" /etc/hosts | awk '{print $1}' | sort | uniq | while read LINE
do
if [ "$NODEIP" = "$LINE" ]; then
continue
fi
echo "Patching node $LINE"
# scp doFix.sh to remote node
./scpExpect $ADMPW./doFix.sh admin@$LINE:/tmp/
if [ $? -ne 0 ]; then
echo "SCP script to node $LINE failed! skip"
continue
fi
# execute patch script
./sshExpectSudo admin $ADMPW $LINE "sudo bash /tmp/doFix.sh"
if [ $? -ne 0 ]; then
echo "Remote patch node $LINE failed! skip"
continue
fi
echo
echo
done
echo "Patch finished."
从/etc/hosts文件中获取cluster环境的所有node IP,然后将doFix.sh脚本通过scp送过去,再执行ssh打补丁。
这里两个关键点是scpExpect和sshExpect,何为Expect文件?网上有很多Expect的介绍文章,归结一句话就是一种实现自动化交互的脚本语言,从TCL扩展出来。
上代码:
scpExpect
#!/usr/bin/expect -f
# usage: scpExpect user password ip file target
system "rm -rf ~/.ssh/known_hosts"
set password [lindex $argv 0]
set from [lindex $argv 1]
set to [lindex $argv 2]
set timeout 120
# now connect to remote UNIX box (ipaddr) with given script to execute
spawn scp -r -o StrictHostKeyChecking=no $from $to
match_max 100000
expect {
"*assword:*" {
send -- "$password\r"
set timeout -1
expect {
"*assword:*" {
exit
}
}
} "yes/no)?" {
send "yes\r"
set timeout -1
} -re . {
exp_continue
} timeout {
exit
} eof {
exit
}
}
sshExpect
#!/usr/bin/expect -f
# usage: ssh user password ipaddre command target
# set Variables
set user [lrange $argv 0 0]
set password [lrange $argv 1 1]
set ipaddr [lrange $argv 2 2]
set command [lrange $argv 3 3]
set target [lrange $argv 4 4]
set timeout -1
# now connect to remote UNIX box (ipaddr) with given script to execute
spawn ssh -o StrictHostKeyChecking=no $user@$ipaddr $command $target
match_max 100000
# Look for passwod prompt
expect "*?assword:*"
# Send password aka $password
send -- "$password\r"
# send blank line (\r) to make sure we get back to gui
send -- "\r"
expect eof
感概一下:学习脚本切忌眼高手低,看着挺简单的东西,但是真正要自己写的时候发现步履维艰啊。shell脚本里面细节一不注意就容易犯错误,比如说if语句中方括号要注意两边的空格,还有"="两边的空格。另外一些细节的处理,shell很周到地为你考虑到了,比如输入密码时要防止回显,一个-s参数就搞定,所以有时候得多看看男人(man)。