5 # Parse input parameters
7 echo "Usage: $0 --release|-r <jessie|wheezy> [options]
10 --debootstrap-url|-u <debootstrap-mirror> (default: http://http.debian.net/debian)
11 --sources.list-mirror|-s <source-list-mirror> (default: http://http.debian.net/debian)
12 --extra-packages|-e <package>,<package>,...
13 --hook-script|-hs <hook-script>
14 --image-size|-is <image-size> (default: 2G)
15 --automatic-resize|-ar
16 --automatic-resize-space|-ars <suplementary-space> (default: 50M)
17 --login|-l <userlogin> (default: debian)
18 --password|-p <root-password> (dangerous option: avoid it if possible)
19 For more info: man $0"
26 "--extra-packages"|"-e")
27 if [ -z "${2}" ] ; then
28 echo "No parameter defining the extra packages"
35 "--debootstrap-url"|"-u")
36 if [ -z "${2}" ] ; then
37 echo "No parameter defining the debootstrap URL"
48 "--automatic-resize"|"-ar")
52 "--automatic-resize-space"|"-ars")
53 if [ -z "${2}" ] ; then
54 echo "No parameter defining the suplementary space"
57 AUTOMATIC_RESIZE_SPACE=${2}
62 if [ -z "${2}" ] ; then
63 echo "No parameter defining the image size"
70 "--hook-script"|"-hs")
71 if [ -z "${2}" ] ; then
72 echo "No parameter defining the hook script"
75 if ! [ -x "${2}" ] ; then
76 echo "Hook script not executable"
82 "--sources.list-mirror"|"-s")
83 if [ -z "${2}" ] ; then
84 echo "No parameter defining the hook script"
87 SOURCE_LIST_MIRROR=${2}
92 if [ "${2}" = "wheezy" ] || [ "${2}" = "jessie" ] || [ "${2}" = "stretch" ] || [ "${2}" = "buster" ] ; then
97 echo "Release not recognized."
102 if [ -z "${2}" ] ; then
103 echo "No parameter defining the user login"
111 if [ -z "${2}" ] ; then
112 echo "No parameter defining the root password"
123 if [ -z "${RELEASE}" ] ; then
124 echo "Release not recognized: please specify the -r parameter."
127 if [ -z "${DEB_MIRROR}" ] ; then
128 DEB_MIRROR=http://http.debian.net/debian
130 if [ -z "${EXTRA_PACKAGES}" ] ; then
131 EXTRA_PACKAGES=bash-completion,joe,most,screen,less,vim,bzip2,nano
133 if [ -z "${SOURCE_LIST_MIRROR}" ] ; then
134 SOURCE_LIST_MIRROR=http://http.debian.net/debian
136 if [ -z "${IMAGE_SIZE}" ] ; then
139 if [ -z "${AUTOMATIC_RESIZE_SPACE}" ] ; then
140 AUTOMATIC_RESIZE_SPACE=50
142 if [ -z "${USER_LOGIN}" ] ; then
146 NEEDED_PACKAGES=sudo,adduser,locales,extlinux,openssh-server,linux-image-amd64,euca2ools,file,kbd
147 if [ "${RELEASE}" = "jessie" ] ; then
148 NEEDED_PACKAGES=${NEEDED_PACKAGES},cloud-init,cloud-utils,cloud-initramfs-growroot,dbus,libpam-systemd
150 # These are needed by cloud-init and friends, and since we don't want backports of them,
151 # but just normal packages from Wheezy, we resolve dependencies by hand, prior to using
152 # apt-get -t wheezy-backports install cloud-init cloud-utils cloud-initramfs-growroot
153 NEEDED_PACKAGES=${NEEDED_PACKAGES},python,python-paramiko,python-argparse,python-cheetah,python-configobj,python-oauth,python-software-properties,python-yaml,python-boto,python-prettytable,initramfs-tools,python-requests,acpid,acpi-support-base
156 if [ ${EXTRA} = "no" ] ; then
157 PKG_LIST=${NEEDED_PACKAGES}
159 PKG_LIST=${NEEDED_PACKAGES},${EXTRA_PACKAGES}
161 if ! [ `whoami` = "root" ] ; then
162 echo "You have to be root to run this script"
165 FILE_NAME=debian-${RELEASE}-7.0.0-3-amd64
166 AMI_NAME=${FILE_NAME}.raw
167 QCOW2_NAME=${FILE_NAME}.qcow2
172 ######################################
173 ### Prepare the HDD (format, ext.) ###
174 ######################################
177 qemu-img create ${AMI_NAME} ${IMAGE_SIZE}G
178 #dd if=/dev/null bs=1M seek=1024 of=${AMI_NAME}
180 ${PARTED} -s ${AMI_NAME} mktable msdos
181 ${PARTED} -s -a optimal ${AMI_NAME} mkpart primary ext3 1Mi 100%
182 ${PARTED} -s ${AMI_NAME} set 1 boot on
183 install-mbr ${AMI_NAME}
184 RESULT_KPARTX=`kpartx -asv ${AMI_NAME} 2>&1`
186 if echo "${RESULT_KPARTX}" | grep "^add map" ; then
187 LOOP_DEVICE=`echo ${RESULT_KPARTX} | cut -d" " -f3`
188 echo "kpartx mounted using: ${LOOP_DEVICE}"
190 echo "It seems kpartx didn't mount the image correctly: exiting."
196 [ ! -d "${MOUNT_DIR}" ] && return
198 echo "error $error, umounting $MOUNT_DIR"
199 chroot ${MOUNT_DIR} umount /proc || true
200 chroot ${MOUNT_DIR} umount /sys || true
203 kpartx -d ${AMI_NAME}
206 trap "cleanup" EXIT TERM INT
208 # We first use ext2, THEN convert to ext3, because that's so much faster this way.
209 mkfs.ext2 /dev/mapper/${LOOP_DEVICE}
211 # No fsck because of X days without checks
212 tune2fs -i 0 /dev/mapper/${LOOP_DEVICE}
214 MOUNT_DIR=`mktemp -d -t build-debimg.XXXXXX`
215 mount -o loop /dev/mapper/${LOOP_DEVICE} ${MOUNT_DIR}
216 debootstrap --verbose \
217 --include=${PKG_LIST} \
218 ${RELEASE} ${MOUNT_DIR} ${DEB_MIRROR}
220 ############################
221 ### Customize the distro ###
222 ############################
223 ### Customize: access to the VM ###
224 # # # # # # # # # # # # # # # # # #
225 # Setup default root password to what has been set on the command line
226 if [ -n "${ROOT_PASSWORD}" ] ; then
227 chroot ${MOUNT_DIR} sh -c "echo root:${ROOT_PASSWORD} | chpasswd"
230 # Otherwise, we have a huge backdoor, since the root password
231 # is always the same.
232 sed -i "s/PermitRootLogin yes/PermitRootLogin without-password/" ${MOUNT_DIR}/etc/ssh/sshd_config
234 # Add a default user which is used by cloud-init by default
235 chroot ${MOUNT_DIR} adduser --gecos Cloud-init-user --disabled-password --quiet ${USER_LOGIN}
237 # Adds the "debian" user to sudoers, since that is the way
238 # cloud-init grant access
239 mkdir -p ${MOUNT_DIR}/etc/sudoers.d
240 echo "${USER_LOGIN} ALL = NOPASSWD: ALL" >${MOUNT_DIR}/etc/sudoers.d/debian-cloud-init
241 chmod 0440 ${MOUNT_DIR}/etc/sudoers.d/debian-cloud-init
243 ### Customize: misc stuff ###
244 # # # # # # # # # # # # # # #
246 echo "# /etc/fstab: static file system information.
247 proc /proc proc nodev,noexec,nosuid 0 0
248 /dev/vda1 / ext3 errors=remount-ro 0 1
249 " > ${MOUNT_DIR}/etc/fstab
250 chroot ${MOUNT_DIR} mount /proc || true
252 echo "# disable pc speaker
253 blacklist pcspkr" >${MOUNT_DIR}/etc/modprobe.d/blacklist.conf
255 #echo "# Required for cinder hotplug
258 #" >>${MOUNT_DIR}/etc/modules
260 # Enable bash-completion by default
261 if [ ${EXTRA} = "yes" ] ; then
262 echo "# enable bash completion in interactive shells
263 if ! shopt -oq posix; then
264 if [ -f /usr/share/bash-completion/bash_completion ]; then
265 . /usr/share/bash-completion/bash_completion
266 elif [ -f /etc/bash_completion ]; then
267 . /etc/bash_completion
269 fi" >>${MOUNT_DIR}/etc/bash.bashrc
271 # No clear for the tty1 console
272 if [ "${RELEASE}" = "wheezy" ] ; then
273 sed -i "s#1:2345:respawn:/sbin/getty 38400 tty1#1:2345:respawn:/sbin/getty --noclear 38400 tty1#" ${MOUNT_DIR}/etc/inittab
275 echo ForwardToConsole=yes >> ${MOUNT_DIR}/etc/systemd/journald.conf
279 chroot ${MOUNT_DIR} apt-get install -y locales-all
282 # Turn off console blanking which is *very* annoying
283 # and increase KEYBOARD_DELAY because it can be annoying
285 sed -i s/^BLANK_TIME=.*/BLANK_TIME=0/ ${MOUNT_DIR}/etc/kbd/config
286 sed -i s/^POWERDOWN_TIME=.*/POWERDOWN_TIME=0/ ${MOUNT_DIR}/etc/kbd/config
287 sed -i 's/^[ \t#]KEYBOARD_DELAY=.*/KEYBOARD_DELAY=1000/' ${MOUNT_DIR}/etc/kbd/config
289 rm -f ${MOUNT_DIR}/etc/ssh/ssh_host_*
290 rm -f ${MOUNT_DIR}/etc/udev/rules.d/70-persistent-net.rules
291 rm -f ${MOUNT_DIR}/lib/udev/write_net_rules
293 # Setup networking (eg: DHCP by default)
294 echo "# This file describes the network interfaces available on your system
295 # and how to activate them. For more information, see interfaces(5).
297 # The loopback network interface
299 iface lo inet loopback
305 # Maybe the VM has 2 NICs?
309 # Maybe the VM has 3 NICs?
312 " > ${MOUNT_DIR}/etc/network/interfaces
314 # Setup the default hostname (will be set by cloud-init
315 # at boot time anyway)
316 echo "debian.example.com" >${MOUNT_DIR}/etc/hostname
318 # This should be a correct default everywhere
319 echo "deb ${SOURCE_LIST_MIRROR} ${RELEASE} main
320 deb-src ${SOURCE_LIST_MIRROR} ${RELEASE} main" >${MOUNT_DIR}/etc/apt/sources.list
322 if [ "${RELEASE}" = "wheezy" ] || [ "${RELEASE}" = "jessie" ] ; then
323 echo "deb ${SOURCE_LIST_MIRROR} ${RELEASE}-updates main
324 deb-src ${SOURCE_LIST_MIRROR} ${RELEASE}-updates main
325 deb http://security.debian.org/ ${RELEASE}/updates main
326 deb-src http://security.debian.org/ ${RELEASE}/updates main
327 " >>${MOUNT_DIR}/etc/apt/sources.list
330 if [ "${RELEASE}" = "wheezy" ] ; then
331 echo "deb ${SOURCE_LIST_MIRROR} wheezy-backports main
332 deb-src ${SOURCE_LIST_MIRROR} wheezy-backports main
333 " >>${MOUNT_DIR}/etc/apt/sources.list
336 chroot ${MOUNT_DIR} apt-get update
337 chroot ${MOUNT_DIR} apt-get upgrade -y
339 # Setup cloud-init, cloud-utils and cloud-initramfs-growroot
340 # These are only available from backports in Wheezy
341 if [ "${RELEASE}" = "wheezy" ] ; then
342 chroot ${MOUNT_DIR} apt-get -t wheezy-backports install cloud-init cloud-utils cloud-initramfs-growroot -y
345 # For OpenStack, we would like to use Ec2 and no other API
346 echo "# to update this file, run dpkg-reconfigure cloud-init
347 datasource_list: [ConfigDrive, Openstack, Ec2]" >${MOUNT_DIR}/etc/cloud/cloud.cfg.d/90_dpkg.cfg
349 # Needed to have automatic mounts of /dev/vdb
350 echo "mount_default_fields: [~, ~, 'auto', 'defaults,nofail', '0', '2']" >>${MOUNT_DIR}/etc/cloud/cloud.cfg
351 echo "manage_etc_hosts: true" >>${MOUNT_DIR}/etc/cloud/cloud.cfg
353 # Set the cloud init default user (required for the keypair to be put in the right home directory)
354 sed -i "s/name: debian/name: ${USER_LOGIN}/" ${MOUNT_DIR}/etc/cloud/cloud.cfg
356 # Setting-up initramfs
357 chroot ${MOUNT_DIR} update-initramfs -u
359 rm ${MOUNT_DIR}/var/cache/apt/archives/*.deb
361 ###########################
362 ### Setting-up extlinux ###
363 ###########################
364 KERNEL=`chroot ${MOUNT_DIR} find boot -name 'vmlinuz-*'`
365 RAMDISK=`chroot ${MOUNT_DIR} find boot -name 'initrd.img-*'`
366 UUID=`blkid -o value -s UUID /dev/mapper/${LOOP_DEVICE}`
367 mkdir -p ${MOUNT_DIR}/boot/extlinux
372 append initrd=${RAMDISK} root=/dev/vda1 console=tty0 console=ttyS0,115200 ro quiet" > ${MOUNT_DIR}/boot/extlinux/extlinux.conf
373 #append initrd=${RAMDISK} root=/dev/vda1 ro quiet console=ttyS0" > ${MOUNT_DIR}/boot/extlinux/extlinux.conf
374 cp ${MOUNT_DIR}/boot/extlinux/extlinux.conf ${MOUNT_DIR}/extlinux.conf
375 extlinux --install ${MOUNT_DIR}
376 #chroot ${MOUNT_DIR} extlinux-update
381 if [ -x ${HOOK_SCRIPT} ] ; then
382 export BODI_CHROOT_PATH=${MOUNT_DIR}
383 export BODI_RELEASE=${RELEASE}
387 ##########################
388 ### Unmount everything ###
389 ##########################
392 # refine cleanup everything was ok
396 chroot ${MOUNT_DIR} umount /proc || true
398 # Run FSCK so that resize can work
399 tune2fs -j /dev/mapper/${LOOP_DEVICE}
400 fsck.ext3 -f /dev/mapper/${LOOP_DEVICE} || true
402 if [ "${AUTOMATIC_RESIZE}" = "yes" ] ; then
403 resize2fs -M /dev/mapper/${LOOP_DEVICE}
404 FS_BLOCKS=`tune2fs -l /dev/mapper/${LOOP_DEVICE} | awk '/Block count/{print $3}'`
405 WANTED_SIZE=`expr $FS_BLOCKS '*' 4 '/' 1024 + ${AUTOMATIC_RESIZE_SPACE}` # Add ${AUTOMATIC_RESIZE_SPACE}M
406 resize2fs /dev/mapper/${LOOP_DEVICE} ${WANTED_SIZE}M
408 FINAL_FS_BLOCKS=`tune2fs -l /dev/mapper/${LOOP_DEVICE} | awk '/Block count/{print $3}'`
409 FINAL_IMG_SIZE=`expr '(' $FINAL_FS_BLOCKS + 258 ')' '*' 4 '/' 1024` # some blocks for mbr and multiple block size (4k)
412 kpartx -d ${AMI_NAME}
415 if [ "${AUTOMATIC_RESIZE}" = "yes" ] ; then
416 # Rebuild a smaller partition table
417 parted -s ${AMI_NAME} rm 1
418 parted -s ${AMI_NAME} mkpart primary ext3 1Mi ${FINAL_IMG_SIZE}Mi
419 parted -s ${AMI_NAME} set 1 boot on
421 # Add 2M for the 1M at the beginning of the partition and some additionnal space
422 truncate -s `expr 3 + ${FINAL_IMG_SIZE}`M ${AMI_NAME}
423 install-mbr ${AMI_NAME}
426 QEMU_VERSION=`qemu-img --help | head -n 1 | cut -d" " -f3 | cut -d"," -f1`
427 if dpkg --compare-versions ${QEMU_VERSION} gt 1.0 ; then
428 OTHER_QEMU_IMG_OPTIONS=" -o compat=0.10"
430 OTHER_QEMU_IMG_OPTIONS=""
433 qemu-img convert -c -f raw ${AMI_NAME}${OTHER_QEMU_IMG_OPTIONS} -O qcow2 ${QCOW2_NAME}