#!/bin/sh set -e DATE_WITH_TIME=`date "+%Y%m%d-%H%M%S"` # Parse input parameters usage() { echo "Usage: $0 --release|-r [options] Options are: --minimal|-m --debootstrap-url|-u (default: http://deb.devuan.org/merged) --sources.list-mirror|-s (default: http://deb.devuan.org/merged) --extra-packages|-e ,,... --hook-script|-hs --image-size|-is (default: 2G) --automatic-resize|-ar --automatic-resize-space|-ars (default: 50M) --login|-l (default: devuan) --password|-p (dangerous option: avoid it if possible) For more info: man $0" exit 1 } EXTRA=yes for i in $@ ; do case "${1}" in "--extra-packages"|"-e") if [ -z "${2}" ] ; then echo "No parameter defining the extra packages" usage fi EXTRA_PACKAGES=${2} shift shift ;; "--debootstrap-url"|"-u") if [ -z "${2}" ] ; then echo "No parameter defining the debootstrap URL" usage fi DEB_MIRROR=${2} shift shift ;; "--minimal"|"-m") EXTRA=no shift ;; "--automatic-resize"|"-ar") AUTOMATIC_RESIZE=yes shift ;; "--automatic-resize-space"|"-ars") if [ -z "${2}" ] ; then echo "No parameter defining the suplementary space" usage fi AUTOMATIC_RESIZE_SPACE=${2} shift shift ;; "--image-size"|"-is") if [ -z "${2}" ] ; then echo "No parameter defining the image size" usage fi IMAGE_SIZE=${2} shift shift ;; "--hook-script"|"-hs") if [ -z "${2}" ] ; then echo "No parameter defining the hook script" usage fi if ! [ -x "${2}" ] ; then echo "Hook script not executable" fi HOOK_SCRIPT=${2} shift shift ;; "--sources.list-mirror"|"-s") if [ -z "${2}" ] ; then echo "No parameter defining the hook script" usage fi SOURCE_LIST_MIRROR=${2} shift shift ;; "--release"|"-r") if [ "${2}" = "beowulf" ] || [ "${2}" = "ascii" ] || [ "${2}" = "stretch" ] || [ "${2}" = "buster" ] ; then RELEASE=${2} shift shift else echo "Release not recognized." usage fi ;; "--login"|"-l") if [ -z "${2}" ] ; then echo "No parameter defining the user login" usage fi USER_LOGIN=${2} shift shift ;; "--password"|"-p") if [ -z "${2}" ] ; then echo "No parameter defining the root password" fi ROOT_PASSWORD=${2} shift shift ;; *) ;; esac done if [ -z "${RELEASE}" ] ; then echo "Release not recognized: please specify the -r parameter." usage fi case "${RELEASE}" in "beowulf") RELEASE_NUM=3 ;; "ascii") RELEASE_NUM=2 ;; "jessie") RELEASE_NUM=1 ;; esac if [ -z "${DEB_MIRROR}" ] ; then # DEB_MIRROR=http://httpredir.devuan.org/devuan DEB_MIRROR=http://deb.devuan.org/merged fi if [ -z "${EXTRA_PACKAGES}" ] ; then EXTRA_PACKAGES=bash-completion,joe,most,screen,less,vim,bzip2,nano fi if [ -z "${SOURCE_LIST_MIRROR}" ] ; then SOURCE_LIST_MIRROR=http://deb.devuan.org/merged fi if [ -z "${IMAGE_SIZE}" ] ; then IMAGE_SIZE=2 fi if [ -z "${AUTOMATIC_RESIZE_SPACE}" ] ; then AUTOMATIC_RESIZE_SPACE=50 fi if [ -z "${USER_LOGIN}" ] ; then USER_LOGIN=devuan fi NEEDED_PACKAGES=sudo,adduser,locales,extlinux,openssh-server,linux-image-cloud-amd64,euca2ools,file,kbd,aptitude,python3-cffi-backend if [ "${RELEASE}" = "beowulf" ] ; then # These are needed by cloud-init and friends, and since we don't want backports of them, # but just normal packages from Wheezy, we resolve dependencies by hand, prior to using # apt-get -t beowulf-backports install cloud-init cloud-utils cloud-initramfs-growroot ###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 ## NEEDED_PACKAGES=${NEEDED_PACKAGES},runit,runit-init,python,python-paramiko,python-cheetah,python-configobj,python-oauth,python-yaml,python-boto,python-prettytable,initramfs-tools,python-requests,acpid,acpi-support-base ##else ## NEEDED_PACKAGES=${NEEDED_PACKAGES},cloud-init,cloud-utils,cloud-initramfs-growroot,dbus NEEDED_PACKAGES=${NEEDED_PACKAGES},initramfs-tools fi if [ ${EXTRA} = "no" ] ; then PKG_LIST=${NEEDED_PACKAGES} else PKG_LIST=${NEEDED_PACKAGES},${EXTRA_PACKAGES} fi if ! [ `whoami` = "root" ] ; then echo "You have to be root to run this script" exit 1 fi FILE_NAME=devuan-${RELEASE}-${RELEASE_NUM}.0.1.${DATE_WITH_TIME}-amd64 AMI_NAME=${FILE_NAME}.raw QCOW2_NAME=${FILE_NAME}.qcow2 rm -f ${AMI_NAME} set -x ###################################### ### Prepare the HDD (format, ext.) ### ###################################### PARTED=/sbin/parted rm -f $AMI_NAME qemu-img create ${AMI_NAME} ${IMAGE_SIZE}G #dd if=/dev/null bs=1M seek=1024 of=${AMI_NAME} ${PARTED} -s ${AMI_NAME} mktable msdos ${PARTED} -s -a optimal ${AMI_NAME} mkpart primary ext4 1Mi 100% ${PARTED} -s ${AMI_NAME} set 1 boot on /sbin/install-mbr ${AMI_NAME} --force RESULT_KPARTX=`/sbin/kpartx -asv ${AMI_NAME} 2>&1` if echo "${RESULT_KPARTX}" | grep "^add map" ; then LOOP_DEVICE=`echo ${RESULT_KPARTX} | cut -d" " -f3` echo "kpartx mounted using: ${LOOP_DEVICE}" else echo "It seems kpartx didn't mount the image correctly: exiting." exit 1 fi cleanup(){ error=$? [ ! -d "${MOUNT_DIR}" ] && return echo echo "error $error, umounting $MOUNT_DIR" /usr/sbin/chroot ${MOUNT_DIR} umount /proc || true /usr/sbin/chroot ${MOUNT_DIR} umount /sys || true umount ${MOUNT_DIR} ## rmdir ${MOUNT_DIR} /sbin/kpartx -d ${AMI_NAME} exit $error } trap "cleanup" EXIT TERM INT # We first use ext2, THEN convert to ext4, because that's so much faster this way. ##mkfs.ext2 /dev/mapper/${LOOP_DEVICE} # No way, create ext4 filesystem at once /sbin/mkfs.ext4 /dev/mapper/${LOOP_DEVICE} # Create XFS filesystem at once #/sbin/mkfs.xfs -f /dev/mapper/${LOOP_DEVICE} # No fsck because of X days without checks #tune2fs -i 0 /dev/mapper/${LOOP_DEVICE} MOUNT_DIR=`mktemp -d -t build-debimg.XXXXXX` mount -o loop /dev/mapper/${LOOP_DEVICE} ${MOUNT_DIR} debootstrap --arch=amd64 --verbose --keyring=/etc/apt/trusted.gpg --include=${PKG_LIST} ${RELEASE} ${MOUNT_DIR} ${DEB_MIRROR} ##/usr/sbin/debootstrap --arch=amd64 --verbose --keyring=devuan-archive-keyring.gpg \ ############################ ### Customize the distro ### ############################ ### Customize: access to the VM ### # # # # # # # # # # # # # # # # # # # Setup default root password to what has been set on the command line if [ -n "${ROOT_PASSWORD}" ] ; then /usr/sbin/chroot ${MOUNT_DIR} sh -c "echo root:${ROOT_PASSWORD} | chpasswd" fi # Otherwise, we have a huge backdoor, since the root password # is always the same. sed -i "s/PermitRootLogin yes/PermitRootLogin without-password/" ${MOUNT_DIR}/etc/ssh/sshd_config # Add a default user which is used by cloud-init by default /usr/sbin/chroot ${MOUNT_DIR} adduser --gecos Cloud-init-user --disabled-password --quiet ${USER_LOGIN} # Adds the "devuan" user to sudoers, since that is the way # cloud-init grant access mkdir -p ${MOUNT_DIR}/etc/sudoers.d echo "${USER_LOGIN} ALL = NOPASSWD: ALL" >${MOUNT_DIR}/etc/sudoers.d/devuan-cloud-init chmod 0440 ${MOUNT_DIR}/etc/sudoers.d/devuan-cloud-init ### Customize: misc stuff ### # # # # # # # # # # # # # # # # Setup fstab echo "# /etc/fstab: static file system information. proc /proc proc nodev,noexec,nosuid 0 0 /dev/vda1 / ext4 errors=remount-ro 0 1 " > ${MOUNT_DIR}/etc/fstab /usr/sbin/chroot ${MOUNT_DIR} mount /proc || true echo "# disable pc speaker blacklist pcspkr" >${MOUNT_DIR}/etc/modprobe.d/blacklist.conf #echo "# Required for cinder hotplug #acpiphp #pci_hotplug #" >>${MOUNT_DIR}/etc/modules # Enable bash-completion by default if [ ${EXTRA} = "yes" ] ; then echo "# enable bash completion in interactive shells if ! shopt -oq posix; then if [ -f /usr/share/bash-completion/bash_completion ]; then . /usr/share/bash-completion/bash_completion elif [ -f /etc/bash_completion ]; then . /etc/bash_completion fi fi" >>${MOUNT_DIR}/etc/bash.bashrc # No clear for the tty1 console if [ "${RELEASE}" = "beowulf" ] ; then sed -i "s#1:2345:respawn:/sbin/getty 38400 tty1#1:2345:respawn:/sbin/getty --noclear 38400 tty1#" ${MOUNT_DIR}/etc/inittab else echo ForwardToConsole=yes >> ${MOUNT_DIR}/etc/systemd/journald.conf fi /usr/sbin/chroot ${MOUNT_DIR} apt-get install -y locales-all fi # Turn off console blanking which is *very* annoying # and increase KEYBOARD_DELAY because it can be annoying # over network. ## sed -i s/^BLANK_TIME=.*/BLANK_TIME=0/ ${MOUNT_DIR}/etc/kbd/config ## sed -i s/^POWERDOWN_TIME=.*/POWERDOWN_TIME=0/ ${MOUNT_DIR}/etc/kbd/config ## sed -i 's/^[ \t#]KEYBOARD_DELAY=.*/KEYBOARD_DELAY=1000/' ${MOUNT_DIR}/etc/kbd/config # ^ This kbd config does not exist in Devuan Ascii rm -f ${MOUNT_DIR}/etc/ssh/ssh_host_* rm -f ${MOUNT_DIR}/etc/udev/rules.d/70-persistent-net.rules rm -f ${MOUNT_DIR}/lib/udev/write_net_rules # Setup networking (eg: DHCP by default) echo "# This file describes the network interfaces available on your system # and how to activate them. For more information, see interfaces(5). # The loopback network interface auto lo iface lo inet loopback # The normal eth0 auto eth0 iface eth0 inet dhcp # Maybe the VM has 2 NICs? allow-hotplug eth1 iface eth1 inet dhcp # Maybe the VM has 3 NICs? allow-hotplug eth2 iface eth2 inet dhcp " > ${MOUNT_DIR}/etc/network/interfaces # Setup the default hostname (will be set by cloud-init # at boot time anyway) echo "devuan.example.com" >${MOUNT_DIR}/etc/hostname # This should be a correct default everywhere echo "deb ${SOURCE_LIST_MIRROR} ${RELEASE} main deb-src ${SOURCE_LIST_MIRROR} ${RELEASE} main" >${MOUNT_DIR}/etc/apt/sources.list if [ "${RELEASE}" = "beowulf" ] || [ "${RELEASE}" = "ascii" ] ; then echo "deb ${SOURCE_LIST_MIRROR} ${RELEASE}-updates main deb-src ${SOURCE_LIST_MIRROR} ${RELEASE}-updates main deb http://security.devuan.org/ ${RELEASE}/updates main deb-src http://security.devuan.org/ ${RELEASE}/updates main " >>${MOUNT_DIR}/etc/apt/sources.list fi #if [ "${RELEASE}" = "beowulf" ] ; then # echo "deb ${SOURCE_LIST_MIRROR} beowulf-backports main #deb-src ${SOURCE_LIST_MIRROR} beowulf-backports main #" >>${MOUNT_DIR}/etc/apt/sources.list #fi /usr/sbin/chroot ${MOUNT_DIR} apt-get update /usr/sbin/chroot ${MOUNT_DIR} apt-get upgrade -y # Setup cloud-init, cloud-utils and cloud-initramfs-growroot # These are only available from backports in Wheezy #if [ "${RELEASE}" = "beowulf" ] ; then # /usr/sbin/chroot ${MOUNT_DIR} apt-get -t beowulf-backports install cloud-init cloud-utils cloud-initramfs-growroot -y #fi # For OpenStack, we would like to use Ec2 and no other API #echo "# to update this file, run dpkg-reconfigure cloud-init #datasource_list: [ConfigDrive, Openstack, Ec2]" >${MOUNT_DIR}/etc/cloud/cloud.cfg.d/90_dpkg.cfg # Needed to have automatic mounts of /dev/vdb #echo "mount_default_fields: [~, ~, 'auto', 'defaults,nofail', '0', '2']" >>${MOUNT_DIR}/etc/cloud/cloud.cfg #echo "manage_etc_hosts: true" >>${MOUNT_DIR}/etc/cloud/cloud.cfg # Set the cloud init default user (required for the keypair to be put in the right home directory) #sed -i "s/name: devuan/name: ${USER_LOGIN}/" ${MOUNT_DIR}/etc/cloud/cloud.cfg # Setting-up initramfs /usr/sbin/chroot ${MOUNT_DIR} update-initramfs -u rm ${MOUNT_DIR}/var/cache/apt/archives/*.deb # Set console for emergency and rescue shells #SYSTEMD_DIR="${MOUNT_DIR}/etc/systemd/system/" #for service in emergency.service rescue.service ; do # mkdir "${SYSTEMD_DIR}/${service}.d" # echo '[Service] #ExecStart= #ExecStart=-/bin/sh -c "/sbin/sulogin /dev/tty0; /bin/systemctl --fail --no-block default"' > "${SYSTEMD_DIR}/${service}.d/console.conf" #done ########################### ### Setting-up extlinux ### ########################### UUID=`blkid -o value -s UUID /dev/mapper/${LOOP_DEVICE}` mkdir -p ${MOUNT_DIR}/boot/extlinux echo "default linux timeout 1 label linux kernel /vmlinuz append initrd=/initrd.img root=/dev/vda1 console=tty0 console=ttyS0,115200 ro" > ${MOUNT_DIR}/boot/extlinux/extlinux.conf extlinux --install ${MOUNT_DIR}/boot/extlinux ################### ### HOOK SCRIPT ### ################### if [ -x ${HOOK_SCRIPT} ] ; then export BODI_CHROOT_PATH=${MOUNT_DIR} export BODI_RELEASE=${RELEASE} ${HOOK_SCRIPT} fi ########################## ### Unmount everything ### ########################## cleanup(){ # refine cleanup everything was ok echo "Finished." } sync /usr/sbin/chroot ${MOUNT_DIR} umount /proc || true umount ${MOUNT_DIR} # Run FSCK so that resize can work tune2fs -j /dev/mapper/${LOOP_DEVICE} fsck.ext4 -f /dev/mapper/${LOOP_DEVICE} || true if [ "${AUTOMATIC_RESIZE}" = "yes" ] ; then resize2fs -M /dev/mapper/${LOOP_DEVICE} FS_BLOCKS=`tune2fs -l /dev/mapper/${LOOP_DEVICE} | awk '/Block count/{print $3}'` WANTED_SIZE=`expr $FS_BLOCKS '*' 4 '/' 1024 + ${AUTOMATIC_RESIZE_SPACE}` # Add ${AUTOMATIC_RESIZE_SPACE}M resize2fs /dev/mapper/${LOOP_DEVICE} ${WANTED_SIZE}M FINAL_FS_BLOCKS=`tune2fs -l /dev/mapper/${LOOP_DEVICE} | awk '/Block count/{print $3}'` FINAL_IMG_SIZE=`expr '(' $FINAL_FS_BLOCKS + 258 ')' '*' 4 '/' 1024` # some blocks for mbr and multiple block size (4k) fi sync kpartx -d ${AMI_NAME} ## Do not remove build directory yet: we'll use it for debug ## rmdir ${MOUNT_DIR} if [ "${AUTOMATIC_RESIZE}" = "yes" ] ; then # Rebuild a smaller partition table parted -s ${AMI_NAME} rm 1 parted -s ${AMI_NAME} mkpart primary ext4 1Mi ${FINAL_IMG_SIZE}Mi parted -s ${AMI_NAME} set 1 boot on # Add 2M for the 1M at the beginning of the partition and some additionnal space truncate -s `expr 3 + ${FINAL_IMG_SIZE}`M ${AMI_NAME} install-mbr ${AMI_NAME} --force fi QEMU_VERSION=`qemu-img --help | head -n 1 | cut -d" " -f3 | cut -d"," -f1` if dpkg --compare-versions ${QEMU_VERSION} gt 1.0 ; then OTHER_QEMU_IMG_OPTIONS=" -o compat=0.10" else OTHER_QEMU_IMG_OPTIONS="" fi qemu-img convert -c -f raw ${AMI_NAME}${OTHER_QEMU_IMG_OPTIONS} -O qcow2 ${QCOW2_NAME}