Skip to content

Instantly share code, notes, and snippets.

@pgmartinez
Created May 11, 2020 20:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pgmartinez/c5c8ae2ecf7d21aff53fdaadef4b1679 to your computer and use it in GitHub Desktop.
Save pgmartinez/c5c8ae2ecf7d21aff53fdaadef4b1679 to your computer and use it in GitHub Desktop.
build devuan openstack image
#!/bin/sh
set -e
# Parse input parameters
usage() {
echo "Usage: $0 --release|-r <ascii|beowulf> [options]
Options are:
--minimal|-m
--debootstrap-url|-u <debootstrap-mirror> (default: http://deb.devuan.org/merged)
--sources.list-mirror|-s <source-list-mirror> (default: http://deb.devuan.org/merged)
--extra-packages|-e <package>,<package>,...
--hook-script|-hs <hook-script>
--image-size|-is <image-size> (default: 2G)
--automatic-resize|-ar
--automatic-resize-space|-ars <suplementary-space> (default: 50M)
--login|-l <userlogin> (default: devuan)
--password|-p <root-password> (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-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},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
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.0-1-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
install-mbr ${AMI_NAME} --force
RESULT_KPARTX=`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"
chroot ${MOUNT_DIR} umount /proc || true
chroot ${MOUNT_DIR} umount /sys || true
umount ${MOUNT_DIR}
## rmdir ${MOUNT_DIR}
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
mkfs.ext4 /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 --verbose \
--include=${PKG_LIST} \
${RELEASE} ${MOUNT_DIR} ${DEB_MIRROR}
############################
### 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
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
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
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
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
chroot ${MOUNT_DIR} apt-get update
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
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
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
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}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment