FreeBSDの自動調整

こんにちは、ハブロフスク!





sh(bash)を使用してFreeBSDのインストールと設定を自動化した経験を共有したいと思います。 次のようなものでした。

入社後は、FreeBSDで複数のサーバーを上げる必要がありました。 2つ目と3つ目の軸に続いて、私の同僚(州内にIT専門家は2人しかいない)は、新しくインストールしたOSを構成するスクリプトを記述してこのプロセスを自動化することを考えました。 執筆の仕事は私の肩にかかった。 同僚が自動インストールの問題を取り上げました。これについては別の投稿で説明します。 それでは始めましょう!



すぐに説明します。スクリプトは自分専用に作成されたため、一部のコードは適切に設計されていないか、単に「完璧ではありません」。 しかし、それでも、それは機能します!

このスクリプトは、ローダーファイル、メインの実行可能ファイル、ライブラリファイルの3つのファイルで構成されています。 順番に始めましょう。



loader.sh


#!/bin/sh lib1='parser.sh' #CORE FILE lib2='parserlib.sh' #LIB FILE #Defining variables --begin-- FTPServer='ftp://10.10.10.50' #FTP server address FileName='tarball.tar.gz' #Default tarball name LongVersion=`uname -r` #Long name of current OS version ShortVersion=${LongVersion%%-*} #Calculatig short version of OS, like "8.3" (DON'T TOUCH THIS LINE, please!) LocalDirectory='/tmp/script/' #Temporary directory. You can manualy clean it later. If it's not exist - just relax, script will create it for you :-) DataDirectory='/var/parser-md5-list/' #Directory with datafiles LockFile='EditLocker.p' #Lock file. Needed to prevent changes in files by script after manual editing. EditLock="$DataDirectory$LockFile" #Absolute LockFile path forcer='-no-force' #Variable must be not empty in case if -f key was not specified #Defining variables --end-- usage (){ echo "Only acceptable options: -f to force rebuild without check -s to force execution without restart -d to delete locker file (needed if files was edited manyally or by system, but now you want to rebuild it) -v <version> to manually specify FreeBSD version. Please be sure to specify it correctly! Example: ./loader.sh -v 9.1 " } #Defining options --begin-- if [ $# -ge 1 ] then while getopts fsdv: opt; do case "$opt" in f) forcer='-f';; s) skip='-s';; d) DL='-d';; v) ShortVersion="$OPTARG";; [?]) usage; exit 1;; esac done fi #Defining options --end-- echo "FreeBSD configuration tool V 1.0" echo "Detected OS version: $LongVersion" echo "Applying tarball for version $ShortVersion" echo; echo "Downloading files..." if [ "$DL" == "-d" ] then touch $EditLock #If file does not exists - this line will create it (needed to avoid error message from rm) rm $EditLock #Remove Lock file fi if ! [ "$skip" == "-s" ] then if ! [ -e run.sh ] then fetch "$FTPServer/FreeBSD/loader.sh" touch run.sh echo "#!/bin/sh clear echo \"*********************************************** * Loader file was updated. Process restarted. * *********************************************** \" " >> run.sh echo "./loader.sh $@" >> run.sh chmod +x run.sh ./run.sh exit 1 else rm run.sh fi fi fetch "$FTPServer/FreeBSD/$lib1" fetch "$FTPServer/FreeBSD/$lib2" chmod +x $lib1 chmod +x $lib2 . $lib1 #Attaching core file to process if [ $? -ne 0 ] #Checking for errors then echo "ERROR! Library $lib1 not found!" #Core file does not exist. exit 1 fi LetItGo $FTPServer $FileName $ShortVersion $LocalDirectory $forcer $DataDirectory $EditLock
      
      







parser.sh


 #!/bin/sh LetItGo() #Body of script { lib1='parserlib.sh' #Lib file . $lib1 #Attaching lib file to process if [ $? -ne 0 ] #Checking for errors then echo "ERROR! Library $lib1 not found!" #Lib file does not exist. kill $$ exit 1 fi #Defining variables --begin-- server=$1 #FTP server address file=$2 #Default tarball name ver=$3 #Version of OS, like "8.3" LocalDir=$4 #Temporary directory. You can manually clean it later. If it's not exist - just relax, script will create it for you :-) DataDirectory=$6 #Directory with data files EditLock=$7 #This file needed to prevent overriding for manually edited files. #Defining variables --end-- cdOrCreate $DataDirectory #Creating data directory if not exists cdOrCreate $LocalDir #Enter temporary directory for file downloads dirchek=`pwd`/ if [ "$dirchek" == "$LocalDir" ] #Checking current directory then rm -rf * #If script successfully entered the temp directory - it will be cleaned else echo "$LocalDir is not accesible! Please check permissions!" kill $$ exit 1 fi fetch "$server/FreeBSD/$ver/$file" #Download tarball hshchk=$(md5 $file) HashCheck ${hshchk##* } $LocalDir $file $5 $DataDirectory cd $LocalDir echo "Extracting files" tar -zxf $file #Unpack it echo "DONE! " rm $file #Remove tarball echo "Tarball was removed from local server." touch $EditLock echo "Lockfile created: $EditLock" for f in $( find $LocalDir ); do #Proceed all files one by one if [ -f $f ] then #check file for manual changes NEWFILE=${f#$LocalDir} NEWFILE=/${NEWFILE#*/} HCK=$(md5 $f) HCK=${HCK##* } #TIMEEDIT="$NEWFILE `stat -f %Sm -t %Y%m%d%H%M%S "$NEWFILE"`" EDITC="$NEWFILE $HCK" CHECK=`grep "$EDITC" "$EditLock"` SIMPLECHECK=`grep "$NEWFILE" "$EditLock"` #You may add your own subtree in additional elif below (for example: immediately script execution, assigning permissions, etc) if [ "`expr "$f" : '.*\(/Merge/\)'`" == "/Merge/" ] #If file should be merged then TempPath=${f##*/Merge} #Cut filepath. Original location will remain echo; echo "Merge: $f --> $TempPath" if ! [ -f $TempPath ] #If original file exist then MoveToLocal $f Merge #Then just replace it by new one else MergeFiles $f $TempPath #Else - merge new file to the old one line by line sort -u $TempPath > $TempPath.tmp #Delete repeating lines if exists mv -f $TempPath.tmp $TempPath #Rewriting merged file by filtered unique data CleanEOL $TempPath #Cleaning empty lines fi echo "DONE!" elif [ "`expr "$f" : '.*\(/Replace/\)'`" == "/Replace/" ] #If file should be replaced then if [ "$EDITC" == "$CHECK" ] || [ "$SIMPLECHECK" = '' ] then echo; echo "Replace: $f --> ${f##*/Replace}" MoveToLocal $f Replace #Then just replace it echo "DONE!" echo "$EDITC" >> $EditLock sort -u $EditLock > $EditLock.tmp #Delete repeating lines if exists mv -f $EditLock.tmp $EditLock else echo; echo "File $NEWFILE was edited manually. Skipped. Use -d key to ignore it." fi elif [ "`expr "$f" : '.*\(/Scripts/\)'`" == "/Scripts/" ] #If tarball contains a scripts, which should have +x permissions then echo; echo "Replace script: $f --> ${f##*/Scripts}" MoveToLocal $f Scripts #Then replace it (scripts cannot be merged) chmod +x ${f##*/Scripts} #And give eXecution permissions echo "DONE!" else echo; echo "DON'T match. Cannot proceed $f. Skipping." #This message means there is another subtree in tarball. It should be removed or described here fi fi done echo; echo "====================================================================" echo; echo "Cleaning temporary files" cd $LocalDir dirchek=`pwd`/ if [ "$dirchek" == "$LocalDir" ] #Checking current directory then rm -rf * echo "Succesfully cleaned" else echo "Temporary files was NOT deleted!" fi echo "DONE!" echo " Tarball was successfully applied." echo "To re-apply it again - use force key (-f)." #Finished }
      
      







そして、実際には、関数:

parserlib.sh


 #!/bin/sh cdOrCreate() #Enter the directory. Create if it's not exist, then enter. Arguments: 1) Path to directory (alternate or absolute). { if ! [ -d $1 ] #If directory does not exists then mkdir -p "$1" #Then create it fi cd "$1" #Enter the directory } MoveToLocal() #Create path and move file there (or replace existing file). Arguments: 1) full filename with full filepath 2) Folder identifyer, without slashes. { TempPath=${1##*/$2} #Deleting folder identifier from path AbsolutePath=${TempPath%/*} #Completing absolute path cdOrCreate $AbsolutePath #See cdOrCreate() description cd ${1%/*}"/" #Entering directory with file for move mv ${1##*/} $AbsolutePath"/"${1##*/} #Move file to new (absolute path) location } MergeFiles() #Using for check each file from "Merge" subtree and replace lines, or add line to end of file if not exist (?). Files MUST BE in conf syntax. { cat $1 | while read line do lineName=${line%=*} #Calculating key name lineName="$lineName=" lineHashedName=${lineName##\#} #Calculating name if commented sed -i -e 's/^'$lineHashedName'.*/'$line'/g' $2 #Replace line with key (uncommented) sed -i -e 's/^#'$lineHashedName'.*/'$line'/g' $2 #Replace line with key (commented by one hash) echo "$line" >> $2 #Append key to the end of file (dublicates will be sorted). done } CleanEOL() #This function needed for delete ^M from end of replaced lines and delete every empty line. Arguments: 1) Filename with path. { mv $1 tempconfig.conf cat tempconfig.conf | tr -d '\r' > tempconfig.conf.1 #Deleting ^M grep '.' tempconfig.conf.1 > $1 #Deleting empty lines and move file to original location rm tempconfig.conf* #Deleting temporary files } HashCheck() #Checks MD5 of tarball. Arguments: 1) Filename 2) Path 3) Tarball name 4) Flag (force rebuild existing installation) 5) Data directory { cdOrCreate "/var/parser-md5-list/" #See cdOrCreate description fpath=$5 #Location of currently downloaded tarball pointcheck=$4 #Force flag. If equal to "-f" then check will be skipped if ! [ -f $fpath ] #If checkfile does not exists then touch $fpath #Then create it echo $(date) >> $fpath #And write date and time into it elif ! [ "$pointcheck" == '-f' ] #If file exists and force flag was not specified then cat $1 | while read line #Then read date and time from existing file do #Show message echo " ===========================================================" echo " This tarball was applied at $line " echo " Use -f (force) to ignore this warning and rebuild anyway " echo "=========================================================== " cd "$2" #Enter directory which contains currently downloaded tarball rm "$3" #And delete tarball kill $$ #Kill parent process and exit exit 1 done esle #If file exists and -f was specified rm $fpath #Delete existing file touch $fpath #And create a new one fi }
      
      







FTPサーバーで、次の設定でアーカイブを作成するスクリプトが実行されます。



 #!/bin/sh cdOrCreate() #Enter the directory. Create if it's not exist, then enter. Arguments: 1) Path to directory (alternate or absolute). { if ! [ -d $1 ] #If directory does not exists then mkdir "$1" #Then create it fi cd "$1" #Enter directory } cd /data/ftproot/FreeBSD/ cat list.txt | while read line do if [ "$1" == '-v' ] then echo " == Processing subtree for version $line ==" fi cdOrCreate $line rm tarball.tar.gz if [ "$1" == '-v' ] then tar -zcvf tarball.tar.gz * cd .. else tar -zcf tarball.tar.gz * cd .. fi done echo " DONE!" if ! [ "$1" == '-v' ] then echo "Use -v for detailed output." fi
      
      







アーカイブには、 list.txtにリストされているサブディレクトリにあるすべてのファイルが含まれます。 ファイルには、バージョン番号に対応する親ディレクトリの名前が1行に1つずつ含まれています。

アーカイブを解凍した後、スクリプトはマージおよび置換ブランチをチェックします。 1つ目は、構成ファイルでパラメーターが追加または置換され、必要に応じて行がコメント化またはコメント解除されます。 2番目については、通常のファイル置換が行われます。 変更されたファイルごとに、そのMD5は$ DataDirectory $ LockFileリストに保存されます。スクリプトを繰り返し実行した場合、不適切なMD5のファイルは変更されません。 これは、管理者が行った手動変更のロールバックを防ぐために行われました。

また、スクリプトの誤った変更を防ぐために、作成されたrun.shファイルを介して再起動機能が作成され、スクリプトが再起動されて削除されます。 原則として、この機能は簡単にカットできます。

スクリプトは、次のキーを(任意の順序で)受け入れます。

-fアーカイブの再利用のチェックをスキップします

-sは、スクリプトの再起動をスキップします

-dはロックファイルを削除します。 手動の変更をロールバックする必要がある

-v VER FreeBSDの強制バージョン

他のキーは使用関数を呼び出し、スクリプトは終了します。

アーカイブ内のサブディレクトリを処理するための独自のオプションを追加することもできます。 これを行うには、対応するコメントの下のelifブランチにあるparser.shファイルでそれらを記述する必要があります。

私のアーカイブの構造は次のとおりです。



マージ/ブート/loader.conf

/etc/rc.confをマージします

/ usr / local / etcを置き換えます

/ usr / local / etc / svnup.confを置き換えます

/ usr / share / skelを置き換えます

置換/ usr / share / skel / dot.cshrc

/etc/ntp.confを置き換えます

/etc/adduser.confを置き換えます

/etc/portsnap.confを置き換えます

/ root / .cshrcを置き換えます



ディレクトリ名の後に(マージと置換がある)、ファイルへの元のパスが保存されます。 ディレクトリの名前と削除される前のすべてのものは、対応するifブランチのコードによってファイルが処理されます。

スクリプトは、ネイティブ関数のみを使用して記述されています。 新しくインストールされたフレンチで動作します。

開始するには、loader.shファイルを取得し、実行権限(chmod + x loared.sh)を付与して、もちろん実行する必要があります。



私は建設的な批判、コメント、提案に喜んでいます。 ソリューションが完璧ではないことを完全に理解しており、それを改善させていただきます。



PS:金曜日の未完成の投稿について本当におaび申し上げます。 誤ってコピーを発行し、これに気付かなかった。



All Articles