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