Created
May 11, 2020 20:16
-
-
Save pgmartinez/c5c8ae2ecf7d21aff53fdaadef4b1679 to your computer and use it in GitHub Desktop.
build devuan openstack image
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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