#!/bin/bash # #!/bin/sh -e # all_args="$@" vrbase="/root" vcopylog=/tmp/vcopy.log echo "`date +%T` TRACE: $0 $*" >> $vcopylog if [ "$UID" != "0" ] then # Sudo to ROOT if [ "$1" = "--sudoprobe" ] then echo "SUDO to root on '`hostname --fqdn`':" fi sudo $0 ----$USER "$@" exit $? else if [ "$1" = "--sudoprobe" ] then echo "Don't need SUDO on '`hostname --fqdn`':" exit 99 fi fi if [ "${1:0:4}" = "----" ] then # INTERNAL: Have sudo self! olduser="${1:4}" shift if [ "$olduser" != "$SUDO_USER" ] then echo "ERROR: user mismach olduser:'$olduser' SUDO_USER:'$SUDO_USER'" exit 255 fi if [ "$1" = "--sudoprobe" ] then exit 0 fi fi if [ "$1" = "--fake-rsh" ] then # INTERNAL: Called to fake rsh shift 2 $* exit $? fi # Defaults - can be changes with: --var="new value", wher var is optionname # without opt_, ie: --ssh=/usr/local/bin/ssh sets witch ssh to use! # Show help and exit opt_help="0" # Major usecase # By default make a backup copy - don't start/activate target. # migrate: Stop/deaktivate source before and start/activate target after copy. opt_migrate="0" # clone: Activate and start target after copy. opt_clone="0" # Source option, if non given, refuse to copy running source. # coldcopy: Stop source before and start after copy. Impied by migrate. opt_coldcopy="0" # hotcopy: Copy a running guest, not recomended. opt_hotcopy="0" # Target options... if non given only accept new targets. # force: Copy over existent target as long not active or running. opt_force="0" # coa: CopyOwerActive, exclude /var/log from copy. Leave target active. opt_coa="0" # sst: SartStopTarget, implies coa. opt_sst="0" # cor: CopyOwerRunning, exclude everything from copy, include by hand. opt_cor="0" # Options to config target # mark: Set mark on guest, determing its startup group. opt_mark="0" # cid: The guests context id, can only be set on a new target opt_cid="0" # ip: IP-number of guests primary interface opt_ip="0" # dev: Device of guests primary interface (typicaly eth0) opt_dev="0" # alias: Alias on guests primary interface, default to vserver name. opt_alias="1" # hostname: The guests hostname, default to vserver name. opt_hostname="1" # Misc # How many precopys to do before coldcopy to speed up opt_precopy=1 # Dry run, dont copy - but print what would be done! opt_dry="0" # Where to find software and config. # Where the vcopy script lives, on remote side. opt_vcopy="vcopy" # Command to use for remote shell. # -M, -O check, -O exit, -S path #opt_ssh="ssh -t -e none" opt_ssh="ssh -T" # Don't su back to normal user before ssh opt_sshroot="0" # Don't build a ssh MASTER conection to first remote host. opt_nomast="0" # Wher rsync lives. opt_rsync="rsync" # Wher rsync lives, on remote side. opt_rrsync="0" # Vrescue script, only used on remote side. vrescue="vrescue" opt_vrescue="0" # Copy config to! opt_cpconf="1" # Where is VServer config directory opt_confdir="/etc/vservers" # Where is VServer config directory, remote side. opt_rconfdir="0" # Where is VServer run (lock) directory, only used on remote side rundir="/var/run/vservers" opt_rundir="0" # INTERNAL: Remote kommand, used togheter with following kommand opt_remote="0" # INTERNAL: Stat a (remote) deamon opt_stat="0" # INTERNAL: Set up a virtual rescuedisk, set to a uniqe rescudisk id. #opt_build="0" # INTERNAL: Start rsync deamon in right context, use same rescudisk id. opt_deamon="0" # INTERNAL: Start rsync deamon on config in right context, use same # rescudisk id. opt_confdeamon="0" # INTERNAL: Clean up and remove the rescudisk whith rescudisk id. opt_cleanup="0" # INTERNAL: Activate remote server, argument to cleanup. opt_activate="0" # END of remote commands # Read in parameters to the opt_ vars parm='source'; # Init positional parameters p=$1 shift while [ "$p" != "--" ] && [ "x$p" != "x" ] do if [ "${p:0:2}" = "--" ] then # Named parameters p=${p:2} k=${p%%=*} if [ "$k" = "$p" ] then # No asignment if [ "${k:0:1}" = "!" ] then # 'not', set false ("0") eval "opt_${k:1}=\"0\"" else # set true ("1") eval "opt_$k=\"1\"" fi else # Asignment eval "opt_$k=\"${p##*=}\"" fi else # Posisional Parameters if [ "$parm" = "source" ] then opt_source="$p" parm='target' elif [ "$parm" = "target" ] then opt_target="$p" parm='done' else echo "ERROR To many positional parameters '$p'" exit 1 fi fi p=$1 shift done if [ "$opt_help" != "0" ] then echo $" $0 Script to copy a VServer. Usage: $0 source|/ [[user@]host[/sock]:]*target [options] [-- rsync options] $0 source|/ [user@]host[/sock]:* [options] [-- rsync options] $0 --help Copy a VServer guest. Only target can be remote. Ther may be multiple host descriptions to alow for an ssh path thru ssh-gateways to local VServer hosts. Each host discription may include a ssh Master socket to speed things up and allow for copy over conections that require password auth. If no socket is given for the first hostdescription, a Master conection will be created. Can be run as normal user and will run ssh as that user. If not root, will atempt to sudo to root on both source and target machine to do copy and VServer magic. Xid/cid taging will hopefully be right on the fly. if source is / it will copy the fysical source machine to the target VServer, imply --skel and --hotcopy. / as source is *not* implemented yet. options (use --foo=0 turn an option off). Options folowing -- is passed on to rsync, but only for copy of the VServer image, not the config. Major usecase, will by default make a backup copy without starting/activating target. --migrate stop/deactivate source and start/activate target. --clone start/activate target, leave source as is. Source options, by default refuse to copy a runing source. --coldcopy stop source before and start it after copy. Implied by --migrate. --hotcopy copy a runing VServer guest - *Not* recomended. Target options, by default only alowe new targets. In every case will only image be overwriten of an existing target! * = not implemented yet. --force Write over existing target if not runing or active. *--coa CopyOverActive - exclude /var/log *--sst StopStartTarget, implies --coa *--cor CopyOverRunning, exclude everyting. Path You want to copy to a running VServer must be included in rsync options. Options to configure target on the fly. --skel Don't copy config - build new skeleton config. *Only* usefull on new target. *Not implemented yet*. --mark Set mark on target, determing it's startup group. --cid Set the targets context# - can *only* be set on a new target. --ip Set ip number of targets primary interface. --dev Set device of targets primary interface --alias Set alias name if targets primary interface. If alias is given without value use the target name (default on for new target). --hostname Set the hostname of target. If hostname is given without value use the target name (default on). Misc options --ssh Path and possably options to the ssh implementation to use. By default use 'ssh -t -e none' to improve sudo pw auth. --sshroot Don't su back to normal user before runing ssh. --nomast Don't build a ssh Master conection. Needed with old ssh. --vcopy Path to vcopy script on remote side --rsync Path to rsync on lokal side --rrsync Path to rsync on remote side --vrescue Path to vrescue on remote side --confdir Path to VServer confdir, lokal side. --rconfdir Path to VServer confdir, remote side. --rundir VServer run (lock) directory, only used on remote side --dry Dry run, dont do any, but print what would be done. --precopy Howe many hotcopys to run before coldcopy/migrate. Default 1. Notes: Try to probe for sudo access befor every real action, to avode that sudo pw querys interfer with operation and to give the user a clue to what pw to type. vcopy need vrescue on the target side to operate. Example: To migrate VServer 'www' to machine 'vhost1' thru 'ssh-gw1' with ssh Master conection do: sourcehost $ ssh -S foo -M ssh-gw1 password: ssh-gw1 $ ssh -S /tmp/foo -M vhost1 password: vhost1 $ in another *term: sourcehost $ vcopy --migrate www ssh-gw1/foo:vhost1//tmp/foo: If You don't run as root You might be querred for pw by sudo. BUGS! *Not* well tested. Talk to much and not feature complet - don't hold Your breath. Shuld not use stdout for errors. No licens, but I smack on GPL if anyone ask. Just hardlink and hashify on the fly when doing lokal copy would be a nice improvment. Report other flaws to lah att micropp dot se " exit 0 fi vparam="" if [ "$opt_rrsync" != "0" ] then vparam="$vparam --rsync=$opt_rrsync" fi if [ "$opt_vrescue" != "0" ] then vparam="$vparam --vrescue=$opt_vrescue" fi if [ "$opt_rconfdir" != "0" ] then vparam="$vparam --confdir=$opt_rconfdir" fi if [ "$opt_rundir" != "0" ] then vparam="$vparam --rundir=$opt_rundir" fi if [ "$opt_dry" != "0" ] then vparam="$vparam --dry=$opt_dry" fi closessh() { for c in "${close_ssh[@]}" do echo "CLOSE SSH: $c" $c done } if [ "$opt_remote" = "0" ] then sourcename=$opt_source if [ "$opt_target" = "${opt_target%:*}" ] then # Target is local targethost="localhost" targetname="$opt_target" vssh="$0 --fake-rsh" sudoprobe="" vremote="$opt_vcopy --remote --target=$targetname $vparam" vstat="$vremote --stat" else trap closessh EXIT targethost="${opt_target%:*}" targetname="${opt_target##*:}" if [ "x$targetname" = "x" ] then targetname=$sourcename fi vssh="$opt_ssh" if [ "x$olduser" != "x" ] && [ "$opt_sshroot" = "0" ] then vssh="su $olduser -- $vssh" fi sshi=1000 if [ "$targethost" != "${targethost%%:*}" ] then thost=${targethost%%:*} tnhost=${targethost#*:} else thost=$targethost tnhost="" fi while [ "x$thost" != "x" ] do tsock="" if [ "$thost" != "${thost%%/*}" ] then tsock="${thost#*/}" thost=${thost%%/*} if [ "$sshi" = "1000" ] && [ "${tsock:0:1}" != "/" ] then tsock="-S $PWD/$tsock" else tsock="-S $tsock" fi elif [ "$sshi" = "1000" ] && [ "$opt_nomast" = "0" ] then echo "BUILD SHH MASTER CONECTION TO '$thost'" tsock="-S $PWD/_vcopy_$$" echo "$vssh -N -f $tsock -M $thost" $vssh -N -f $tsock -M $thost close_ssh[$sshi]="$vssh $tsock -O exit $thost" fi if [ "x$tnhost" != "x" ] then vssh="$vssh $tsock $thost $opt_ssh" else targethost=$thost vssh="$vssh $tsock" fi sshi=$(($sshi - 1)) if [ "$tnhost" != "${tnhost%%:*}" ] then thost=${tnhost%%:*} tnhost=${tnhost#*:} else thost=$tnhost tnhost="" fi done vremote="$opt_vcopy --remote --target=$targetname $vparam" sudoprobe="$vssh $targethost $opt_vcopy --sudoprobe" vstat="$vssh $targethost $vremote --stat" fi else if [ "$opt_rundir" != "0" ] then rundir=$opt_rundir fi if [ "$opt_vrescue" != "0" ] then vrescue=$opt_vrescue fi fi if [ "$opt_stat" != "0" ] then echo "*******STAT on '$opt_target' on '`hostname --fqdn`'*******" if [ "A$opt_target" = "A" ] then echo "No target specified" exit 100 fi if [ -d $opt_confdir/$opt_target ] then if vserver $opt_target running then echo "Target '$opt_target' run" exit 101 fi if [ -f $opt_confdir/$opt_target/apps/init/mark ] then mark="`cat $opt_confdir/$opt_target/apps/init/mark`" if [ "x$mark" != "x" ] then echo "Target '$opt_target' have mark '$mark'" exit 102 fi fi echo "Target '$opt_target' exist." exit 2 else echo "New target: '$opt_target'." exit 1 fi exit 0 fi build_vrescue() { echo "`date +%T` TRACE: build_vrescue $*" >> $vcopylog cdir=$opt_confdir/$opt_target if [ ! -d $cdir ] then mkdir $cdir vdir=$opt_confdir/.defaults/vdirbase/$opt_target mkdir $vdir run=$rundir/$opt_target ln -snf $vdir $cdir/vdir ln -snf $run $cdir/run fi $vrescue --build="$1" $opt_target } if [ "$opt_confdeamon" != "0" ] then dir="vcopy-$opt_confdeamon-$olduser" if [ ! -d $vrbase/$dir ] then echo "`date +%T` TRACE: confdeamon: build $dir" >> $vcopylog build_vrescue $dir fi cdir=$opt_confdir/$opt_target vdir=`ls -l $cdir/vdir | awk '{ print $NF }'` run=`ls -l $cdir/run | awk '{ print $NF }'` mkdir $vrbase/$dir/r/conf mount --bind $cdir/. $vrbase/$dir/r/conf echo $"#!/bin/bash cd /conf $opt_rsync $@ " > $vrbase/$dir/r/confrsync chmod a+x $vrbase/$dir/r/confrsync echo "`date +%T` TRACE:chroot $vrbase/$dir/r /confrsync" "$@" >> $vcopylog chroot $vrbase/$dir/r /confrsync "$@" ret=$? rm $vrbase/$dir/r/confrsync ln -snf $vdir $cdir/vdir ln -snf $run $cdir/run echo $opt_target > $cdir/name if [ -f $cdir/mark ] then mv $cdir/mark $cdir/mark.dac fi if [ "$opt_cid" != "0" ] then echo $opt_cid > $cdir/context fi umount $vrbase/$dir/r/conf rmdir $vrbase/$dir/r/conf exit $ret fi if [ "$opt_deamon" != "0" ] then dir="vcopy-$opt_deamon-$olduser" if [ ! -d $vrbase/$dir ] then echo "`date +%T` TRACE: deamon: build $dir" >> $vcopylog build_vrescue $dir fi cdir=$opt_confdir/$opt_target if [ "$opt_ip" != "0" ] then echo $opt_ip > $cdir/interfaces/0/ip fi if [ "$opt_dev" != "0" ] then echo $opt_dev > $cdir/interfaces/0/dev fi if [ "$opt_alias" != "0" ] then if [ "$opt_alias" = "1" ] then opt_alias=$opt_target fi echo $opt_alias > $cdir/interfaces/0/name fi if [ "$opt_hostname" != "0" ] then if [ "$opt_hostname" = "1" ] then opt_hostname=$opt_target fi echo $opt_hostname > $cdir/uts/nodename fi if [ "$opt_mark" != "0" ] then echo $opt_mark > $cdir/mark.dac fi echo $"#!/bin/bash cd /target $opt_rsync $@ " > $vrbase/$dir/r/targetrsync chmod a+x $vrbase/$dir/r/targetrsync $vrescue --dir=$dir $opt_target /targetrsync "$@" rm $vrbase/$dir/r/targetrsync exit $? fi if [ "$opt_cleanup" != "0" ] then cdir=$opt_confdir/$opt_target echo "****cleanup: '$opt_cleanup', activate: '$opt_activate'****" echo "target: $opt_target cdir: $cdir" if [ "$opt_activate" != "0" ] then if [ "$opt_dry" = "0" ] then vserver -s $opt_target start ret=$? echo "******* vserver -s $opt_target start = $ret *******" if [ "$ret" = "0" ] && [ -f $cdir/mark.dac ] then mv $cdir/mark.dac $cdir/mark fi $vrescue --cleanup="vcopy-${opt_cleanup}-$olduser" else echo "DRYRUN(remote): vserver -s $opt_target start" echo "DRYRUN(remote): mv $cdir/mark.dac $cdir/mark" echo "DRYRUN(remote): \ $vrescue --cleanup=\"vcopy-${opt_cleanup}-$olduser\"" exit 0 fi else if [ "$opt_dry" = "0" ] then $vrescue --cleanup="vcopy-${opt_cleanup}-$olduser" ret=$? else echo "DRYRUN(remote): \ $vrescue --cleanup=\"vcopy-${opt_cleanup}-$olduser\"" exit 0 fi fi exit $ret fi echo "FROM : '$opt_source'" echo "TO : '$opt_target'" echo "IP : '$opt_ip'" echo "FORCE : '$opt_force'" echo "Copy VServer guest '$opt_source' to guest '$opt_target'" echo "sourcename: '$sourcename'" echo "targethost: '$targethost'" echo "targetname: '$targetname'" echo "vssh: '$vssh'" echo "vremote: '$vremote'" echo "vstat: '$vstat'" echo "sudoprobe: '$sudoprobe'" if [ "x$sudoprobe" != "x" ] then $sudoprobe ret=$? case $ret in 0) #OK echo "Remote sudo OK" ;; 99) # No sudo needed echo "No sudo needed on remote side" sudoprobe="" ;; *) # ? echo "ERROR: Unknowed return on sudoprobe ($ret)" exit $ret ;; esac fi $vstat ret=$? case $ret in 1) # Target is new echo "Target is new!" ;; 2) # Target exist if [ "$opt_force" = "1" ] then echo "Target exist, force given, only copy image (not conf)!" opt_cpconf="0" else echo "Target exist, --force must be given to overwrite target image!" exit 1 fi ;; 100) # ERROR: No target given echo "ERROR: No target specified!" exit 100 ;; 101) # ERROR: Target is running echo "ERROR: Target is runing" exit 101 ;; 102) # ERROR: Target have mark (is active) echo "ERROR: Target have mark" exit 102 ;; 0) # ERROR: --stat shuld not return zero echo "INTERNAL error, target stat return zero." exit 1 ;; *) # ERROR: Unknown return echo "INTERNAL error, unknown return, target stat ($ret)" exit $ret ;; esac if [ "${opt_source:0:1}" = "/" ] then source=$opt_source precopy="0" stopsrc="0" startsrc="0" opt_cpconf="0" elif [ -d $opt_confdir/$opt_source ] then precopy="0" stopsrc="0" startsrc="0" source="$opt_confdir/$opt_source/vdir" if vserver $opt_source running then if [ "$opt_migrate" = "1" ] then precopy=$opt_precopy stopsrc="1" elif [ "$opt_coldcopy" = "1" ] then precopy=$opt_precopy stopsrc="1" startsrc="1" elif [ "$opt_hotcopy" = "0" ] then echo "ERROR: Source VServer guest '$opt_source' is runing" exit 111 fi fi else echo "Source VServer guest '$opt_source' does not exist" exit 1 fi rid="$targetname$$" echo "TRACE: rid: '$rid'" if [ "$opt_cpconf" = "1" ] then vopt="" if [ "$opt_cid" != "0" ] then vopt="$vopt --cid=$opt_cid" fi echo "TRACE: conf vopt:'$vopt'" echo "`date +%T`: Copy VServer config" echo "`date +%T`: TRACE:( cd $opt_confdir/$opt_source && \ $opt_rsync -a --numeric-ids -e \"$vssh\" \ --rsync-path=\"$vremote --confdeamon=$rid $vopt --\" \ . $targethost: )" >> $vcopylog $sudoprobe if [ "$opt_dry" = "0" ] then ( cd $opt_confdir/$opt_source && $opt_rsync -a --numeric-ids \ -e "$vssh" \ --rsync-path="$vremote --confdeamon=$rid $vopt --" . $targethost: ) else echo "DRYRUN: ( cd $opt_confdir/$opt_source && \ $opt_rsync -a --numeric-ids -e \"$vssh\" \ --rsync-path=\"$vremote --confdeamon=$rid $vopt --\" \ . $targethost: )" fi fi vopt="" if [ "$opt_ip" != "0" ] then vopt="$vopt --ip=$opt_ip" fi if [ "$opt_dev" != "0" ] then vopt="$vopt --dev=$opt_dev" fi if [ "$opt_alias" != "1" ] then vopt="$vopt --alias=$opt_alias" fi if [ "$opt_hostname" != "1" ] then vopt="$vopt --hostname=$opt_hostname" fi if [ "$opt_mark" != "0" ] then vopt="$vopt --mark=$opt_mark" fi echo "TRACE: copy vopt:'$vopt'" echo "`date +%T`: TRACE:( cd $source && $opt_rsync -a --numeric-ids \ -e \"$vssh\" \ --rsync-path=\"$vremote --deamon=$rid $vopt --\" \ . $targethost: )" >> $vcopylog while [ $precopy -gt 0 ] do echo "`date +%T`: Precopy $precopy VServer image" $sudoprobe if [ "$opt_dry" = "0" ] then ( cd $source && $opt_rsync -a --numeric-ids -e "$vssh" \ --rsync-path="$vremote --deamon=$rid --" "$@" . $targethost: ) else echo "DRYRUN: ( cd $source && $opt_rsync -a --numeric-ids -e \ \"$vssh\" --rsync-path=\"$vremote --deamon=$rid --\" $@ . $targethost: )" fi precopy=$(($precopy - 1)) done if [ "$stopsrc" = "1" ] then echo "`date +%T`: Stoping source VServer '$opt_source'" if [ "$opt_dry" = "0" ] then vserver -s $opt_source stop else echo "DRYRUN: vserver -s $opt_source stop" fi fi echo "`date +%T`: Copy VServer image" $sudoprobe if [ "$opt_dry" = "0" ] then ( cd $source && $opt_rsync -a --numeric-ids -e "$vssh" \ --rsync-path="$vremote --deamon=$rid $vopt --" "$@" . $targethost: ) else echo "DRYRUN: ( cd $source && $opt_rsync -a --numeric-ids -e \"$vssh\" \ --rsync-path=\"$vremote --deamon=$rid $vopt --\" $@ . $targethost: )" fi if [ "$opt_migrate" = "1" ] then echo TRACE: $vssh $targethost $vremote --cleanup=$rid --activate $sudoprobe if $vssh $targethost $vremote --cleanup=$rid --activate then echo "`date +%T`: Activated target VServer." if [ -f $opt_confdir/$opt_source/mark ] then if [ "$opt_dry" = "0" ] then mv $opt_confdir/$opt_source/mark \ $opt_confdir/$opt_source/mark.dac else echo "DRYRUN: \ mv $opt_confdir/$opt_source/mark $opt_confdir/$opt_source/mark.dac" fi fi else echo "`date +%T`: FAILED to activate target VServer." if [ "$stopsrc" = "1" ] then echo "`date +%T`: FALLBACK: Atempt to restart source VServer" if [ "$opt_dry" = "0" ] then if vserver -s $opt_source start then echo "`date +%T`: Source VServer restarted" else echo "`date +%T`: ERROR: FAILED to start source VServer" fi else echo "DRYRUN: vserver -s $opt_source start" fi fi fi elif [ "$opt_clone" = "1" ] then if [ "$startsrc" = "1" ] then echo "`date +%T`: Restart source VServer" if [ "$opt_dry" = "0" ] then if vserver -s $opt_source start then echo "`date +%T`: Source VServer restarted" else echo "`date +%T`: ERROR: FAILED to start source VServer" fi else echo "DRYRUN: vserver -s $opt_source start" fi fi echo TRACE: $vssh $targethost $vremote --cleanup=$rid --activate $sudoprobe if $vssh $targethost $vremote --cleanup=$rid --activate then echo "`date +%T`: Activated target VServer." else echo "`date +%T`: FAILED to activate target VServer." fi else if [ "$startsrc" = "1" ] then echo "`date +%T`: Restart source VServer" if [ "$opt_dry" = "0" ] then if vserver -s $opt_source start then echo "`date +%T`: Source VServer restarted" else echo "`date +%T`: ERROR: FAILED to start source VServer" fi else echo "DRYRUN: vserver -s $opt_source start" fi fi echo TRACE: $vssh $targethost $vremote --cleanup=$rid $sudoprobe $vssh $targethost $vremote --cleanup=$rid fi