merge to HEAD as of 2006-08-21
[bootcd.git] / cdcustom.sh
1 #!/bin/bash
2
3 # purpose : create a node-specific CD ISO image
4
5 # NOTE (see also bootcd/build.sh)
6 # If you run your own myplc instance, and you dont need to
7 # customize the bootcd, you might wish to use bootcd/build.sh
8 # with the -f option
9 # However cdcustom.sh might turn out useful if
10 # (*) you only have an iso image and nothing else
11 # (*) or you want to generate several iso images in a single run
12 # (*) or you run myplc rpm, but need to customize the bootcd image,
13 #     because the myplc rpm does not come with the required sources
14
15 # See usage for full details
16
17 ######## Implementation note
18 # in a former release it was possible to perform faster by
19 # loopback-mounting the generic iso image
20 # Unfortunately mkisofs cannot graft a file that already exists on the
21 # original tree (so overlay.img cannot be overridden)
22 # to make things worse we cannot loopback-mount the cpio-gzipped
23 # overlay image either, so all this stuff is way more complicated
24 # than it used to be.
25 #
26 # as of 2006 jun 28 we use a third image named custom.img for
27 # overriding files in bootcd.img, which let us use bootcd.img intact
28 # and thus notably speeds things up 
29 #
30 ######## Logic
31 # here is how we do this
32 # for efficiency, we do only once:
33 #   (*) mount the generic iso
34 #   (*) copy it into a temp dir
35 #   (*) unzip/unarchive overlay image into another temp dir
36 #   (*) if required prepare a custom.img 
37 # then for each node, we
38 #   (*) insert plnode.txt at the right place if not a default iso
39 #   (*) rewrap a gzipped/cpio overlay.img, that we push onto the
40 #       copied iso tree
41 #   (*) rewrap this into an iso image
42 # and cleanup/umount everything 
43
44
45 set -e 
46 COMMANDSH=$(basename $0)
47 COMMAND=$(basename $0 .sh)
48 REVISION="$Id: cdcustom.sh,v 1.7 2006/06/28 14:18:11 thierry Exp $"
49
50 function usage () {
51
52    echo "Usage: $COMMANDSH [-f] [ -c bootcd-dir] generic-iso node-config [.. node-configs]"
53    echo " Creates a node-specific ISO image"
54    echo "*Options"
55    echo -e " -f\r\t\tForces overwrite of output iso images"
56    echo -e " -c bootcd-dir\r\t\tis taken as the root of a set of custom bootcd files"
57    echo -e "\t\ti.e. the files under dir take precedence"
58    echo -e "\t\tover the ones in the generic bootcd"
59    echo "*Arguments"
60    echo -e " generic-iso\r\t\tThe iso image as downloaded from myplc"
61    echo -e "\t\ttypically in /plc/data/var/www/html/download/"
62    echo -e " node-config(s)\r\t\tnode config files (plnode.txt format)"
63    echo -e " default\r\t\tmentioned instead of a plnode.txt file, for generating"
64    echo -e "\t\ta node-independent iso image"
65    echo -e "\t\tThis is defaultbehaviour when no node-config are provided"
66    echo "*Outputs"
67    echo " node-specific iso images are named after nodename[-bootcd-dir].iso"
68    echo " node-independant iso image is named after bootcd-dir.iso"
69    echo "*Examples"
70    echo "# $COMMANDSH -c onelab-bootcd /plc/data/var/www/html/download/onelab-BootCD-3.3.iso"
71    echo "  Creates onelab-bootcd.iso that has no plnode.txt embedded and that uses"
72    echo "  the hw init scripts located under onelab-bootcd/etc/rc.d/init.d/"
73    echo "# $COMMANDSH  /plc/data/var/www/html/download/onelab-BootCD-3.3.iso node1.txt node2.txt"
74    echo "  Creates node1.iso and node2.iso that have plnode.txt embedded for these two nodes"
75    echo "  and the standard bootcd"
76    echo "*Version $REVISION"
77    exit 1
78 }
79
80 ### read config file in a subshell and echoes host_name
81 function host_name () {
82   export CONFIG=$1; shift
83   ( . "$CONFIG" ; echo $HOST_NAME )
84 }
85
86 ### Globals
87 PLNODE_PATH=/usr/boot
88 PLNODE=plnode.txt
89 DEFAULT_TARGET=default
90 # defined on the command-line
91 CUSTOM_DIR=
92 ## arg-provided generic iso
93 ISO_GENERIC=
94 # node-dep conf file
95 NODE_CONFIG=
96 # resulting iso image and log
97 NODE_ISO=
98 NODE_LOG=
99 ## mount points and temps
100 ISO_MOUNT=/tmp/$COMMAND-$$-mount
101 ISO_ROOT=/tmp/$COMMAND-$$-iso
102 OVERLAY_ROOT=/tmp/$COMMAND-$$-overlay
103 # node-dep cpio/gzip image
104 NODE_OVERLAY=
105
106 CPIO_OARGS="-oc --quiet"
107 CPIO_IARGS="-id --quiet"
108 CPIO_PARGS="-pdu --quiet"
109
110 # export DEBUG=true
111 # for enabling debug messages (set -x)
112
113 # export VERBOSE=true for enabling this
114 function verbose () {
115    if [ -n "$VERBOSE" ] ; then
116      echo "$@"
117    fi
118  }
119
120 function message () { echo -e "$COMMAND : $@" ; }
121 function message-n () { echo -en "$COMMAND : $@" ; }
122 function message-done () { echo Done ; }
123 function error () { echo -e "$COMMAND : ERROR $@ - exiting" ; exit 1 ;}
124
125 # lazy startup
126 STARTED_UP=
127 function startup () {
128
129    [[ -n "$DEBUG" ]] && set -x
130
131    # lazy : run only once
132    [[ -n "$STARTED_UP" ]] && return
133    message "lazy start up"
134
135    ### checking
136    [ ! -f "$ISO_GENERIC" ] && error "Could not find template ISO image"
137    [ -d "$ISO_MOUNT" ] && error "$ISO_MOUNT already exists" 
138    [ -d "$ISO_ROOT" ] && error "$ISO_ROOT already exists" 
139    [ -d "$OVERLAY_ROOT" ] && error "$OVERLAY_ROOT already exists"
140
141    verbose "Creating temp dirs"
142    mkdir -p $ISO_MOUNT $ISO_ROOT $OVERLAY_ROOT
143    verbose "Mounting generic ISO $ISO_GENERIC under $ISO_MOUNT"
144    mount -o ro,loop $ISO_GENERIC $ISO_MOUNT
145
146    ### DONT!! use tar for duplication
147    message "Duplicating ISO image in $ISO_ROOT"
148    (cd $ISO_MOUNT ; find . | cpio $CPIO_PARGS  $ISO_ROOT )
149
150    message "Extracting generic overlay image in $OVERLAY_ROOT"
151    gzip -d -c "$ISO_ROOT/overlay.img" | ( cd "$OVERLAY_ROOT" ; cpio $CPIO_IARGS )
152
153    if [ -n "$CUSTOM_DIR" ] ; then
154      [ -d "$CUSTOM_DIR" ] || error "Directory $CUSTOM_DIR not found"
155      prepare_custom_image
156    fi
157
158    STARTED_UP=true
159
160 }   
161
162 function prepare_custom_image () {
163
164    # Cleaning any sequel
165    rm -f custom.img
166    [ -f custom.img ] && error "Could not cleanup custom.img"
167    
168    message "WARNING : You are creating *custom* boot CDs"
169
170    message-n "Creating $ISO_ROOT/custom.img"
171    (cd $CUSTOM_DIR ; find . | cpio $CPIO_OARGS) | gzip -9 > $ISO_ROOT/custom.img
172    message-done
173    
174 }
175
176 function node_cleanup () {
177    verbose "Cleaning node-dependent cpio image"
178    rm -rf "$NODE_OVERLAY"
179   
180 }
181
182 function cleanup () {
183
184    echo "$COMMAND : cleaning up"
185    [[ -n "$DEBUG" ]] && set -x
186
187    verbose "Cleaning overlay image"
188    rm -rf "$OVERLAY_ROOT"
189    verbose "Cleaning ISO image"
190    rm -rf "$ISO_ROOT"
191    verbose "Cleaning node-dep overlay image"
192    rm -f "$NODE_OVERLAY"
193    verbose "Unmounting $ISO_MOUNT"
194    umount "$ISO_MOUNT" 2> /dev/null
195    rmdir "$ISO_MOUNT"
196    exit
197 }
198
199 function abort () {
200    echo "$COMMAND : Aborting"
201    message "Cleaning $NODE_ISO"
202    rm -f "$NODE_ISO"
203    cleanup
204 }
205
206 function main () {
207
208    trap abort int hup quit err
209    set -e
210
211    [[ -n "$DEBUG" ]] && set -x
212
213    # accept -b as -c, I am used to it now
214    while getopts "c:b:fh" opt ; do
215      case $opt in
216        c|b)
217          CUSTOM_DIR=$OPTARG ;;
218        f)
219          FORCE_OUTPUT=true ;;
220        h|*)
221          usage ;;
222      esac
223    done
224
225    shift $(($OPTIND-1))
226    
227    [[ -z "$@" ]] && usage
228    ISO_GENERIC=$1; shift
229
230    if [ -z "$@" ] ; then
231      nodes="$DEFAULT_TARGET"
232    else
233      nodes="$@"
234    fi
235
236 #  perform that later (lazily)
237 #  so that (1st) node-dep checking are done before we bother to unpack
238 #   startup
239
240    for NODE_CONFIG in $nodes ; do
241
242      if [ "$NODE_CONFIG" = "$DEFAULT_TARGET" ] ; then
243        NODE_DEP=""
244        # default node without customization does not make sense
245        if [ -z "$CUSTOM_DIR" ] ; then
246          message "creating a non-custom node-indep. image refused\n(Would have no effect)"
247          continue
248        else
249          NODENAME=$DEFAULT_TARGET
250          NODEOUTPUT=$(basename $CUSTOM_DIR)
251        fi
252      else
253        NODE_DEP=true
254        NODENAME=$(host_name $NODE_CONFIG)
255        if [ -z "$NODENAME" ] ; then
256          message "HOST_NAME not found in $NODE_CONFIG - skipped"
257          continue
258        fi
259        if [ -z "$CUSTOM_DIR" ] ; then
260          NODEOUTPUT=$NODENAME
261        else
262          NODEOUTPUT=${NODENAME}-$(basename $CUSTOM_DIR)
263        fi
264      fi
265
266      message "$COMMAND : dealing with node $NODENAME"
267
268      NODE_ISO="$NODEOUTPUT.iso"
269      NODE_LOG="$NODEOUTPUT.log"
270
271      ### checking
272      if [ -e  "$NODE_ISO" ] ; then
273        if [ -n "$FORCE_OUTPUT" ] ; then
274          message "$NODE_ISO exists, will overwrite (-f)"
275          rm $NODE_ISO
276        else
277          message "$NODE_ISO exists, please remove first - skipped" ; continue
278        fi
279      fi
280      if [ -n "$NODE_DEP" -a ! -f "$NODE_CONFIG" ] ; then
281        message "Could not find node-specifig config - skipped" ; continue
282      fi
283      
284      startup
285
286      if [ -n "$NODE_DEP" ] ; then
287        verbose "Pushing node config into overlay image"
288        mkdir -p $OVERLAY_ROOT/$PLNODE_PATH
289        cp "$NODE_CONFIG" $OVERLAY_ROOT/$PLNODE_PATH/$PLNODE
290      else
291        verbose "Cleaning node config for node-indep. image"
292        rm -f $OVERLAY_ROOT/$PLNODE_PATH/$PLNODE
293      fi
294
295      echo "$COMMAND : Creating overlay image for $NODENAME"
296      (cd "$OVERLAY_ROOT" ; find . | cpio $CPIO_OARGS) | gzip -9 > $ISO_ROOT/overlay.img
297
298      message "Refreshing isolinux.cfg"
299      # Calculate ramdisk size (total uncompressed size of both archives)
300
301      ##########
302      # N.B. Thierry Parmentelat - 2006-06-28
303      # the order in which these images need to be mentioned here for
304      # isolinux involved some - not so educated - guesses
305      # as per syslinux source code in syslinux/runkernel.inc, the
306      # config file is parsed left to right, and indeed it's in that
307      # order that the files are loaded right off the CD
308      # This does not tell however, in case a given file is present in
309      # two different images - and that's the very purpose here - which
310      # one will take precedence over the other
311      # I came up with this order on a trial-and-error basis, I would
312      # have preferred to find it described somewhere
313      # Might be worth checking with other versions of syslinux in case
314      # the custom files would turn out to not be taken into account
315      ##########
316
317      if [ -n "$CUSTOM_DIR" ] ; then
318        images="bootcd.img custom.img overlay.img"
319      else
320        images="bootcd.img overlay.img"
321      fi
322      
323      ramdisk_size=$(cd $ISO_ROOT ; gzip -l $images | tail -1 | awk '{ print $2; }') # bytes
324      # keep safe, provision for cpio's block size
325      ramdisk_size=$(($ramdisk_size / 1024 + 1)) # kilobytes
326
327      initrd_images=$(echo "$images" | sed -e 's/ /,/g')
328      # Write isolinux configuration
329      cat > $ISO_ROOT/isolinux.cfg <<EOF
330 DEFAULT kernel
331 APPEND ramdisk_size=$ramdisk_size initrd=$initrd_images root=/dev/ram0 rw
332 DISPLAY pl_version
333 PROMPT 0
334 TIMEOUT 40
335 EOF
336
337      message-n "Writing custom image, log on $NODE_LOG .. "
338      mkisofs -o "$NODE_ISO" -R -allow-leading-dots -J -r -b isolinux.bin \
339      -c boot.cat -no-emul-boot -boot-load-size 4 -boot-info-table \
340      "$ISO_ROOT" > "$NODE_LOG" 2>&1
341      message-done
342    
343      node_cleanup
344      
345      message "CD ISO image for $NODENAME in $NODE_ISO"
346    done
347
348    cleanup
349
350 }
351
352 ####################
353 main "$@"