aboutsummaryrefslogtreecommitdiffstats
path: root/qemu-headless/make-vm-debootstrap.py.orig
diff options
context:
space:
mode:
Diffstat (limited to 'qemu-headless/make-vm-debootstrap.py.orig')
-rwxr-xr-xqemu-headless/make-vm-debootstrap.py.orig331
1 files changed, 331 insertions, 0 deletions
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(
+ '''\
+ # <device> <mount point> <type> <options> <dump> <pass>
+ 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()