From fb6fa99c0da96a45d458b2ffa0b9b2fe8890ac36 Mon Sep 17 00:00:00 2001 From: VG Date: Sun, 6 Nov 2016 17:32:20 +0100 Subject: move to a subdir since this repo will have other debootstrap scripts --- build-grub2-instructions.txt | 7 - extract-kernel.py | 30 --- make-vm-debootstrap | 392 ----------------------------- make-vm-debootstrap-efi.py | 365 --------------------------- make-vm-debootstrap.py.orig | 331 ------------------------ make_vm_debootstrap.py | 299 ---------------------- make_vm_debootstrap_armhf.py | 11 - qemu-headless/build-grub2-instructions.txt | 7 + qemu-headless/extract-kernel.py | 30 +++ qemu-headless/make-vm-debootstrap-efi.py | 365 +++++++++++++++++++++++++++ qemu-headless/make-vm-debootstrap.py.orig | 331 ++++++++++++++++++++++++ qemu-headless/make_vm_debootstrap.py | 299 ++++++++++++++++++++++ qemu-headless/make_vm_debootstrap_armhf.py | 11 + qemu-headless/old-make-vm-debootstrap | 392 +++++++++++++++++++++++++++++ qemu-headless/start-arm-vm.sh | 15 ++ readme.rst | 1 + start-arm-vm.sh | 15 -- 17 files changed, 1451 insertions(+), 1450 deletions(-) delete mode 100644 build-grub2-instructions.txt delete mode 100755 extract-kernel.py delete mode 100755 make-vm-debootstrap delete mode 100755 make-vm-debootstrap-efi.py delete mode 100755 make-vm-debootstrap.py.orig delete mode 100755 make_vm_debootstrap.py delete mode 100755 make_vm_debootstrap_armhf.py create mode 100644 qemu-headless/build-grub2-instructions.txt create mode 100755 qemu-headless/extract-kernel.py create mode 100755 qemu-headless/make-vm-debootstrap-efi.py create mode 100755 qemu-headless/make-vm-debootstrap.py.orig create mode 100755 qemu-headless/make_vm_debootstrap.py create mode 100755 qemu-headless/make_vm_debootstrap_armhf.py create mode 100755 qemu-headless/old-make-vm-debootstrap create mode 100755 qemu-headless/start-arm-vm.sh create mode 100644 readme.rst delete mode 100755 start-arm-vm.sh diff --git a/build-grub2-instructions.txt b/build-grub2-instructions.txt deleted file mode 100644 index fe2cf5a..0000000 --- a/build-grub2-instructions.txt +++ /dev/null @@ -1,7 +0,0 @@ -# amd64-efi -./configure --with-platform=efi --target=x86_64 -make - -# armhf -./configure --target=arm-linux-gnueabihf -make diff --git a/extract-kernel.py b/extract-kernel.py deleted file mode 100755 index 71dde88..0000000 --- a/extract-kernel.py +++ /dev/null @@ -1,30 +0,0 @@ -#!/usr/bin/python3 - - -import os -import shutil -import subprocess -import sys - -def get_vmlinuz_path(): - return os.path.join('mnt', os.readlink('mnt/vmlinuz').lstrip('/')) - -def get_initrd_path(): - return os.path.join('mnt', os.readlink('mnt/initrd.img').lstrip('/')) - -if not os.path.exists('mnt'): - os.mkdir('mnt') - -subprocess.run([ - 'guestmount', - '-a', 'unstable-armhf.qcow2', - '-m', '/dev/sda1', - '--ro', - 'mnt']) -print(get_vmlinuz_path()) -print(get_initrd_path()) - -shutil.copyfile(get_vmlinuz_path(), 'vmlinuz', follow_symlinks=True) -shutil.copyfile(get_initrd_path(), 'initrd.img', follow_symlinks=True) - -subprocess.run(['fusermount', '-u', 'mnt']) diff --git a/make-vm-debootstrap b/make-vm-debootstrap deleted file mode 100755 index 667eead..0000000 --- a/make-vm-debootstrap +++ /dev/null @@ -1,392 +0,0 @@ -#!/bin/bash - -set -eu - -#if [ "$UID" != "0" ]; then -# exec sudo -- "$0" "$@" -#fi - -traperror() -{ - echo "args: $*" >&2 - echo "trace: ${FUNCNAME[*]} ${BASH_LINENO[*]}" >&2 - exit 1 -} - -fail() { echo "${FUNCNAME[1]} ${BASH_LINENO[0]}: $*" >&2; exit 1; } -#trap 'fail unhandled exception' ERR -trap 'traperror $LINENO ${FUNCNAME:-main} $BASH_LINENO; trap - ERR' ERR - -exec 3>&1 -exec >debootstrap.log -echo "I: log=debootstrap.log">&2 - -RELEASE=unstable -#MIRROR=http://fr.archive.ubuntu.com/ubuntu/ -MIRROR=http://apt:9999/debian/ -ARCH=amd64 -PKGS="$(sed 's/^#.*$//'<&2 - false - echo 'test0-end' >&2 -} - -test0 - -false - -#echo "Warning: will erase $BLOCKDEV with mkfs.ext4 in 10seconds...">&2 -#for i in $(seq 10 -1 0); do sleep 1; printf "%d... " $i>&2; done -#echo>&2 -#mkfs -q -F -t ext4 -L rootfs "$BLOCKDEV" || exit 1 -#mkdir -p "$DESTDIR" -#echo "mounting block device...">&2 -#mount -v -t ext4 "$BLOCKDEV" "$DESTDIR" || \ -# mount -v -t ext4 -o loop "$BLOCKDEV" "$DESTDIR" || exit 1 -#echo "In case of error, please remember to umount $DESTDIR">&2 - -# compacting qcow2 images files -# You have to zerofill the data on the disk first. -# sfill -fllvz -# Then clone the image file -# qemu-img convert -p -O qcow2 ./source.img ./packed. - -#modprobe nbd max_part=16 -#qemu-nbd -c /dev/nbd0 image.qcow2 -#partprobe /dev/nbd0 -#mount /dev/nbd0p1 /mnt/image - -echo "I: debootstraping...">&2 -qemu-debootstrap --arch="$ARCH" \ - --include="$PKGS" --components=main,universe \ - "$RELEASE" "$DESTDIR" "$MIRROR" - -if [ ! -e "$KERNEL" ];then - echo "I: wget kernel...">&2 - wget -q "$MIRROR/dists/$RELEASE/main/installer-$ARCH/current/images/netboot/ubuntu-installer/$ARCH/linux" -O "$KERNEL" -fi - -echo "I: adjust configuration...">&2 -rm -f "$DESTDIR/etc/localtime" -cat "$DESTDIR/usr/share/zoneinfo/Europe/Paris" > "$DESTDIR/etc/localtime" - -# network -cat >"$DESTDIR/etc/network/interfaces"<"$DESTDIR/etc/hosts" -echo "localhost">"$DESTDIR/etc/hostname" -echo "servername 10.1.1.254">"$DESTDIR/etc/resolv.conf" - -for f in "$DESTDIR"/etc/init/tty[1-6].conf; do - echo "manual">${f%.conf}.override -done - -chmod +w "$DESTDIR/etc/sudoers" -sed 's,#* *%sudo.*,%sudo ALL=(ALL) NOPASSWD: ALL,' -i "$DESTDIR/etc/sudoers" -chmod -w "$DESTDIR/etc/sudoers" - -#UUID="$(blkid "$BLOCKDEV" | sed 's/^.*UUID="\([a-zA-Z0-9-]\+\)".*$/\1/')" -cat >"$DESTDIR/etc/fstab"< -/dev/vda / ext4 errors=remount-ro,noatime 0 1 -tmpfs /tmp tmpfs mode=1777,noatime,size=128m,nodev,nosuid,noexec 0 0 -tmpfs /var/log tmpfs mode=1777,noatime,size=4m,nodev,nosuid,noexec 0 0 -tmpfs /var/tmp tmpfs mode=1777,noatime,size=128m,nodev,nosuid,noexec 0 0 - -#UUID=$UUID / ext4 errors=remount-ro,noatime 0 1 -#UUID=use blkid for uuid discovery /home ext4 noatime 0 2 -#UUID=6d9ac889-3c25-4e05-90f3-d183f8b14aca none swap sw,auto 0 0 -EOF - -mkdir -p "$DESTDIR/etc/sv/ttyS0/" -mkdir -p "$DESTDIR/etc/service/" -cat >"$DESTDIR/etc/sv/ttyS0/run"<<"EOF" -#!/bin/bash -dev=/dev/ttyS0 -/bin/stty -F $dev sane clocal -crtscts -hupcl -exec <$dev >$dev 2>$dev -echo -echo "Press enter to continue..."; read -cd /root -/usr/bin/env -i HOME=/root PATH=/usr/sbin:/usr/bin:/sbin:/bin TERM=screen-256color /usr/bin/setsid /bin/bash -exec wait -#exec /bin/bash -#exec /usr/bin/env -i /bin/sh -#exec /usr/bin/setsid /bin/sh -EOF -chmod +x "$DESTDIR/etc/sv/ttyS0/run" -ln -sfv "/etc/sv/ttyS0/" "$DESTDIR/etc/service/" - -mkdir -p "$DESTDIR/etc/ssh" -cat >"$DESTDIR/etc/ssh/sshd_config"< "$DESTDIR/etc/apt/sources.list" < "$DESTDIR/etc/default/console-setup"<"$DESTDIR/etc/default/keyboard"<"$DESTDIR/etc/default/locale"<>"$DESTDIR/etc/bash.bashrc"<"$DESTDIR/etc/vim/vimrc"<,[,] -set scrolloff=1 -EOF - -rm -r "$DESTDIR/etc/skel" -mkdir -p "$DESTDIR/etc/skel" - -cat > "$DESTDIR/init-stage2.sh" << EOF -#!/bin/sh -export PATH="/usr/sbin:/usr/bin:/sbin:/bin" -mount -no remount,rw / -mount -t proc proc /proc -mount -t sysfs sysfs /sys - -/debootstrap/debootstrap --second-stage - -# second stage may unmount this... -[ -e /proc/mounts ] || mount -t proc proc /proc -[ -e /sys/kernel ] || mount -t sysfs sysfs /sys -busybox mdev -s - -echo '------ mounts:' -cat /proc/mounts -echo '------' - -mv /etc/default/extlinux /etc/default/extlinux.dist -cat >/etc/default/extlinux<~root/.bashrc -echo '. ~/.bashrc' >~root/.bash_profile -echo '. /etc/bash.bashrc' >~calendros/.bashrc -echo '. ~/.bashrc' >~calendros/.bash_profile - -locale-gen en_US.UTF-8 en_GB.UTF-8 en_DK.UTF-8 fr_FR.UTF-8 - -# dhclient may erase /etc/resolv.conf, and is not really useful here -#ip link set eth0 up -#dhclient eth0 -#apt-get update - -echo "Dropping a shell, press Ctrl-D or type exit to finish." -/bin/bash - -rm -v /etc/udev/rules.d/70-persistent-*.rules - -echo "Bootstrap ended, poweroff in 10seconds..." -rm /init-stage2.sh -#sleep 10 || exec /bin/bash # for debug -#poweroff & # does not work -sync -fuser -k / -sleep 1 -mount -o remount,ro / -exit 0 # will panic, but does not matter now (don't know how to shutdown o/w) -EOF -chmod +x "$DESTDIR/init-stage2.sh" - -cat >&2 < - PARTUUID={rid} / ext4 errors=remount-ro,relatime 0 1 - PARTUUID={bid} /boot/firmware vfat errors=remount-ro,relatime 0 2 - '''.format(bid=bootpartuuid, - rid=rootpartuuid))) - - # activate serial console - run([ - 'ln', - '-s', - '/lib/systemd/system/serial-getty@.service', - rootdir + '/etc/systemd/system/getty.target.wants/serial-getty@ttyS0.service', - ], check=True) - os.makedirs(rootdir + '/etc/systemd/system/serial-getty@ttyS0.service.d', - mode=0o755) - open8(rootdir + - '/etc/systemd/system/serial-getty@ttyS0.service.d/autologin.conf', - 'w').write(mlstrip( - '''\ - [Service] - ExecStart= - ExecStart=-/sbin/agetty --autologin root --login-pause --noclear %I 115200,38400,9600 vt102 - ''')) - - script_dir = os.path.dirname(os.path.realpath(__file__)) - os.makedirs(rootdir + '/boot/firmware/efi/boot', mode=0o755) - with tempfile.NamedTemporaryFile(mode='w', encoding='utf8') as f: - f.write(mlstrip( - '''\ - search --set=root --no-floppy --fs-uuid {rid} --hint hd0,gpt2 - configfile /boot/grub/grub.cfg - '''.format(rid=rootfsuuid))) - f.flush() - run([ - script_dir + '/grub-2.02~beta3/grub-mkimage', - '-d', script_dir + '/grub-2.02~beta3/grub-core', - '-O', 'x86_64-efi', - '--output={}/boot/firmware/efi/boot/bootx64.efi'.format(rootdir), - '--prefix=/efi/boot', - '--config=' + f.name, - ] + list(glob.glob(script_dir + '/grub-2.02~beta3/grub-core/*.mod')), - check=True) - - open8(rootdir + '/etc/default/keyboard', 'w').write(mlstrip( - '''\ - XKBMODEL="pc105" - XKBLAYOUT="fr" - XKBVARIANT="bepo" - XKBOPTIONS="" - ''')) - - open8(rootdir + '/etc/default/locale', 'w').write(mlstrip( - '''\ - LANG="en_US.UTF-8" - LC_TIME="en_DK.UTF-8" - LC_PAPER="en_GB.UTF-8" - LC_MEASUREMENT="en_GB.UTF-8" - ''')) - - for locale in ['fr_FR', 'en_US', 'en_GB', 'en_DK', 'de_DE']: - run([ - 'localedef', - '--prefix={}'.format(rootdir), - '-f', 'UTF-8', - '-i', locale, - '{}.UTF-8'.format(locale) - ], check=True) - - open8(rootdir + '/etc/bash.bashrc', 'a').write(mlstrip( - '''\ - - # 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 - alias ls="ls --color=aut" - alias l="ls -CF" - alias ll="l -lh" - alias la="l -a" - alias e="vim" - alias rm='rm -i' - alias cp='cp -i' - alias mv='mv -i' - export PAGER=less - export EDITOR=vim - export VISUAL=vim - ''')) - - open8(rootdir + '/etc/vim/vimrc', 'w').write(mlstrip( - '''\ - set nocompatible - filetype plugin indent on - set autoindent - set background=dark - set backspace=2 - set hidden - set hlsearch - set ignorecase - set incsearch - set laststatus=2 - set modelines=0 - set nobackup - set nowritebackup - set ruler - set scrolloff=3 - set shiftwidth=4 - set showcmd - set showmatch - set statusline=%<%f%h%m%r%=%l,%c\ %P - set ts=4 - set whichwrap=<,>,[,] - set wildmode=list:full - syntax on - ''')) - - open8(rootdir + '/etc/default/grub', 'w').write(mlstrip( - '''\ - GRUB_DEFAULT=0 - GRUB_TIMEOUT=10 - GRUB_DISTRIBUTOR=Debian - GRUB_TERMINAL=console - GRUB_SERIAL_COMMAND="serial --unit=0 --speed=115200 --word=8 --parity=no --stop=1" - GRUB_CMDLINE_LINUX_DEFAULT="" - GRUB_CMDLINE_LINUX="text console=ttyS0,115200n8 console=tty0 net.ifnames=0" - ''')) - - kernel_params = 'ro text console=ttyS0,115200n8' - kernel_params += ' console=tty0 net.ifnames=0' - open8(rootdir + '/boot/grub/grub.cfg', 'w').write(mlstrip( - '''\ - terminal_output console - serial --unit=0 --speed=115200 --word=8 --parity=no --stop=1 - set default=0 - set timeout=3 - menuentry 'default' {{ - search --set=root --file /boot/grub/grub.cfg --hint hd0,gpt2 - linux /vmlinuz root=PARTUUID={rid} {kp} - initrd /initrd.img - }} - '''.format(rid=rootpartuuid, kp=kernel_params))) - - -def make_image(): - device = None - rootdirobj = tempfile.TemporaryDirectory() - rootdir = rootdirobj.name - try: - run(['qemu-img', 'create', '-f', 'qcow2', image, '10G'], check=True) - - run(['modprobe', 'nbd', 'max_part=16']) - - for d in sorted(glob.glob('/dev/nbd*'), key=numerical_sort): - if run(['qemu-nbd', '-c', d, image]).returncode == 0: - device = d - break - else: - print('No free nbd device found for qemu-nbd') - raise SystemExit(1) - - run([ - 'sgdisk', - '--set-alignment=8192', - '--zap-all', - '--new=1:4M:+300M', - '--change-name=1:boot', - '--typecode=1:EF00', - '--largest-new=2', - '--change-name=2:root', - '--typecode=2:8300', - device, - ], check=True) - - run(['partprobe', device]) - run(['mkfs.fat', '-n', 'boot', device+'p1'], check=True) - run(['mkfs.ext4', '-q', '-L', 'root', device+'p2'], check=True) - run(['mount', device+'p2', rootdir], check=True) - os.makedirs(rootdir + '/boot/firmware', mode=0o755) - run(['mount', device+'p1', rootdir+'/boot/firmware'], check=True) - - run([ - 'qemu-debootstrap', - '--arch=' + parameters['arch'], - '--include=' + ','.join(parameters['packages']), - '--components=main,universe', - parameters['release'], - rootdir, - parameters['mirror'], - ], check=True) - - rootpartuuid = (run([ - 'partx', - '--noheadings', - '--show', - '--output', - 'UUID', - device+'p2'], check=True, stdout=subprocess.PIPE) - .stdout - .decode('utf8') - .strip()) - bootpartuuid = (run([ - 'partx', - '--noheadings', - '--show', - '--output', - 'UUID', - device+'p1'], check=True, stdout=subprocess.PIPE) - .stdout - .decode('utf8') - .strip()) - - rootfsuuid = [line.split()[-1] - for line in (run([ 'tune2fs', '-l', device+'p2'], - check=True, stdout=subprocess.PIPE) - .stdout - .decode('utf8') - .splitlines()) - if "filesystem uuid" in line.lower()][0] - - system_customization(rootdir, bootpartuuid, rootpartuuid, rootfsuuid) - - finally: - if rootdir: - cmd = ['umount'] + \ - ['{rootdir}/{}'.format(x, rootdir=rootdir) - for x in 'proc sys dev boot/firmware'.split()] - run(cmd) - run(['umount', rootdir]) - if device: - run(['qemu-nbd', '-d', device]) - - -def main(): - reexec_root() - make_image() - - -if __name__ == '__main__': - main() diff --git a/make-vm-debootstrap.py.orig b/make-vm-debootstrap.py.orig deleted file mode 100755 index 62f1165..0000000 --- a/make-vm-debootstrap.py.orig +++ /dev/null @@ -1,331 +0,0 @@ -#!/usr/bin/python3 - -import os -import sys -import glob -import subprocess -import tempfile -import shutil -import re -import functools - - - - -parameters = dict( - release='unstable', - #mirror='http://fr.archive.ubuntu.com/ubuntu/', - mirror='http://apt:9999/debian/', - arch='amd64', - image = 'unstable-amd64.qcow2', - packages=''' - apt - aptitude - bash - bash-completion - bind9-host - bmon - busybox - bzip2 - curl - ed - grub2 - htop - iftop - ifupdown - iotop - iperf - iproute2 - iptables - iputils-ping - less - lftp - linux-image-amd64 - locales - ncdu - ncurses-base - ncurses-term - net-tools - netbase - netcat - nload - openssh-client - openssh-server - psmisc - python3 - ranger - rsync - screen - sed - socat - strace - tar - tcpdump - telnet - tmux - tree - tzdata - vim - vim-nox - vim-runtime - w3m - wget - zsh - '''.split(), -) - - -open8 = functools.partial(open, encoding='utf8') -numerical_sort = lambda y: [int(x) if x.isdigit() else x - for x in re.split('(\d+)', y)] - - -def mlstrip(s): - return re.sub(r'^\s*', '', s, flags=re.MULTILINE) - - -def run(*l, **kw): - print('run:', *l) - return subprocess.run(*l, **kw) - - -def reexec_root(): - # run as root - if os.geteuid() != 0: - os.execvp("sudo", ["sudo"] + sys.argv) - - -def system_customization(device, rootdir, rootpartuuid, rootfsuuid): - os.unlink(rootdir + '/etc/localtime') - open8(rootdir + '/etc/zoneinfo', 'w').write('Europe/Paris\n') - shutil.copy(rootdir + '/usr/share/zoneinfo/Europe/Paris', - rootdir + '/etc/localtime') - - open8(rootdir + '/etc/network/interfaces', 'w').write(mlstrip( - '''\ - auto lo - iface lo inet loopback - - auto eth0 - iface eth0 inet dhcp - ''')) - open8(rootdir + '/etc/hosts', 'w').write(mlstrip( - '''\ - 127.0.0.1 localhost localhost.localdomain debian - - # the following lines are desirable for IPv6 capable hosts - ::1 localhost ip6-localhost ip6-loopback - ff02::1 ip6-allnodes - ff02::2 ip6-allrouters - ''')) - open8(rootdir + '/etc/hostname', 'w').write('debian\n') - open8(rootdir + '/etc/fstab', 'w').write(mlstrip( - '''\ - # - PARTUUID={rid} / ext4 errors=remount-ro,relatime 0 1 - '''.format(rid=rootpartuuid))) - - # activate serial console - run([ - 'ln', - '-s', - '/lib/systemd/system/serial-getty@.service', - rootdir + '/etc/systemd/system/getty.target.wants/serial-getty@ttyS0.service', - ], check=True) - os.makedirs(rootdir + '/etc/systemd/system/serial-getty@ttyS0.service.d', - mode=0o755) - open8(rootdir + - '/etc/systemd/system/serial-getty@ttyS0.service.d/autologin.conf', - 'w').write(mlstrip( - '''\ - [Service] - ExecStart= - ExecStart=-/sbin/agetty --autologin root --login-pause --noclear %I 115200,38400,9600 vt102 - ''')) - - open8(rootdir + '/etc/default/keyboard', 'w').write(mlstrip( - '''\ - XKBMODEL="pc105" - XKBLAYOUT="fr" - XKBVARIANT="bepo" - XKBOPTIONS="" - ''')) - - open8(rootdir + '/etc/default/locale', 'w').write(mlstrip( - '''\ - LANG="en_US.UTF-8" - LC_TIME="en_DK.UTF-8" - LC_PAPER="en_GB.UTF-8" - LC_MEASUREMENT="en_GB.UTF-8" - ''')) - - for locale in ['fr_FR', 'en_US', 'en_GB', 'en_DK', 'de_DE']: - run([ - 'localedef', - '--prefix={}'.format(rootdir), - '-f', 'UTF-8', - '-i', locale, - '{}.UTF-8'.format(locale) - ], check=True) - - open8(rootdir + '/etc/bash.bashrc', 'a').write(mlstrip( - '''\ - - # 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 - alias ls="ls --color=aut" - alias l="ls -CF" - alias ll="l -lh" - alias la="l -a" - alias e="vim" - alias rm='rm -i' - alias cp='cp -i' - alias mv='mv -i' - export PAGER=less - export EDITOR=vim - export VISUAL=vim - ''')) - - open8(rootdir + '/etc/vim/vimrc', 'w').write(mlstrip( - '''\ - set nocompatible - filetype plugin indent on - set autoindent - set background=dark - set backspace=2 - set hidden - set hlsearch - set ignorecase - set incsearch - set laststatus=2 - set modelines=0 - set nobackup - set nowritebackup - set ruler - set scrolloff=3 - set shiftwidth=4 - set showcmd - set showmatch - set statusline=%<%f%h%m%r%=%l,%c\ %P - set ts=4 - set whichwrap=<,>,[,] - set wildmode=list:full - syntax on - ''')) - - open8(rootdir + '/etc/default/grub', 'w').write(mlstrip( - '''\ - GRUB_DEFAULT=0 - GRUB_TIMEOUT=1 - GRUB_TERMINAL=serial - GRUB_SERIAL_COMMAND="serial --speed=115200 --word=8 --parity=no --stop=1" - GRUB_CMDLINE_LINUX_DEFAULT="" - GRUB_CMDLINE_LINUX="text console=ttyS0,115200n8 console=tty0 net.ifnames=0" - ''')) - - kernel_params = 'ro text console=ttyS0,115200n8' - kernel_params += ' console=tty0 net.ifnames=0' - open8(rootdir + '/boot/grub/grub.cfg', 'w').write(mlstrip( - '''\ - terminal_output serial - serial --speed=115200 --word=8 --parity=no --stop=1 - set default=0 - set timeout=1 - menuentry 'default' {{ - search --set=root --file /boot/grub/grub.cfg --hint hd0,gpt2 - linux /vmlinuz root=PARTUUID={rid} {kp} - initrd /initrd.img - }} - '''.format(rid=rootpartuuid, kp=kernel_params))) - - run(['grub-install', - '--boot-directory=' + rootdir + '/boot', - device]) - - -def make_image(): - device = None - rootdirobj = tempfile.TemporaryDirectory() - rootdir = rootdirobj.name - try: - run(['qemu-img', 'create', '-f', 'qcow2', parameters.image, '20G'], - check=True) - - run(['modprobe', 'nbd', 'max_part=16']) - - for d in sorted(glob.glob('/dev/nbd*'), key=numerical_sort): - if run(['qemu-nbd', '-c', d, parameters.image]).returncode == 0: - device = d - break - else: - print('No free nbd device found for qemu-nbd') - raise SystemExit(1) - - run([ - 'sgdisk', - '--set-alignment=8192', - '--zap-all', - '--new=1:1M:+1M', - '--change-name=1:boot', - '--typecode=1:EF02', - '--largest-new=2', - '--change-name=2:root', - '--typecode=2:8300', - device, - ], check=True) - - run(['partprobe', device]) - run(['mkfs.ext4', '-q', '-L', 'root', device+'p2'], check=True) - run(['mount', device+'p2', rootdir], check=True) - - run([ - 'qemu-debootstrap', - '--arch=' + parameters['arch'], - '--include=' + ','.join(parameters['packages']), - '--components=main,universe', - parameters['release'], - rootdir, - parameters['mirror'], - ], check=True) - - rootpartuuid = (run([ - 'partx', - '--noheadings', - '--show', - '--output', - 'UUID', - device+'p2'], check=True, stdout=subprocess.PIPE) - .stdout - .decode('utf8') - .strip()) - - rootfsuuid = [line.split()[-1] - for line in (run([ 'tune2fs', '-l', device+'p2'], - check=True, stdout=subprocess.PIPE) - .stdout - .decode('utf8') - .splitlines()) - if "filesystem uuid" in line.lower()][0] - - system_customization(device, rootdir, rootpartuuid, rootfsuuid) - - finally: - if rootdir: - run(['umount', rootdir]) - if device: - run(['qemu-nbd', '-d', device]) - - -def main(): - reexec_root() - make_image() - - -if __name__ == '__main__': - main() diff --git a/make_vm_debootstrap.py b/make_vm_debootstrap.py deleted file mode 100755 index 5317099..0000000 --- a/make_vm_debootstrap.py +++ /dev/null @@ -1,299 +0,0 @@ -#!/usr/bin/python3 - -import os -import sys -import glob -import subprocess -import tempfile -import shutil -import re -import functools - - -parameters = dict( - release='unstable', - #mirror='http://fr.archive.ubuntu.com/ubuntu/', - mirror='http://apt:9999/debian/', - arch='amd64', - image = 'unstable-amd64.qcow2', - packages=''' - apt - aptitude - bash - bash-completion - bind9-host - bmon - busybox - bzip2 - curl - ed - htop - iftop - ifupdown - iotop - iperf - iproute2 - iptables - iputils-ping - less - lftp - locales - ncdu - ncurses-base - ncurses-term - net-tools - netbase - netcat - nload - openssh-client - openssh-server - psmisc - python3 - ranger - rsync - screen - sed - socat - strace - tar - tcpdump - telnet - tmux - tree - tzdata - vim - vim-nox - vim-runtime - w3m - wget - zsh - '''.split(), - kernel_package = 'linux-image-amd64', - serial_console = 'ttyS0', -) - - -open8 = functools.partial(open, encoding='utf8') -numerical_sort = lambda y: [int(x) if x.isdigit() else x - for x in re.split('(\d+)', y)] - - -def mlstrip(s): - return re.sub(r'^\s*', '', s, flags=re.MULTILINE) - - -def run(*l, **kw): - print('run:', *l) - return subprocess.run(*l, **kw) - - -def reexec_root(): - # run as root - if os.geteuid() != 0: - os.execvp("sudo", ["sudo"] + sys.argv) - - -def system_customization(device, rootdir, rootpartuuid, rootfsuuid): - os.unlink(rootdir + '/etc/localtime') - open8(rootdir + '/etc/zoneinfo', 'w').write('Europe/Paris\n') - shutil.copy(rootdir + '/usr/share/zoneinfo/Europe/Paris', - rootdir + '/etc/localtime') - - open8(rootdir + '/etc/network/interfaces', 'w').write(mlstrip( - '''\ - auto lo - iface lo inet loopback - - auto eth0 - iface eth0 inet dhcp - ''')) - open8(rootdir + '/etc/hosts', 'w').write(mlstrip( - '''\ - 127.0.0.1 localhost localhost.localdomain debian - - # the following lines are desirable for IPv6 capable hosts - ::1 localhost ip6-localhost ip6-loopback - ff02::1 ip6-allnodes - ff02::2 ip6-allrouters - ''')) - open8(rootdir + '/etc/hostname', 'w').write('debian\n') - open8(rootdir + '/etc/fstab', 'w').write(mlstrip( - '''\ - # - PARTUUID={rid} / ext4 errors=remount-ro,relatime 0 1 - '''.format(rid=rootpartuuid))) - - # activate serial console on parameter['serial_console'] - console = parameters['serial_console'] - run([ - 'ln', - '-s', - '/lib/systemd/system/serial-getty@.service', - rootdir + '/etc/systemd/system/getty.target.wants/serial-getty@%s.service' % console, - ], check=True) - os.makedirs(rootdir + '/etc/systemd/system/serial-getty@%s.service.d' % console, - mode=0o755) - open8(rootdir + - '/etc/systemd/system/serial-getty@%s.service.d/autologin.conf' % console, - 'w').write(mlstrip( - '''\ - [Service] - ExecStart= - ExecStart=-/sbin/agetty --autologin root --login-pause --noclear %I 115200,38400,9600 vt102 - ''')) - - open8(rootdir + '/etc/default/keyboard', 'w').write(mlstrip( - '''\ - XKBMODEL="pc105" - XKBLAYOUT="fr" - XKBVARIANT="bepo" - XKBOPTIONS="" - ''')) - - open8(rootdir + '/etc/default/locale', 'w').write(mlstrip( - '''\ - LANG="en_US.UTF-8" - LC_TIME="en_DK.UTF-8" - LC_PAPER="en_GB.UTF-8" - LC_MEASUREMENT="en_GB.UTF-8" - ''')) - - for locale in ['fr_FR', 'en_US', 'en_GB', 'en_DK', 'de_DE']: - run([ - 'localedef', - '--prefix={}'.format(rootdir), - '-f', 'UTF-8', - '-i', locale, - '{}.UTF-8'.format(locale) - ], check=True) - - open8(rootdir + '/etc/bash.bashrc', 'a').write(mlstrip( - '''\ - - # 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 - alias ls="ls --color=aut" - alias l="ls -CF" - alias ll="l -lh" - alias la="l -a" - alias e="vim" - alias rm='rm -i' - alias cp='cp -i' - alias mv='mv -i' - export PAGER=less - export EDITOR=vim - export VISUAL=vim - ''')) - - open8(rootdir + '/etc/vim/vimrc', 'w').write(mlstrip( - '''\ - set nocompatible - filetype plugin indent on - set autoindent - set background=dark - set backspace=2 - set hidden - set hlsearch - set ignorecase - set incsearch - set laststatus=2 - set modelines=0 - set nobackup - set nowritebackup - set ruler - set scrolloff=3 - set shiftwidth=4 - set showcmd - set showmatch - set statusline=%<%f%h%m%r%=%l,%c\ %P - set ts=4 - set whichwrap=<,>,[,] - set wildmode=list:full - syntax on - ''')) - - -def make_image(): - device = None - rootdirobj = tempfile.TemporaryDirectory() - rootdir = rootdirobj.name - try: - run(['qemu-img', 'create', '-f', 'qcow2', parameters['image'], '20G'], - check=True) - - run(['modprobe', 'nbd', 'max_part=16']) - - for d in sorted(glob.glob('/dev/nbd*'), key=numerical_sort): - if run(['qemu-nbd', '-c', d, parameters['image']]).returncode == 0: - device = d - break - else: - print('No free nbd device found for qemu-nbd') - raise SystemExit(1) - - run([ - 'sgdisk', - '--set-alignment=8192', - '--zap-all', - '--largest-new=1', - '--change-name=1:root', - '--typecode=1:8300', - device, - ], check=True) - - run(['partprobe', device]) - run(['mkfs.ext4', '-q', '-L', 'root', device+'p1'], check=True) - run(['mount', device+'p1', rootdir], check=True) - - run([ - 'qemu-debootstrap', - '--arch=' + parameters['arch'], - '--include=' + ','.join(parameters['packages'] - + [parameters['kernel_package']]), - '--components=main,universe', - parameters['release'], - rootdir, - parameters['mirror'], - ], check=True) - - rootpartuuid = (run([ - 'partx', - '--noheadings', - '--show', - '--output', - 'UUID', - device+'p1'], check=True, stdout=subprocess.PIPE) - .stdout - .decode('utf8') - .strip()) - - rootfsuuid = [line.split()[-1] - for line in (run([ 'tune2fs', '-l', device+'p1'], - check=True, stdout=subprocess.PIPE) - .stdout - .decode('utf8') - .splitlines()) - if "filesystem uuid" in line.lower()][0] - - system_customization(device, rootdir, rootpartuuid, rootfsuuid) - - finally: - if rootdir: - run(['umount', rootdir]) - if device: - run(['qemu-nbd', '-d', device]) - - -def main(): - reexec_root() - make_image() - - -if __name__ == '__main__': - main() diff --git a/make_vm_debootstrap_armhf.py b/make_vm_debootstrap_armhf.py deleted file mode 100755 index 69fc807..0000000 --- a/make_vm_debootstrap_armhf.py +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/python3 - -import make_vm_debootstrap - -make_vm_debootstrap.parameters['arch'] = 'armhf' -make_vm_debootstrap.parameters['image'] = 'unstable-armhf.qcow2' -make_vm_debootstrap.parameters['kernel_package'] = 'linux-image-armmp-lpae' -make_vm_debootstrap.parameters['serial_console'] = 'ttyAMA0' - -if __name__ == '__main__': - make_vm_debootstrap.main() diff --git a/qemu-headless/build-grub2-instructions.txt b/qemu-headless/build-grub2-instructions.txt new file mode 100644 index 0000000..fe2cf5a --- /dev/null +++ b/qemu-headless/build-grub2-instructions.txt @@ -0,0 +1,7 @@ +# amd64-efi +./configure --with-platform=efi --target=x86_64 +make + +# armhf +./configure --target=arm-linux-gnueabihf +make diff --git a/qemu-headless/extract-kernel.py b/qemu-headless/extract-kernel.py new file mode 100755 index 0000000..71dde88 --- /dev/null +++ b/qemu-headless/extract-kernel.py @@ -0,0 +1,30 @@ +#!/usr/bin/python3 + + +import os +import shutil +import subprocess +import sys + +def get_vmlinuz_path(): + return os.path.join('mnt', os.readlink('mnt/vmlinuz').lstrip('/')) + +def get_initrd_path(): + return os.path.join('mnt', os.readlink('mnt/initrd.img').lstrip('/')) + +if not os.path.exists('mnt'): + os.mkdir('mnt') + +subprocess.run([ + 'guestmount', + '-a', 'unstable-armhf.qcow2', + '-m', '/dev/sda1', + '--ro', + 'mnt']) +print(get_vmlinuz_path()) +print(get_initrd_path()) + +shutil.copyfile(get_vmlinuz_path(), 'vmlinuz', follow_symlinks=True) +shutil.copyfile(get_initrd_path(), 'initrd.img', follow_symlinks=True) + +subprocess.run(['fusermount', '-u', 'mnt']) diff --git a/qemu-headless/make-vm-debootstrap-efi.py b/qemu-headless/make-vm-debootstrap-efi.py new file mode 100755 index 0000000..15654f4 --- /dev/null +++ b/qemu-headless/make-vm-debootstrap-efi.py @@ -0,0 +1,365 @@ +#!/usr/bin/python3 + +import os +import sys +import glob +import subprocess +import tempfile +import shutil +import re +import functools + + +image = 'unstable.qcow2' + + +parameters = dict( + release='unstable', + #mirror='http://fr.archive.ubuntu.com/ubuntu/', + mirror='http://apt:9999/debian/', + arch='amd64', + packages=''' + apt + aptitude + bash + bash-completion + bind9-host + bmon + busybox + bzip2 + curl + ed + grub2 + htop + iftop + ifupdown + iotop + iperf + iproute2 + iptables + iputils-ping + less + lftp + linux-image-amd64 + locales + ncdu + ncurses-base + ncurses-term + net-tools + netbase + netcat + nload + openssh-client + openssh-server + psmisc + python3 + ranger + rsync + screen + sed + socat + strace + tar + tcpdump + telnet + tmux + tree + tzdata + vim + vim-nox + vim-runtime + w3m + wget + zsh + '''.split(), +) + + +open8 = functools.partial(open, encoding='utf8') +numerical_sort = lambda y: [int(x) if x.isdigit() else x + for x in re.split('(\d+)', y)] + + +def mlstrip(s): + return re.sub(r'^\s*', '', s, flags=re.MULTILINE) + + +def run(*l, **kw): + print('run:', *l) + return subprocess.run(*l, **kw) + + +def reexec_root(): + # run as root + if os.geteuid() != 0: + os.execvp("sudo", ["sudo"] + sys.argv) + + +def system_customization(rootdir, bootpartuuid, rootpartuuid, rootfsuuid): + os.unlink(rootdir + '/etc/localtime') + open8(rootdir + '/etc/zoneinfo', 'w').write('Europe/Paris\n') + shutil.copy(rootdir + '/usr/share/zoneinfo/Europe/Paris', + rootdir + '/etc/localtime') + + open8(rootdir + '/etc/network/interfaces', 'w').write(mlstrip( + '''\ + auto lo + iface lo inet loopback + + auto eth0 + iface eth0 inet dhcp + ''')) + open8(rootdir + '/etc/hosts', 'w').write(mlstrip( + '''\ + 127.0.0.1 localhost localhost.localdomain debian + + # the following lines are desirable for IPv6 capable hosts + ::1 localhost ip6-localhost ip6-loopback + ff02::1 ip6-allnodes + ff02::2 ip6-allrouters + ''')) + open8(rootdir + '/etc/hostname', 'w').write('debian\n') + open8(rootdir + '/etc/fstab', 'w').write(mlstrip( + '''\ + # + PARTUUID={rid} / ext4 errors=remount-ro,relatime 0 1 + PARTUUID={bid} /boot/firmware vfat errors=remount-ro,relatime 0 2 + '''.format(bid=bootpartuuid, + rid=rootpartuuid))) + + # activate serial console + run([ + 'ln', + '-s', + '/lib/systemd/system/serial-getty@.service', + rootdir + '/etc/systemd/system/getty.target.wants/serial-getty@ttyS0.service', + ], check=True) + os.makedirs(rootdir + '/etc/systemd/system/serial-getty@ttyS0.service.d', + mode=0o755) + open8(rootdir + + '/etc/systemd/system/serial-getty@ttyS0.service.d/autologin.conf', + 'w').write(mlstrip( + '''\ + [Service] + ExecStart= + ExecStart=-/sbin/agetty --autologin root --login-pause --noclear %I 115200,38400,9600 vt102 + ''')) + + script_dir = os.path.dirname(os.path.realpath(__file__)) + os.makedirs(rootdir + '/boot/firmware/efi/boot', mode=0o755) + with tempfile.NamedTemporaryFile(mode='w', encoding='utf8') as f: + f.write(mlstrip( + '''\ + search --set=root --no-floppy --fs-uuid {rid} --hint hd0,gpt2 + configfile /boot/grub/grub.cfg + '''.format(rid=rootfsuuid))) + f.flush() + run([ + script_dir + '/grub-2.02~beta3/grub-mkimage', + '-d', script_dir + '/grub-2.02~beta3/grub-core', + '-O', 'x86_64-efi', + '--output={}/boot/firmware/efi/boot/bootx64.efi'.format(rootdir), + '--prefix=/efi/boot', + '--config=' + f.name, + ] + list(glob.glob(script_dir + '/grub-2.02~beta3/grub-core/*.mod')), + check=True) + + open8(rootdir + '/etc/default/keyboard', 'w').write(mlstrip( + '''\ + XKBMODEL="pc105" + XKBLAYOUT="fr" + XKBVARIANT="bepo" + XKBOPTIONS="" + ''')) + + open8(rootdir + '/etc/default/locale', 'w').write(mlstrip( + '''\ + LANG="en_US.UTF-8" + LC_TIME="en_DK.UTF-8" + LC_PAPER="en_GB.UTF-8" + LC_MEASUREMENT="en_GB.UTF-8" + ''')) + + for locale in ['fr_FR', 'en_US', 'en_GB', 'en_DK', 'de_DE']: + run([ + 'localedef', + '--prefix={}'.format(rootdir), + '-f', 'UTF-8', + '-i', locale, + '{}.UTF-8'.format(locale) + ], check=True) + + open8(rootdir + '/etc/bash.bashrc', 'a').write(mlstrip( + '''\ + + # 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 + alias ls="ls --color=aut" + alias l="ls -CF" + alias ll="l -lh" + alias la="l -a" + alias e="vim" + alias rm='rm -i' + alias cp='cp -i' + alias mv='mv -i' + export PAGER=less + export EDITOR=vim + export VISUAL=vim + ''')) + + open8(rootdir + '/etc/vim/vimrc', 'w').write(mlstrip( + '''\ + set nocompatible + filetype plugin indent on + set autoindent + set background=dark + set backspace=2 + set hidden + set hlsearch + set ignorecase + set incsearch + set laststatus=2 + set modelines=0 + set nobackup + set nowritebackup + set ruler + set scrolloff=3 + set shiftwidth=4 + set showcmd + set showmatch + set statusline=%<%f%h%m%r%=%l,%c\ %P + set ts=4 + set whichwrap=<,>,[,] + set wildmode=list:full + syntax on + ''')) + + open8(rootdir + '/etc/default/grub', 'w').write(mlstrip( + '''\ + GRUB_DEFAULT=0 + GRUB_TIMEOUT=10 + GRUB_DISTRIBUTOR=Debian + GRUB_TERMINAL=console + GRUB_SERIAL_COMMAND="serial --unit=0 --speed=115200 --word=8 --parity=no --stop=1" + GRUB_CMDLINE_LINUX_DEFAULT="" + GRUB_CMDLINE_LINUX="text console=ttyS0,115200n8 console=tty0 net.ifnames=0" + ''')) + + kernel_params = 'ro text console=ttyS0,115200n8' + kernel_params += ' console=tty0 net.ifnames=0' + open8(rootdir + '/boot/grub/grub.cfg', 'w').write(mlstrip( + '''\ + terminal_output console + serial --unit=0 --speed=115200 --word=8 --parity=no --stop=1 + set default=0 + set timeout=3 + menuentry 'default' {{ + search --set=root --file /boot/grub/grub.cfg --hint hd0,gpt2 + linux /vmlinuz root=PARTUUID={rid} {kp} + initrd /initrd.img + }} + '''.format(rid=rootpartuuid, kp=kernel_params))) + + +def make_image(): + device = None + rootdirobj = tempfile.TemporaryDirectory() + rootdir = rootdirobj.name + try: + run(['qemu-img', 'create', '-f', 'qcow2', image, '10G'], check=True) + + run(['modprobe', 'nbd', 'max_part=16']) + + for d in sorted(glob.glob('/dev/nbd*'), key=numerical_sort): + if run(['qemu-nbd', '-c', d, image]).returncode == 0: + device = d + break + else: + print('No free nbd device found for qemu-nbd') + raise SystemExit(1) + + run([ + 'sgdisk', + '--set-alignment=8192', + '--zap-all', + '--new=1:4M:+300M', + '--change-name=1:boot', + '--typecode=1:EF00', + '--largest-new=2', + '--change-name=2:root', + '--typecode=2:8300', + device, + ], check=True) + + run(['partprobe', device]) + run(['mkfs.fat', '-n', 'boot', device+'p1'], check=True) + run(['mkfs.ext4', '-q', '-L', 'root', device+'p2'], check=True) + run(['mount', device+'p2', rootdir], check=True) + os.makedirs(rootdir + '/boot/firmware', mode=0o755) + run(['mount', device+'p1', rootdir+'/boot/firmware'], check=True) + + run([ + 'qemu-debootstrap', + '--arch=' + parameters['arch'], + '--include=' + ','.join(parameters['packages']), + '--components=main,universe', + parameters['release'], + rootdir, + parameters['mirror'], + ], check=True) + + rootpartuuid = (run([ + 'partx', + '--noheadings', + '--show', + '--output', + 'UUID', + device+'p2'], check=True, stdout=subprocess.PIPE) + .stdout + .decode('utf8') + .strip()) + bootpartuuid = (run([ + 'partx', + '--noheadings', + '--show', + '--output', + 'UUID', + device+'p1'], check=True, stdout=subprocess.PIPE) + .stdout + .decode('utf8') + .strip()) + + rootfsuuid = [line.split()[-1] + for line in (run([ 'tune2fs', '-l', device+'p2'], + check=True, stdout=subprocess.PIPE) + .stdout + .decode('utf8') + .splitlines()) + if "filesystem uuid" in line.lower()][0] + + system_customization(rootdir, bootpartuuid, rootpartuuid, rootfsuuid) + + finally: + if rootdir: + cmd = ['umount'] + \ + ['{rootdir}/{}'.format(x, rootdir=rootdir) + for x in 'proc sys dev boot/firmware'.split()] + run(cmd) + run(['umount', rootdir]) + if device: + run(['qemu-nbd', '-d', device]) + + +def main(): + reexec_root() + make_image() + + +if __name__ == '__main__': + main() diff --git a/qemu-headless/make-vm-debootstrap.py.orig b/qemu-headless/make-vm-debootstrap.py.orig new file mode 100755 index 0000000..62f1165 --- /dev/null +++ b/qemu-headless/make-vm-debootstrap.py.orig @@ -0,0 +1,331 @@ +#!/usr/bin/python3 + +import os +import sys +import glob +import subprocess +import tempfile +import shutil +import re +import functools + + + + +parameters = dict( + release='unstable', + #mirror='http://fr.archive.ubuntu.com/ubuntu/', + mirror='http://apt:9999/debian/', + arch='amd64', + image = 'unstable-amd64.qcow2', + packages=''' + apt + aptitude + bash + bash-completion + bind9-host + bmon + busybox + bzip2 + curl + ed + grub2 + htop + iftop + ifupdown + iotop + iperf + iproute2 + iptables + iputils-ping + less + lftp + linux-image-amd64 + locales + ncdu + ncurses-base + ncurses-term + net-tools + netbase + netcat + nload + openssh-client + openssh-server + psmisc + python3 + ranger + rsync + screen + sed + socat + strace + tar + tcpdump + telnet + tmux + tree + tzdata + vim + vim-nox + vim-runtime + w3m + wget + zsh + '''.split(), +) + + +open8 = functools.partial(open, encoding='utf8') +numerical_sort = lambda y: [int(x) if x.isdigit() else x + for x in re.split('(\d+)', y)] + + +def mlstrip(s): + return re.sub(r'^\s*', '', s, flags=re.MULTILINE) + + +def run(*l, **kw): + print('run:', *l) + return subprocess.run(*l, **kw) + + +def reexec_root(): + # run as root + if os.geteuid() != 0: + os.execvp("sudo", ["sudo"] + sys.argv) + + +def system_customization(device, rootdir, rootpartuuid, rootfsuuid): + os.unlink(rootdir + '/etc/localtime') + open8(rootdir + '/etc/zoneinfo', 'w').write('Europe/Paris\n') + shutil.copy(rootdir + '/usr/share/zoneinfo/Europe/Paris', + rootdir + '/etc/localtime') + + open8(rootdir + '/etc/network/interfaces', 'w').write(mlstrip( + '''\ + auto lo + iface lo inet loopback + + auto eth0 + iface eth0 inet dhcp + ''')) + open8(rootdir + '/etc/hosts', 'w').write(mlstrip( + '''\ + 127.0.0.1 localhost localhost.localdomain debian + + # the following lines are desirable for IPv6 capable hosts + ::1 localhost ip6-localhost ip6-loopback + ff02::1 ip6-allnodes + ff02::2 ip6-allrouters + ''')) + open8(rootdir + '/etc/hostname', 'w').write('debian\n') + open8(rootdir + '/etc/fstab', 'w').write(mlstrip( + '''\ + # + PARTUUID={rid} / ext4 errors=remount-ro,relatime 0 1 + '''.format(rid=rootpartuuid))) + + # activate serial console + run([ + 'ln', + '-s', + '/lib/systemd/system/serial-getty@.service', + rootdir + '/etc/systemd/system/getty.target.wants/serial-getty@ttyS0.service', + ], check=True) + os.makedirs(rootdir + '/etc/systemd/system/serial-getty@ttyS0.service.d', + mode=0o755) + open8(rootdir + + '/etc/systemd/system/serial-getty@ttyS0.service.d/autologin.conf', + 'w').write(mlstrip( + '''\ + [Service] + ExecStart= + ExecStart=-/sbin/agetty --autologin root --login-pause --noclear %I 115200,38400,9600 vt102 + ''')) + + open8(rootdir + '/etc/default/keyboard', 'w').write(mlstrip( + '''\ + XKBMODEL="pc105" + XKBLAYOUT="fr" + XKBVARIANT="bepo" + XKBOPTIONS="" + ''')) + + open8(rootdir + '/etc/default/locale', 'w').write(mlstrip( + '''\ + LANG="en_US.UTF-8" + LC_TIME="en_DK.UTF-8" + LC_PAPER="en_GB.UTF-8" + LC_MEASUREMENT="en_GB.UTF-8" + ''')) + + for locale in ['fr_FR', 'en_US', 'en_GB', 'en_DK', 'de_DE']: + run([ + 'localedef', + '--prefix={}'.format(rootdir), + '-f', 'UTF-8', + '-i', locale, + '{}.UTF-8'.format(locale) + ], check=True) + + open8(rootdir + '/etc/bash.bashrc', 'a').write(mlstrip( + '''\ + + # 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 + alias ls="ls --color=aut" + alias l="ls -CF" + alias ll="l -lh" + alias la="l -a" + alias e="vim" + alias rm='rm -i' + alias cp='cp -i' + alias mv='mv -i' + export PAGER=less + export EDITOR=vim + export VISUAL=vim + ''')) + + open8(rootdir + '/etc/vim/vimrc', 'w').write(mlstrip( + '''\ + set nocompatible + filetype plugin indent on + set autoindent + set background=dark + set backspace=2 + set hidden + set hlsearch + set ignorecase + set incsearch + set laststatus=2 + set modelines=0 + set nobackup + set nowritebackup + set ruler + set scrolloff=3 + set shiftwidth=4 + set showcmd + set showmatch + set statusline=%<%f%h%m%r%=%l,%c\ %P + set ts=4 + set whichwrap=<,>,[,] + set wildmode=list:full + syntax on + ''')) + + open8(rootdir + '/etc/default/grub', 'w').write(mlstrip( + '''\ + GRUB_DEFAULT=0 + GRUB_TIMEOUT=1 + GRUB_TERMINAL=serial + GRUB_SERIAL_COMMAND="serial --speed=115200 --word=8 --parity=no --stop=1" + GRUB_CMDLINE_LINUX_DEFAULT="" + GRUB_CMDLINE_LINUX="text console=ttyS0,115200n8 console=tty0 net.ifnames=0" + ''')) + + kernel_params = 'ro text console=ttyS0,115200n8' + kernel_params += ' console=tty0 net.ifnames=0' + open8(rootdir + '/boot/grub/grub.cfg', 'w').write(mlstrip( + '''\ + terminal_output serial + serial --speed=115200 --word=8 --parity=no --stop=1 + set default=0 + set timeout=1 + menuentry 'default' {{ + search --set=root --file /boot/grub/grub.cfg --hint hd0,gpt2 + linux /vmlinuz root=PARTUUID={rid} {kp} + initrd /initrd.img + }} + '''.format(rid=rootpartuuid, kp=kernel_params))) + + run(['grub-install', + '--boot-directory=' + rootdir + '/boot', + device]) + + +def make_image(): + device = None + rootdirobj = tempfile.TemporaryDirectory() + rootdir = rootdirobj.name + try: + run(['qemu-img', 'create', '-f', 'qcow2', parameters.image, '20G'], + check=True) + + run(['modprobe', 'nbd', 'max_part=16']) + + for d in sorted(glob.glob('/dev/nbd*'), key=numerical_sort): + if run(['qemu-nbd', '-c', d, parameters.image]).returncode == 0: + device = d + break + else: + print('No free nbd device found for qemu-nbd') + raise SystemExit(1) + + run([ + 'sgdisk', + '--set-alignment=8192', + '--zap-all', + '--new=1:1M:+1M', + '--change-name=1:boot', + '--typecode=1:EF02', + '--largest-new=2', + '--change-name=2:root', + '--typecode=2:8300', + device, + ], check=True) + + run(['partprobe', device]) + run(['mkfs.ext4', '-q', '-L', 'root', device+'p2'], check=True) + run(['mount', device+'p2', rootdir], check=True) + + run([ + 'qemu-debootstrap', + '--arch=' + parameters['arch'], + '--include=' + ','.join(parameters['packages']), + '--components=main,universe', + parameters['release'], + rootdir, + parameters['mirror'], + ], check=True) + + rootpartuuid = (run([ + 'partx', + '--noheadings', + '--show', + '--output', + 'UUID', + device+'p2'], check=True, stdout=subprocess.PIPE) + .stdout + .decode('utf8') + .strip()) + + rootfsuuid = [line.split()[-1] + for line in (run([ 'tune2fs', '-l', device+'p2'], + check=True, stdout=subprocess.PIPE) + .stdout + .decode('utf8') + .splitlines()) + if "filesystem uuid" in line.lower()][0] + + system_customization(device, rootdir, rootpartuuid, rootfsuuid) + + finally: + if rootdir: + run(['umount', rootdir]) + if device: + run(['qemu-nbd', '-d', device]) + + +def main(): + reexec_root() + make_image() + + +if __name__ == '__main__': + main() diff --git a/qemu-headless/make_vm_debootstrap.py b/qemu-headless/make_vm_debootstrap.py new file mode 100755 index 0000000..5317099 --- /dev/null +++ b/qemu-headless/make_vm_debootstrap.py @@ -0,0 +1,299 @@ +#!/usr/bin/python3 + +import os +import sys +import glob +import subprocess +import tempfile +import shutil +import re +import functools + + +parameters = dict( + release='unstable', + #mirror='http://fr.archive.ubuntu.com/ubuntu/', + mirror='http://apt:9999/debian/', + arch='amd64', + image = 'unstable-amd64.qcow2', + packages=''' + apt + aptitude + bash + bash-completion + bind9-host + bmon + busybox + bzip2 + curl + ed + htop + iftop + ifupdown + iotop + iperf + iproute2 + iptables + iputils-ping + less + lftp + locales + ncdu + ncurses-base + ncurses-term + net-tools + netbase + netcat + nload + openssh-client + openssh-server + psmisc + python3 + ranger + rsync + screen + sed + socat + strace + tar + tcpdump + telnet + tmux + tree + tzdata + vim + vim-nox + vim-runtime + w3m + wget + zsh + '''.split(), + kernel_package = 'linux-image-amd64', + serial_console = 'ttyS0', +) + + +open8 = functools.partial(open, encoding='utf8') +numerical_sort = lambda y: [int(x) if x.isdigit() else x + for x in re.split('(\d+)', y)] + + +def mlstrip(s): + return re.sub(r'^\s*', '', s, flags=re.MULTILINE) + + +def run(*l, **kw): + print('run:', *l) + return subprocess.run(*l, **kw) + + +def reexec_root(): + # run as root + if os.geteuid() != 0: + os.execvp("sudo", ["sudo"] + sys.argv) + + +def system_customization(device, rootdir, rootpartuuid, rootfsuuid): + os.unlink(rootdir + '/etc/localtime') + open8(rootdir + '/etc/zoneinfo', 'w').write('Europe/Paris\n') + shutil.copy(rootdir + '/usr/share/zoneinfo/Europe/Paris', + rootdir + '/etc/localtime') + + open8(rootdir + '/etc/network/interfaces', 'w').write(mlstrip( + '''\ + auto lo + iface lo inet loopback + + auto eth0 + iface eth0 inet dhcp + ''')) + open8(rootdir + '/etc/hosts', 'w').write(mlstrip( + '''\ + 127.0.0.1 localhost localhost.localdomain debian + + # the following lines are desirable for IPv6 capable hosts + ::1 localhost ip6-localhost ip6-loopback + ff02::1 ip6-allnodes + ff02::2 ip6-allrouters + ''')) + open8(rootdir + '/etc/hostname', 'w').write('debian\n') + open8(rootdir + '/etc/fstab', 'w').write(mlstrip( + '''\ + # + PARTUUID={rid} / ext4 errors=remount-ro,relatime 0 1 + '''.format(rid=rootpartuuid))) + + # activate serial console on parameter['serial_console'] + console = parameters['serial_console'] + run([ + 'ln', + '-s', + '/lib/systemd/system/serial-getty@.service', + rootdir + '/etc/systemd/system/getty.target.wants/serial-getty@%s.service' % console, + ], check=True) + os.makedirs(rootdir + '/etc/systemd/system/serial-getty@%s.service.d' % console, + mode=0o755) + open8(rootdir + + '/etc/systemd/system/serial-getty@%s.service.d/autologin.conf' % console, + 'w').write(mlstrip( + '''\ + [Service] + ExecStart= + ExecStart=-/sbin/agetty --autologin root --login-pause --noclear %I 115200,38400,9600 vt102 + ''')) + + open8(rootdir + '/etc/default/keyboard', 'w').write(mlstrip( + '''\ + XKBMODEL="pc105" + XKBLAYOUT="fr" + XKBVARIANT="bepo" + XKBOPTIONS="" + ''')) + + open8(rootdir + '/etc/default/locale', 'w').write(mlstrip( + '''\ + LANG="en_US.UTF-8" + LC_TIME="en_DK.UTF-8" + LC_PAPER="en_GB.UTF-8" + LC_MEASUREMENT="en_GB.UTF-8" + ''')) + + for locale in ['fr_FR', 'en_US', 'en_GB', 'en_DK', 'de_DE']: + run([ + 'localedef', + '--prefix={}'.format(rootdir), + '-f', 'UTF-8', + '-i', locale, + '{}.UTF-8'.format(locale) + ], check=True) + + open8(rootdir + '/etc/bash.bashrc', 'a').write(mlstrip( + '''\ + + # 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 + alias ls="ls --color=aut" + alias l="ls -CF" + alias ll="l -lh" + alias la="l -a" + alias e="vim" + alias rm='rm -i' + alias cp='cp -i' + alias mv='mv -i' + export PAGER=less + export EDITOR=vim + export VISUAL=vim + ''')) + + open8(rootdir + '/etc/vim/vimrc', 'w').write(mlstrip( + '''\ + set nocompatible + filetype plugin indent on + set autoindent + set background=dark + set backspace=2 + set hidden + set hlsearch + set ignorecase + set incsearch + set laststatus=2 + set modelines=0 + set nobackup + set nowritebackup + set ruler + set scrolloff=3 + set shiftwidth=4 + set showcmd + set showmatch + set statusline=%<%f%h%m%r%=%l,%c\ %P + set ts=4 + set whichwrap=<,>,[,] + set wildmode=list:full + syntax on + ''')) + + +def make_image(): + device = None + rootdirobj = tempfile.TemporaryDirectory() + rootdir = rootdirobj.name + try: + run(['qemu-img', 'create', '-f', 'qcow2', parameters['image'], '20G'], + check=True) + + run(['modprobe', 'nbd', 'max_part=16']) + + for d in sorted(glob.glob('/dev/nbd*'), key=numerical_sort): + if run(['qemu-nbd', '-c', d, parameters['image']]).returncode == 0: + device = d + break + else: + print('No free nbd device found for qemu-nbd') + raise SystemExit(1) + + run([ + 'sgdisk', + '--set-alignment=8192', + '--zap-all', + '--largest-new=1', + '--change-name=1:root', + '--typecode=1:8300', + device, + ], check=True) + + run(['partprobe', device]) + run(['mkfs.ext4', '-q', '-L', 'root', device+'p1'], check=True) + run(['mount', device+'p1', rootdir], check=True) + + run([ + 'qemu-debootstrap', + '--arch=' + parameters['arch'], + '--include=' + ','.join(parameters['packages'] + + [parameters['kernel_package']]), + '--components=main,universe', + parameters['release'], + rootdir, + parameters['mirror'], + ], check=True) + + rootpartuuid = (run([ + 'partx', + '--noheadings', + '--show', + '--output', + 'UUID', + device+'p1'], check=True, stdout=subprocess.PIPE) + .stdout + .decode('utf8') + .strip()) + + rootfsuuid = [line.split()[-1] + for line in (run([ 'tune2fs', '-l', device+'p1'], + check=True, stdout=subprocess.PIPE) + .stdout + .decode('utf8') + .splitlines()) + if "filesystem uuid" in line.lower()][0] + + system_customization(device, rootdir, rootpartuuid, rootfsuuid) + + finally: + if rootdir: + run(['umount', rootdir]) + if device: + run(['qemu-nbd', '-d', device]) + + +def main(): + reexec_root() + make_image() + + +if __name__ == '__main__': + main() diff --git a/qemu-headless/make_vm_debootstrap_armhf.py b/qemu-headless/make_vm_debootstrap_armhf.py new file mode 100755 index 0000000..69fc807 --- /dev/null +++ b/qemu-headless/make_vm_debootstrap_armhf.py @@ -0,0 +1,11 @@ +#!/usr/bin/python3 + +import make_vm_debootstrap + +make_vm_debootstrap.parameters['arch'] = 'armhf' +make_vm_debootstrap.parameters['image'] = 'unstable-armhf.qcow2' +make_vm_debootstrap.parameters['kernel_package'] = 'linux-image-armmp-lpae' +make_vm_debootstrap.parameters['serial_console'] = 'ttyAMA0' + +if __name__ == '__main__': + make_vm_debootstrap.main() diff --git a/qemu-headless/old-make-vm-debootstrap b/qemu-headless/old-make-vm-debootstrap new file mode 100755 index 0000000..667eead --- /dev/null +++ b/qemu-headless/old-make-vm-debootstrap @@ -0,0 +1,392 @@ +#!/bin/bash + +set -eu + +#if [ "$UID" != "0" ]; then +# exec sudo -- "$0" "$@" +#fi + +traperror() +{ + echo "args: $*" >&2 + echo "trace: ${FUNCNAME[*]} ${BASH_LINENO[*]}" >&2 + exit 1 +} + +fail() { echo "${FUNCNAME[1]} ${BASH_LINENO[0]}: $*" >&2; exit 1; } +#trap 'fail unhandled exception' ERR +trap 'traperror $LINENO ${FUNCNAME:-main} $BASH_LINENO; trap - ERR' ERR + +exec 3>&1 +exec >debootstrap.log +echo "I: log=debootstrap.log">&2 + +RELEASE=unstable +#MIRROR=http://fr.archive.ubuntu.com/ubuntu/ +MIRROR=http://apt:9999/debian/ +ARCH=amd64 +PKGS="$(sed 's/^#.*$//'<&2 + false + echo 'test0-end' >&2 +} + +test0 + +false + +#echo "Warning: will erase $BLOCKDEV with mkfs.ext4 in 10seconds...">&2 +#for i in $(seq 10 -1 0); do sleep 1; printf "%d... " $i>&2; done +#echo>&2 +#mkfs -q -F -t ext4 -L rootfs "$BLOCKDEV" || exit 1 +#mkdir -p "$DESTDIR" +#echo "mounting block device...">&2 +#mount -v -t ext4 "$BLOCKDEV" "$DESTDIR" || \ +# mount -v -t ext4 -o loop "$BLOCKDEV" "$DESTDIR" || exit 1 +#echo "In case of error, please remember to umount $DESTDIR">&2 + +# compacting qcow2 images files +# You have to zerofill the data on the disk first. +# sfill -fllvz +# Then clone the image file +# qemu-img convert -p -O qcow2 ./source.img ./packed. + +#modprobe nbd max_part=16 +#qemu-nbd -c /dev/nbd0 image.qcow2 +#partprobe /dev/nbd0 +#mount /dev/nbd0p1 /mnt/image + +echo "I: debootstraping...">&2 +qemu-debootstrap --arch="$ARCH" \ + --include="$PKGS" --components=main,universe \ + "$RELEASE" "$DESTDIR" "$MIRROR" + +if [ ! -e "$KERNEL" ];then + echo "I: wget kernel...">&2 + wget -q "$MIRROR/dists/$RELEASE/main/installer-$ARCH/current/images/netboot/ubuntu-installer/$ARCH/linux" -O "$KERNEL" +fi + +echo "I: adjust configuration...">&2 +rm -f "$DESTDIR/etc/localtime" +cat "$DESTDIR/usr/share/zoneinfo/Europe/Paris" > "$DESTDIR/etc/localtime" + +# network +cat >"$DESTDIR/etc/network/interfaces"<"$DESTDIR/etc/hosts" +echo "localhost">"$DESTDIR/etc/hostname" +echo "servername 10.1.1.254">"$DESTDIR/etc/resolv.conf" + +for f in "$DESTDIR"/etc/init/tty[1-6].conf; do + echo "manual">${f%.conf}.override +done + +chmod +w "$DESTDIR/etc/sudoers" +sed 's,#* *%sudo.*,%sudo ALL=(ALL) NOPASSWD: ALL,' -i "$DESTDIR/etc/sudoers" +chmod -w "$DESTDIR/etc/sudoers" + +#UUID="$(blkid "$BLOCKDEV" | sed 's/^.*UUID="\([a-zA-Z0-9-]\+\)".*$/\1/')" +cat >"$DESTDIR/etc/fstab"< +/dev/vda / ext4 errors=remount-ro,noatime 0 1 +tmpfs /tmp tmpfs mode=1777,noatime,size=128m,nodev,nosuid,noexec 0 0 +tmpfs /var/log tmpfs mode=1777,noatime,size=4m,nodev,nosuid,noexec 0 0 +tmpfs /var/tmp tmpfs mode=1777,noatime,size=128m,nodev,nosuid,noexec 0 0 + +#UUID=$UUID / ext4 errors=remount-ro,noatime 0 1 +#UUID=use blkid for uuid discovery /home ext4 noatime 0 2 +#UUID=6d9ac889-3c25-4e05-90f3-d183f8b14aca none swap sw,auto 0 0 +EOF + +mkdir -p "$DESTDIR/etc/sv/ttyS0/" +mkdir -p "$DESTDIR/etc/service/" +cat >"$DESTDIR/etc/sv/ttyS0/run"<<"EOF" +#!/bin/bash +dev=/dev/ttyS0 +/bin/stty -F $dev sane clocal -crtscts -hupcl +exec <$dev >$dev 2>$dev +echo +echo "Press enter to continue..."; read +cd /root +/usr/bin/env -i HOME=/root PATH=/usr/sbin:/usr/bin:/sbin:/bin TERM=screen-256color /usr/bin/setsid /bin/bash +exec wait +#exec /bin/bash +#exec /usr/bin/env -i /bin/sh +#exec /usr/bin/setsid /bin/sh +EOF +chmod +x "$DESTDIR/etc/sv/ttyS0/run" +ln -sfv "/etc/sv/ttyS0/" "$DESTDIR/etc/service/" + +mkdir -p "$DESTDIR/etc/ssh" +cat >"$DESTDIR/etc/ssh/sshd_config"< "$DESTDIR/etc/apt/sources.list" < "$DESTDIR/etc/default/console-setup"<"$DESTDIR/etc/default/keyboard"<"$DESTDIR/etc/default/locale"<>"$DESTDIR/etc/bash.bashrc"<"$DESTDIR/etc/vim/vimrc"<,[,] +set scrolloff=1 +EOF + +rm -r "$DESTDIR/etc/skel" +mkdir -p "$DESTDIR/etc/skel" + +cat > "$DESTDIR/init-stage2.sh" << EOF +#!/bin/sh +export PATH="/usr/sbin:/usr/bin:/sbin:/bin" +mount -no remount,rw / +mount -t proc proc /proc +mount -t sysfs sysfs /sys + +/debootstrap/debootstrap --second-stage + +# second stage may unmount this... +[ -e /proc/mounts ] || mount -t proc proc /proc +[ -e /sys/kernel ] || mount -t sysfs sysfs /sys +busybox mdev -s + +echo '------ mounts:' +cat /proc/mounts +echo '------' + +mv /etc/default/extlinux /etc/default/extlinux.dist +cat >/etc/default/extlinux<~root/.bashrc +echo '. ~/.bashrc' >~root/.bash_profile +echo '. /etc/bash.bashrc' >~calendros/.bashrc +echo '. ~/.bashrc' >~calendros/.bash_profile + +locale-gen en_US.UTF-8 en_GB.UTF-8 en_DK.UTF-8 fr_FR.UTF-8 + +# dhclient may erase /etc/resolv.conf, and is not really useful here +#ip link set eth0 up +#dhclient eth0 +#apt-get update + +echo "Dropping a shell, press Ctrl-D or type exit to finish." +/bin/bash + +rm -v /etc/udev/rules.d/70-persistent-*.rules + +echo "Bootstrap ended, poweroff in 10seconds..." +rm /init-stage2.sh +#sleep 10 || exec /bin/bash # for debug +#poweroff & # does not work +sync +fuser -k / +sleep 1 +mount -o remount,ro / +exit 0 # will panic, but does not matter now (don't know how to shutdown o/w) +EOF +chmod +x "$DESTDIR/init-stage2.sh" + +cat >&2 <