- moved here from sysv/
[util-vserver.git] / scripts / vserver-copy
1 #!/bin/sh
2
3 # Copyright (C) 2003 Mark Lawrence <nomad@null.net>,
4 #               2203,2004 Enrico Scholz <enrico.scholz@informatik.tu-chemnitz.de>
5 #  
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2, or (at your option)
9 # any later version.
10 #  
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 # GNU General Public License for more details.
15 #  
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19
20 # Copy/Sync a virtual host from one machine to another
21 #
22 # History:
23 #
24 # 2003-04-04: Version 0.4 - Mark lawrence
25 # - Set "ONBOOT=no" in the destination .conf file when --startstop
26 #   is not used, in case the destination roothost reboots. We don't
27 #   want two copies of a vserver running at the same time.
28 #
29 # 2003-03-04: Version 0.3 - Mark lawrence
30 # - Changed all checks for [ "" != "$var" ] into [ -n|-z "$var" ]. "" doesn't
31 #   seem to work for bash on the Sparc architecture.
32 # - Changed $ssh variable into $shcmd.
33 #
34 # 2003-02-23: Version 0.2 - Mark Lawrence
35 # - Set ONBOOT to "no" in the original .conf file when the "-s" flag is 
36 #   used so that if/when you reboot the source roothost you don't have the
37 #   same vserver and IP address running on two machines.
38
39 : ${UTIL_VSERVER_VARS:=$(dirname $0)/util-vserver-vars}
40 test -e "$UTIL_VSERVER_VARS" || {
41     echo "Can not find util-vserver installation; aborting..."
42     exit 1
43 }
44 . "$UTIL_VSERVER_VARS"
45
46 VERSION="0.4"
47 umask 022
48 me=${0##*/}
49
50 mktemp="$(which mktemp 2>/dev/null) /tmp/vc.XXXXXX" || \
51 mktemp=$(which tempfile 2>/dev/null) || {
52     echo $"Can not find mktemp or tempfile" >&2
53     exit 1
54 }
55
56 ### Helper functions ###
57
58 # Save stdin and stdout for later use
59 exec 3>&1
60 exec 4>&2
61
62 noninteractive () {
63         exec &> /dev/null
64 }
65
66 interactive () {
67         exec 1>&3
68         exec 2>&4
69 }
70
71 info () {
72         ! $quiet && echo "I: $me: $1" >&3
73 }
74
75 warn () {
76         ! $quiet && echo "W: $me: $1" >&4
77 }
78
79 error () {
80         ! $quiet && echo "E: $me: $2" >&4
81         exit $1
82 }
83
84
85 ### Usage/Info functions ###
86
87 usage () {
88     cat <<EOF 1>&2
89 Usage:  $me [-hVvqidrRs] vserver newname
90         $me [-hVvqidrRs] vserver host:[newname]
91 EOF
92 }
93
94 full_usage () {
95         usage
96         cat <<EOF
97
98 $me uses rsync to make a copy of a vserver. If the destination
99 name contains a host specification the vserver will be synchronised to
100 the remote destination over ssh/rsh.
101
102 This can be used on a running vserver to make a warm backup. With the -s
103 flag a vserver can even be operationally moved to different hardware within
104 seconds.
105
106 The -i and -d flags can be used to minimally reconfigure the destination
107 vserver (rewrites /etc/vservers/newname.conf and $VROOTDIR/newname/etc/hosts)
108
109 Options:
110         -h, --help              this help
111         -V, --version           copyright and version information
112         -v, --verbose           show all output
113         -q, --quiet             direct all output to /dev/null (no password
114                                 prompt for logins on remote hosts!)
115         -d, --domain [string]   new dns domain (must be used with -i)
116         -i, --ip [addr]         new IP address (must be used with -d)
117         -r, --vsroot            location of "/vserver/" directory
118         -R, --rsh               use rsh (instead of default ssh) for
119                                 network transport
120         -s, --stopstart         stop the local vserver before copying and start
121                                 it on the destination host afterwards
122
123 EOF
124 }
125
126 full_version () {
127     cat <<EOF
128 $me version $VERSION
129 Copyright (c) 2002 Mark Lawrence   <nomad@null.net>
130
131 This program is free software; you can redistribute it and/or modify
132 it under the terms of the GNU General Public License as published by
133 the Free Software Foundation; either version 2 of the License, or (at
134 your option) any later version.
135
136 EOF
137 }
138
139
140 ### Default values and Command line options ###
141
142 stopstart=(false)
143 verbose=(false)
144 quiet=(false)
145 shcmd="ssh"
146 rsflag="-e"
147 rsh=(false)
148 colon=":"
149 domain=""
150 ip=""
151 vsroot=$VROOTDIR
152
153 if [ $# -eq 0 ]; then  # Script invoked with no command-line args?
154         usage
155         exit 1
156 fi  
157
158 temp=$(getopt -o hVvqd:i:rRs --long help,version,verbose,quiet,domain:,ip:,vsroot,rsh,stopstart, -n $me -- "$@")
159
160 if [ $? -ne 0 ]; then
161         echo "  (See -h for help)"
162         exit 1
163 fi
164
165 # Note the quotes around `$temp': they are essential!
166 eval set -- "$temp"
167
168 while true; do
169         case "$1" in
170                 -h|--help)      full_usage
171                                 exit 1
172                                 ;;
173                 -V|--version)   full_version
174                                 exit 1
175                                 ;;
176                 -v|--verbose)   verbose=(true)
177                                 shift
178                                 ;;
179                 -q|--quiet)     quiet=(true)
180                                 shift
181                                 ;;
182                 -d|--domain)    domain="$2"
183                                 shift 2
184                                 ;;
185                 -i|--ip)        ip="$2"
186                                 shift 2
187                                 ;;
188                 -r|--vsroot)    vsroot="$2"
189                                 shift 2
190                                 ;;
191                 -R|--rsh)       rsh=(true)
192                                 shift
193                                 ;;
194                 -s|--stopstart) stopstart=(true)
195                                 shift
196                                 ;;
197                 --)             shift
198                                 break
199                                 ;;
200                 *)              echo "Internal error!"
201                                 exit 1
202                                 ;;
203         esac
204 done
205
206 if [ $# -ne 2 ]; then
207         usage
208         exit 1
209 fi
210
211
212 ### ###
213
214 # By default we are reasonably quiet (ouput only via info, warn & error)
215 if $verbose; then
216         interactive
217 else
218         noninteractive
219 fi
220
221 now=$(date)
222 info "called on $(hostname) at $now"
223
224
225 vserver=$1
226 vconf=/etc/vservers/$vserver.conf
227 vroot=$vsroot/$vserver
228
229 if $rsh; then
230         shcmd="rsh"
231 fi
232
233 if (echo $2 | grep '^[a-z][a-z0-9]\+$'); then
234         dhost=""
235         newname=$2
236         shcmd=""
237         rsflag=""
238         colon=""
239         if $rsh; then
240                 warn "rsh is set but not used for a local copy"
241         fi
242 elif (echo $2 | grep '^[a-z].*[a-z0-9]:$'); then
243         dhost=${2/:/}
244         newname=$vserver
245 elif (echo $2 | grep '^[a-z].*[a-z0-9]:[a-z].*[a-z0-9]$'); then
246         dhost=${2/:*/}
247         newname=${2/*:/}
248 else
249         error 1 "Second argument must be of the form \"[host:]name\" or \"host:\""
250 fi
251
252 target=$vsroot/$newname
253 targetconf=/etc/vservers/$newname.conf
254
255
256 ### Perform some sanity checks ###
257
258 if [ ! -d $vroot ]; then
259         error 1 "Directory \"$vroot\" does not exist"
260 fi
261
262 if [ ! -e $vconf ]; then
263         error 1 "Vserver file \"$vconf\" does not exist"
264 fi
265
266 if [ -z "$dhost" ] && [ "$vserver" == "$newname" ]; then
267         error 1 "Source and destination names cannot be the same on the localhost"
268 fi
269
270 if [ -n "$dhost" ] && ! (host $dhost | grep 'has address'); then
271         warn "$dhost does not resolve into an IP address"
272 fi
273
274 if [ \( -n "$ip" -a -z "$domain" \) -o \
275      \( -z "$ip" -a -n "$domain" \) ]
276 then
277         error 1 "Both IP address and domain must be specified together"
278 fi
279
280 if [ -n "$ip" ] && \
281 ! (echo $ip | grep '^[0-9]\{1,3\}\(\.[0-9]\{1,3\}\)\{3\}$' ); then
282         error 1 "\"$ip\" is not a valid IP address"
283 fi
284
285 # This works both locally and remote
286 if ($shcmd $dhost $SBINDIR/vserver $newname running | grep 'is running'); then
287         warn "destination vserver \"$newname\" is running" 
288         error 1 "Cannot copy over a running vserver"
289 fi
290
291
292 ### Do the copy ###
293
294 info "Attempting to copy $vserver to $dhost$colon$newname"
295
296 if $stopstart; then
297         info "Stopping virtual server \"$vserver\" on localhost"
298         $SBINDIR/vserver $vserver stop
299 fi
300
301 test "$dhost" || {
302     mkdir -p -m755 $target
303     chattr -t $target
304 }
305
306 info "Syncing directories"
307 # trailing slashes very important in the rsync!
308 if ! rsync -avxz $rsflag $shcmd $vroot/ $dhost$colon$target/; then
309         error 1 "rsync failed"
310 fi
311
312 if [ -n "$ip" -a -n "$domain" ]; then
313         # Insert the new IPROOT/S_HOSTNAME values into the config file
314         info "Modifying $targetconf"
315         tmpf=$($mktemp)
316         if (sed -e "s/^S_HOSTNAME=.*/S_HOSTNAME=\"$newname\"/" \
317                 -e "s/^IPROOT=.*/IPROOT=\"$ip\"/" $vconf > $tmpf)
318         then
319                 if ! rsync -v $rsflag $shcmd $tmpf $dhost$colon$targetconf; then
320                         error $? "vserver config file copy/change failed"
321                 fi
322
323         else
324                 warn "Unable to reconfigure virtual server config file"
325         fi
326
327         # create a new /etc/hostname
328         info "Creating hostname file"
329         echo $newname > $tmpf
330         if ! rsync -v $rsflag $shcmd $tmpf $dhost$colon$target/etc/hostname; then
331                 error 1 "vserver /etc/hostname copy failed"
332         fi
333
334         info "Creating /etc/hosts"
335         cat << EOF > $tmpf
336 # /etc/hosts (automatically generated by $me)
337
338 127.0.0.1       localhost
339 $ip     $newname.$domain        $newname
340
341 # The following lines are desirable for IPv6 capable hosts
342
343 ::1     ip6-localhost ip6-loopback
344 fe00::0 ip6-localnet
345 ff00::0 ip6-mcastprefix
346 ff02::1 ip6-allnodes
347 ff02::2 ip6-allrouters
348 ff02::3 ip6-allhosts
349 EOF
350
351         # copy /etc/hosts
352         if ! rsync -v $rsflag $shcmd $tmpf $dhost$colon$target/etc/hosts; then
353                 error 1 "vserver /etc/hosts copy failed"
354         fi
355         rm -f $tmpf
356
357 else
358         if ! $stopstart; then
359                 # Make sure that this vserver doesn't start on the 
360                 # destination host if it reboots
361                 tmpf=$($mktemp)
362                 sed -e 's/^ONBOOT=.*/ONBOOT=no/' $vconf > $tmpf
363                 vconf=$tmpf
364         fi
365
366         # copy newname.conf unchanged
367         info "Copying $targetconf"
368         if ! rsync -v $rsflag $shcmd $vconf $dhost$colon$targetconf; then
369                 error 1 "vserver config file copy/change failed"
370         fi
371
372         rm -f $tmpf
373 fi
374
375
376 if $stopstart; then
377         info "Starting virtual server \"$vserver\" on $dhost"
378         $shcmd $dhost $SBINDIR/vserver $vserver start
379         if ($shcmd $dhost $SBINDIR/vserver $vserver running | \
380         grep 'not running'); then
381                 error 1 "Virtual server \"$vserver\" failed to start on $dhost"
382         fi
383
384         # Make sure that we don't start the original on next boot
385         tmpf=$($mktemp)
386         sed -e 's/^ONBOOT=.*/ONBOOT=no/' $vconf > $tmpf
387         mv $tmpf $vconf
388 fi
389
390 exit 0