9f73a843afa26a316fe0c9d9e38f27190082d63b
[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 # given a (generic, node-independant) CD ISO image, and a (set of)
16 # node-specific config file(s), this command creates a new almost
17 # identical ISO image with the node config file embedded as
18 # /usr/boot/plnode.txt in the overlay.img image
19 # the output iso images are named after the nodes, and stored in .
20
21 ######## Logic
22 # here is how we do this
23 # for efficiency, we do only once:
24 #   (*) mount the generic iso
25 #   (*) copy it into a temp dir
26 #   (*) unzip/unarchive overlay image into another temp dir
27 # then for each node, we
28 #   (*) insert plnode.txt at the right place
29 #   (*) rewrap a gzipped/cpio overlay.img, that we push onto the
30 #       copied iso tree
31 #   (*) rewrap this into an iso image
32 # and cleanup/umount everything 
33
34 ######## Customizing the BootCD
35 # In addition we check (once) for
36 #  (*) a file called 'bootcd.img' in the current dir
37 #  (*) a directory named 'bootcd/' in the current dir
38 # if any of those is present, we use this - presumably custom - stuff to
39 # replace original bootcd.img from the CD
40 # more precisely:
41 #  (*) if the .img is present, it is taken as-is,
42 #  (*) if not but bootcd/ is present, bootcd.img is refreshed and used
43 # All this is done only once at startup because it typically
44 #  takes 40s to recompress bootcd.img
45 # TODO
46 #  allow local bootcd/ to hold only patched files
47 # and get the rest from the CD's bootcd.img
48
49 ######## Implementation note
50 # in a former release it was possible to perform faster by
51 # loopback-mounting the generic iso image
52 # Unfortunately mkisofs cannot graft a file that already exists on the
53 # original tree (so overlay.img cannot be overridden)
54 # to make things worse we cannot loopback-mount the cpio-gzipped
55 # overlay image either, so all this stuff is way more complicated
56 # than it used to be.
57 # It's still pretty fast, unless recompressing a bootcd.img is required
58
59 set -e 
60 COMMAND=$(basename $0 .sh)
61
62 function usage () {
63
64    echo "Usage: $0 generic-iso node-config [node-configs]"
65    echo "Creates a node-specific ISO image"
66    echo "with the node-specific config file embedded as /boot/plnode.txt"
67    exit 1
68 }
69
70 ### read config file in a subshell and echoes host_name
71 function host_name () {
72   export CONFIG=$1; shift
73   ( . "$CONFIG" ; echo $HOST_NAME )
74 }
75
76 ### Globals
77 OVERLAY_IMAGE=overlay.img
78 PLNODE_PATH=/usr/boot
79 PLNODE=plnode.txt
80 # use local bootcd/ or bootcd.img if existing
81 BOOTCD_IMAGE=bootcd.img
82 BOOTCD_ROOT=bootcd
83 ## arg-provided generic iso
84 ISO_GENERIC=
85 # node-dep conf file
86 NODE_CONFIG=
87 # resulting iso image and log
88 NODE_ISO=
89 NODE_LOG=
90 ## mount points and temps
91 ISO_MOUNT=/tmp/$COMMAND-$$-mount
92 ISO_ROOT=/tmp/$COMMAND-$$-iso
93 OVERLAY_ROOT=/tmp/$COMMAND-$$-overlay
94 # node-dep cpio/gzip image
95 NODE_OVERLAY=
96
97 CPIO_OARGS="-oc --quiet"
98 CPIO_IARGS="-id --quiet"
99 CPIO_PARGS="-pdu"
100
101 # export VERBOSE=true for enabling this
102 function verbose () {
103    if [ -n "$VERBOSE" ] ; then
104      echo "$@"
105    fi
106  }
107
108 function message () { echo "$COMMAND : $@" ; }
109 function message-n () { echo -n "$COMMAND : $@" ; }
110 function message-done () { echo Done ; }
111 function error () { echo "$COMMAND : ERROR $@ - exiting" ; exit 1 ;}
112
113 # lazy startup
114 STARTED_UP=
115 function startup () {
116
117    [[ -n "$DEBUG" ]] && set -x
118
119    # lazy : run only once
120    [[ -n "$STARTED_UP" ]] && return
121    message "starting up"
122
123    ### checking
124    [ ! -f "$ISO_GENERIC" ] && error "Could not find template ISO image"
125    [ -d "$ISO_MOUNT" ] && error "$ISO_MOUNT already exists" 
126    [ -d "$ISO_ROOT" ] && error "$ISO_ROOT already exists" 
127    [ -d "$OVERLAY_ROOT" ] && error "$OVERLAY_ROOT already exists"
128
129    verbose "Creating temp dirs"
130    mkdir -p $ISO_MOUNT $ISO_ROOT $OVERLAY_ROOT
131    verbose "Mounting generic ISO $ISO_GENERIC under $ISO_MOUNT"
132    mount -o ro,loop $ISO_GENERIC $ISO_MOUNT
133
134    ### DONT!! use tar for duplication
135    message "Duplicating ISO image in $ISO_ROOT"
136    (cd $ISO_MOUNT ; find . | cpio $CPIO_PARGS  $ISO_ROOT )
137
138    # use local bootcd.img or bootcd/ if existing
139    message-n "Checking for custom $BOOTCD_ROOT in . "
140    if [ -d $BOOTCD_ROOT -a -f $BOOTCD_IMAGE ] ; then
141      message-n " using $BOOTCD_IMAGE as-is "
142    elif [ -d $BOOTCD_ROOT -a ! -f $BOOTCD_IMAGE ] ; then
143      message-n "yes, making img .. "
144      (cd $BOOTCD_ROOT ; find . | cpio $CPIO_OARGS) | gzip -9 > $BOOTCD_IMAGE
145    fi
146    if [ -f $BOOTCD_IMAGE ] ; then
147      message-n "pushing onto $ISO_ROOT.. "
148      cp $BOOTCD_IMAGE $ISO_ROOT/$BOOTCD_IMAGE
149    fi
150    message-done
151      
152    message "Extracting generic overlay image in $OVERLAY_ROOT"
153    gzip -d -c "$ISO_ROOT/$OVERLAY_IMAGE" | ( cd "$OVERLAY_ROOT" ; cpio $CPIO_IARGS )
154
155    STARTED_UP=true
156
157 }   
158
159 function node_cleanup () {
160    verbose "Cleaning node-dependent cpio image"
161    rm -rf "$NODE_OVERLAY"
162   
163 }
164
165 function cleanup () {
166
167    echo "$COMMAND : cleaning up"
168    [[ -n "$DEBUG" ]] && set -x
169
170    verbose "Cleaning overlay image"
171    rm -rf "$OVERLAY_ROOT"
172    verbose "Cleaning ISO image"
173    rm -rf "$ISO_ROOT"
174    verbose "Cleaning node-dep overlay image"
175    rm -f "$NODE_OVERLAY"
176    verbose "Unmounting $ISO_MOUNT"
177    umount "$ISO_MOUNT" 2> /dev/null
178    rmdir "$ISO_MOUNT"
179    exit
180 }
181
182 function abort () {
183    echo "$COMMAND : Aborting"
184    message "Cleaning $NODE_ISO"
185    rm -f "$NODE_ISO"
186    cleanup
187 }
188
189 function main () {
190
191    trap abort int hup quit err
192    set -e
193
194    [[ -n "$DEBUG" ]] && set -x
195
196    [[ -z "$@" ]] && usage
197    ISO_GENERIC=$1; shift
198
199    [[ -z "$@" ]] && usage
200
201 #  perform that later (lazily)
202 #  so that (1st) node-dep checking are done before we bother to unpack
203 #   startup
204
205    for NODE_CONFIG in "$@" ; do
206
207      NODENAME=$(host_name $NODE_CONFIG)
208      if [ -z "$NODENAME" ] ; then
209        message "HOST_NAME not found in $NODE_CONFIG - skipped"
210        continue
211      fi
212    
213      message "$COMMAND : dealing with node $NODENAME"
214
215      NODE_ISO="$NODENAME.iso"
216      NODE_LOG="$NODENAME.log"
217      NODE_OVERLAY="$NODENAME.img"
218
219      ### checking
220      if [ -e  "$NODE_ISO" ] ; then
221        message "$NODE_ISO exists, please remove first - skipped" ; continue
222      fi
223      if [ ! -f "$NODE_CONFIG" ] ; then
224        message "Could not find node-specifig config - skipped" ; continue
225      fi
226      
227      startup
228
229      verbose "Pushing node config into overlay image"
230      mkdir -p $OVERLAY_ROOT/$PLNODE_PATH
231      cp "$NODE_CONFIG" $OVERLAY_ROOT/$PLNODE_PATH/$PLNODE
232
233      echo "$COMMAND : Creating overlay image for $NODENAME"
234      (cd "$OVERLAY_ROOT" ; find . | cpio $CPIO_OARGS) | gzip -9 > $NODE_OVERLAY
235
236      message-n "Pushing custom overlay image "
237      cp "$NODE_OVERLAY" "$ISO_ROOT/$OVERLAY_IMAGE"
238      message-done
239
240      message "Refreshing isolinux.cfg"
241      # Calculate ramdisk size (total uncompressed size of both archives)
242      ramdisk_size=$(gzip -l $ISO_ROOT/bootcd.img $ISO_ROOT/overlay.img | tail -1 | awk '{ print $2; }') # bytes
243      # keep safe, provision for cpio's block size
244      ramdisk_size=$(($ramdisk_size / 1024 + 1)) # kilobytes
245
246      # Write isolinux configuration
247      cat > $ISO_ROOT/isolinux.cfg <<EOF
248 DEFAULT kernel
249 APPEND ramdisk_size=$ramdisk_size initrd=bootcd.img,overlay.img root=/dev/ram0 rw
250 DISPLAY pl_version
251 PROMPT 0
252 TIMEOUT 40
253 EOF
254
255      message-n "Writing custom image, log on $NODE_LOG .. "
256      mkisofs -o "$NODE_ISO" -R -allow-leading-dots -J -r -b isolinux.bin \
257      -c boot.cat -no-emul-boot -boot-load-size 4 -boot-info-table \
258      "$ISO_ROOT" > "$NODE_LOG" 2>&1
259      message-done
260    
261      node_cleanup
262      
263      message "CD ISO image for $NODENAME in $NODE_ISO"
264    done
265
266    cleanup
267
268 }
269
270 ####################
271 main "$@"