5 # Parse input parameters
7 echo "Usage: $0 --release|-r <jessie|wheezy> [options]
10 --debootstrap-url|-u <debootstrap-mirror> (default: http://httpredir.debian.org/debian)
11 --sources.list-mirror|-s <source-list-mirror> (default: http://httpredir.debian.org/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."
142 if [ -z "${DEB_MIRROR}" ] ; then
143 DEB_MIRROR=http://httpredir.debian.org/debian
145 if [ -z "${EXTRA_PACKAGES}" ] ; then
146 EXTRA_PACKAGES=bash-completion,joe,most,screen,less,vim,bzip2,nano
148 if [ -z "${SOURCE_LIST_MIRROR}" ] ; then
149 SOURCE_LIST_MIRROR=http://httpredir.debian.org/debian
151 if [ -z "${IMAGE_SIZE}" ] ; then
154 if [ -z "${AUTOMATIC_RESIZE_SPACE}" ] ; then
155 AUTOMATIC_RESIZE_SPACE=50
157 if [ -z "${USER_LOGIN}" ] ; then
161 NEEDED_PACKAGES=sudo,adduser,locales,extlinux,openssh-server,linux-image-amd64,euca2ools,file,kbd,aptitude
162 if [ "${RELEASE}" = "wheezy" ] ; then
163 # These are needed by cloud-init and friends, and since we don't want backports of them,
164 # but just normal packages from Wheezy, we resolve dependencies by hand, prior to using
165 # apt-get -t wheezy-backports install cloud-init cloud-utils cloud-initramfs-growroot
166 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
168 NEEDED_PACKAGES=${NEEDED_PACKAGES},cloud-init,cloud-utils,cloud-initramfs-growroot,dbus,libpam-systemd
171 if [ ${EXTRA} = "no" ] ; then
172 PKG_LIST=${NEEDED_PACKAGES}
174 PKG_LIST=${NEEDED_PACKAGES},${EXTRA_PACKAGES}
176 if ! [ `whoami` = "root" ] ; then
177 echo "You have to be root to run this script"
180 FILE_NAME=debian-${RELEASE}-${RELEASE_NUM}.0.0-1-amd64
181 AMI_NAME=${FILE_NAME}.raw
182 QCOW2_NAME=${FILE_NAME}.qcow2
187 ######################################
188 ### Prepare the HDD (format, ext.) ###
189 ######################################
192 qemu-img create ${AMI_NAME} ${IMAGE_SIZE}G
193 #dd if=/dev/null bs=1M seek=1024 of=${AMI_NAME}
195 ${PARTED} -s ${AMI_NAME} mktable msdos
196 ${PARTED} -s -a optimal ${AMI_NAME} mkpart primary ext3 1Mi 100%
197 ${PARTED} -s ${AMI_NAME} set 1 boot on
198 install-mbr ${AMI_NAME}
199 RESULT_KPARTX=`kpartx -asv ${AMI_NAME} 2>&1`
201 if echo "${RESULT_KPARTX}" | grep "^add map" ; then
202 LOOP_DEVICE=`echo ${RESULT_KPARTX} | cut -d" " -f3`
203 echo "kpartx mounted using: ${LOOP_DEVICE}"
205 echo "It seems kpartx didn't mount the image correctly: exiting."
211 [ ! -d "${MOUNT_DIR}" ] && return
213 echo "error $error, umounting $MOUNT_DIR"
214 chroot ${MOUNT_DIR} umount /proc || true
215 chroot ${MOUNT_DIR} umount /sys || true
218 kpartx -d ${AMI_NAME}
221 trap "cleanup" EXIT TERM INT
223 # We first use ext2, THEN convert to ext3, because that's so much faster this way.
224 mkfs.ext2 /dev/mapper/${LOOP_DEVICE}
226 # No fsck because of X days without checks
227 tune2fs -i 0 /dev/mapper/${LOOP_DEVICE}
229 MOUNT_DIR=`mktemp -d -t build-debimg.XXXXXX`
230 mount -o loop /dev/mapper/${LOOP_DEVICE} ${MOUNT_DIR}
231 debootstrap --verbose \
232 --include=${PKG_LIST} \
233 ${RELEASE} ${MOUNT_DIR} ${DEB_MIRROR}
235 ############################
236 ### Customize the distro ###
237 ############################
238 ### Customize: access to the VM ###
239 # # # # # # # # # # # # # # # # # #
240 # Setup default root password to what has been set on the command line
241 if [ -n "${ROOT_PASSWORD}" ] ; then
242 chroot ${MOUNT_DIR} sh -c "echo root:${ROOT_PASSWORD} | chpasswd"
245 # Otherwise, we have a huge backdoor, since the root password
246 # is always the same.
247 sed -i "s/PermitRootLogin yes/PermitRootLogin without-password/" ${MOUNT_DIR}/etc/ssh/sshd_config
249 # Add a default user which is used by cloud-init by default
250 chroot ${MOUNT_DIR} adduser --gecos Cloud-init-user --disabled-password --quiet ${USER_LOGIN}
252 # Adds the "debian" user to sudoers, since that is the way
253 # cloud-init grant access
254 mkdir -p ${MOUNT_DIR}/etc/sudoers.d
255 echo "${USER_LOGIN} ALL = NOPASSWD: ALL" >${MOUNT_DIR}/etc/sudoers.d/debian-cloud-init
256 chmod 0440 ${MOUNT_DIR}/etc/sudoers.d/debian-cloud-init
258 ### Customize: misc stuff ###
259 # # # # # # # # # # # # # # #
261 echo "# /etc/fstab: static file system information.
262 proc /proc proc nodev,noexec,nosuid 0 0
263 /dev/vda1 / ext3 errors=remount-ro 0 1
264 " > ${MOUNT_DIR}/etc/fstab
265 chroot ${MOUNT_DIR} mount /proc || true
267 echo "# disable pc speaker
268 blacklist pcspkr" >${MOUNT_DIR}/etc/modprobe.d/blacklist.conf
270 #echo "# Required for cinder hotplug
273 #" >>${MOUNT_DIR}/etc/modules
275 # Enable bash-completion by default
276 if [ ${EXTRA} = "yes" ] ; then
277 echo "# enable bash completion in interactive shells
278 if ! shopt -oq posix; then
279 if [ -f /usr/share/bash-completion/bash_completion ]; then
280 . /usr/share/bash-completion/bash_completion
281 elif [ -f /etc/bash_completion ]; then
282 . /etc/bash_completion
284 fi" >>${MOUNT_DIR}/etc/bash.bashrc
286 # No clear for the tty1 console
287 if [ "${RELEASE}" = "wheezy" ] ; then
288 sed -i "s#1:2345:respawn:/sbin/getty 38400 tty1#1:2345:respawn:/sbin/getty --noclear 38400 tty1#" ${MOUNT_DIR}/etc/inittab
290 echo ForwardToConsole=yes >> ${MOUNT_DIR}/etc/systemd/journald.conf
294 chroot ${MOUNT_DIR} apt-get install -y locales-all
297 # Turn off console blanking which is *very* annoying
298 # and increase KEYBOARD_DELAY because it can be annoying
300 sed -i s/^BLANK_TIME=.*/BLANK_TIME=0/ ${MOUNT_DIR}/etc/kbd/config
301 sed -i s/^POWERDOWN_TIME=.*/POWERDOWN_TIME=0/ ${MOUNT_DIR}/etc/kbd/config
302 sed -i 's/^[ \t#]KEYBOARD_DELAY=.*/KEYBOARD_DELAY=1000/' ${MOUNT_DIR}/etc/kbd/config
304 rm -f ${MOUNT_DIR}/etc/ssh/ssh_host_*
305 rm -f ${MOUNT_DIR}/etc/udev/rules.d/70-persistent-net.rules
306 rm -f ${MOUNT_DIR}/lib/udev/write_net_rules
308 # Setup networking (eg: DHCP by default)
309 echo "# This file describes the network interfaces available on your system
310 # and how to activate them. For more information, see interfaces(5).
312 # The loopback network interface
314 iface lo inet loopback
320 # Maybe the VM has 2 NICs?
324 # Maybe the VM has 3 NICs?
327 " > ${MOUNT_DIR}/etc/network/interfaces
329 # Setup the default hostname (will be set by cloud-init
330 # at boot time anyway)
331 echo "debian.example.com" >${MOUNT_DIR}/etc/hostname
333 # This should be a correct default everywhere
334 echo "deb ${SOURCE_LIST_MIRROR} ${RELEASE} main
335 deb-src ${SOURCE_LIST_MIRROR} ${RELEASE} main" >${MOUNT_DIR}/etc/apt/sources.list
337 if [ "${RELEASE}" = "wheezy" ] || [ "${RELEASE}" = "jessie" ] ; then
338 echo "deb ${SOURCE_LIST_MIRROR} ${RELEASE}-updates main
339 deb-src ${SOURCE_LIST_MIRROR} ${RELEASE}-updates main
340 deb http://security.debian.org/ ${RELEASE}/updates main
341 deb-src http://security.debian.org/ ${RELEASE}/updates main
342 " >>${MOUNT_DIR}/etc/apt/sources.list
345 if [ "${RELEASE}" = "wheezy" ] ; then
346 echo "deb ${SOURCE_LIST_MIRROR} wheezy-backports main
347 deb-src ${SOURCE_LIST_MIRROR} wheezy-backports main
348 " >>${MOUNT_DIR}/etc/apt/sources.list
351 chroot ${MOUNT_DIR} apt-get update
352 chroot ${MOUNT_DIR} apt-get upgrade -y
354 # Setup cloud-init, cloud-utils and cloud-initramfs-growroot
355 # These are only available from backports in Wheezy
356 if [ "${RELEASE}" = "wheezy" ] ; then
357 chroot ${MOUNT_DIR} apt-get -t wheezy-backports install cloud-init cloud-utils cloud-initramfs-growroot -y
360 # For OpenStack, we would like to use Ec2 and no other API
361 echo "# to update this file, run dpkg-reconfigure cloud-init
362 datasource_list: [ConfigDrive, Openstack, Ec2]" >${MOUNT_DIR}/etc/cloud/cloud.cfg.d/90_dpkg.cfg
364 # Needed to have automatic mounts of /dev/vdb
365 echo "mount_default_fields: [~, ~, 'auto', 'defaults,nofail', '0', '2']" >>${MOUNT_DIR}/etc/cloud/cloud.cfg
366 echo "manage_etc_hosts: true" >>${MOUNT_DIR}/etc/cloud/cloud.cfg
368 # Set the cloud init default user (required for the keypair to be put in the right home directory)
369 sed -i "s/name: debian/name: ${USER_LOGIN}/" ${MOUNT_DIR}/etc/cloud/cloud.cfg
371 # Setting-up initramfs
372 chroot ${MOUNT_DIR} update-initramfs -u
374 rm ${MOUNT_DIR}/var/cache/apt/archives/*.deb
376 # Set console for emergency and rescue shells
377 SYSTEMD_DIR="${MOUNT_DIR}/etc/systemd/system/"
378 for service in emergency.service rescue.service ; do
379 mkdir "${SYSTEMD_DIR}/${service}.d"
382 ExecStart=-/bin/sh -c "/sbin/sulogin /dev/tty0; /bin/systemctl --fail --no-block default"' > "${SYSTEMD_DIR}/${service}.d/console.conf"
385 ###########################
386 ### Setting-up extlinux ###
387 ###########################
388 UUID=`blkid -o value -s UUID /dev/mapper/${LOOP_DEVICE}`
389 mkdir -p ${MOUNT_DIR}/boot/extlinux
394 append initrd=/initrd.img root=/dev/vda1 biosdevname=0 net.ifnames=0 console=tty0 console=ttyS0,115200 ro" > ${MOUNT_DIR}/boot/extlinux/extlinux.conf
395 extlinux --install ${MOUNT_DIR}/boot/extlinux
400 if [ -x ${HOOK_SCRIPT} ] ; then
401 export BODI_CHROOT_PATH=${MOUNT_DIR}
402 export BODI_RELEASE=${RELEASE}
406 ##########################
407 ### Unmount everything ###
408 ##########################
411 # refine cleanup everything was ok
416 chroot ${MOUNT_DIR} umount /proc || true
418 # Run FSCK so that resize can work
419 tune2fs -j /dev/mapper/${LOOP_DEVICE}
420 fsck.ext3 -f /dev/mapper/${LOOP_DEVICE} || true
422 if [ "${AUTOMATIC_RESIZE}" = "yes" ] ; then
423 resize2fs -M /dev/mapper/${LOOP_DEVICE}
424 FS_BLOCKS=`tune2fs -l /dev/mapper/${LOOP_DEVICE} | awk '/Block count/{print $3}'`
425 WANTED_SIZE=`expr $FS_BLOCKS '*' 4 '/' 1024 + ${AUTOMATIC_RESIZE_SPACE}` # Add ${AUTOMATIC_RESIZE_SPACE}M
426 resize2fs /dev/mapper/${LOOP_DEVICE} ${WANTED_SIZE}M
428 FINAL_FS_BLOCKS=`tune2fs -l /dev/mapper/${LOOP_DEVICE} | awk '/Block count/{print $3}'`
429 FINAL_IMG_SIZE=`expr '(' $FINAL_FS_BLOCKS + 258 ')' '*' 4 '/' 1024` # some blocks for mbr and multiple block size (4k)
433 kpartx -d ${AMI_NAME}
436 if [ "${AUTOMATIC_RESIZE}" = "yes" ] ; then
437 # Rebuild a smaller partition table
438 parted -s ${AMI_NAME} rm 1
439 parted -s ${AMI_NAME} mkpart primary ext3 1Mi ${FINAL_IMG_SIZE}Mi
440 parted -s ${AMI_NAME} set 1 boot on
442 # Add 2M for the 1M at the beginning of the partition and some additionnal space
443 truncate -s `expr 3 + ${FINAL_IMG_SIZE}`M ${AMI_NAME}
444 install-mbr ${AMI_NAME}
447 QEMU_VERSION=`qemu-img --help | head -n 1 | cut -d" " -f3 | cut -d"," -f1`
448 if dpkg --compare-versions ${QEMU_VERSION} gt 1.0 ; then
449 OTHER_QEMU_IMG_OPTIONS=" -o compat=0.10"
451 OTHER_QEMU_IMG_OPTIONS=""
454 qemu-img convert -c -f raw ${AMI_NAME}${OTHER_QEMU_IMG_OPTIONS} -O qcow2 ${QCOW2_NAME}