summaryrefslogtreecommitdiff
path: root/install-VM-grongo
diff options
context:
space:
mode:
Diffstat (limited to 'install-VM-grongo')
-rwxr-xr-xinstall-VM-grongo417
1 files changed, 417 insertions, 0 deletions
diff --git a/install-VM-grongo b/install-VM-grongo
new file mode 100755
index 0000000..f3599e0
--- /dev/null
+++ b/install-VM-grongo
@@ -0,0 +1,417 @@
+#!/bin/bash
+
+# create a VM image using debootstrap
+#
+# Copyright 2013, 2014, 2015 Peter Palfrader <peter@palfrader.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You probably already have access to a copy of the GNU General Public
+# License or you can find one on the Internet; if not, write to the
+# copyright holder or to the Free Software Foundation, Inc., 59 Temple
+# Place, Suite 330, Boston, MA 02111-1307 USA.
+
+set -e
+set -u
+
+SUITE=jessie
+hostlistfile=/root/VMs/host-list
+
+MIRROR=http://ftp.at.debian.org/debian
+prefixlen=26
+gateway=213.235.231.193
+ip6prefix=2001:858:10f:100::
+ip6gateway=fe80::
+basedir=/srv/vmstore
+
+check_installed() {
+ local p
+ for p in "$@"; do
+ if ! dpkg -l "$p" 2>/dev/null | grep -q '^ii'; then
+ echo >&2 "Error: package $p not installed:"
+ echo >&2 " apt-get install $*"
+ exit 1
+ fi
+ done
+}
+get_sshkey_fprs() {
+ local f
+ for f in etc/ssh/ssh_host*_key.pub; do
+ echo -n " "
+ ssh-keygen -l -f "$f"
+ done
+}
+
+do_cleanup() {
+ #echo "Proposed cleanup:"
+ local cnt
+ cnt=$((${#cleanup[*]}-1))
+ #for i in $(seq ${cnt} -1 0); do
+ # echo " ${cleanup[$i]}"
+ #done
+ #echo "Press enter to commence cleanup."
+ #read
+ for i in $(seq ${cnt} -1 0); do
+ echo "* ${cleanup[$i]}"
+ eval "${cleanup[$i]}" || true
+ done
+ echo "done."
+}
+
+check_installed debootstrap debian-archive-keyring kpartx
+
+declare -a cleanup
+cleanup+=(":")
+trap do_cleanup EXIT
+
+echo -n "New VM's FQDN: "
+if [ -n "${1:-}" ]; then echo "$1"; fqdn="$1"; shift; else read fqdn; fi
+echo
+guest="${fqdn%%.*}"
+domainname="${fqdn#*.}"
+
+echo -n "Disk size: [10g]"
+if [ -n "${1:-}" ]; then echo "$1"; disksize="$1"; shift; else read disksize; fi
+disksize=${disksize:-10g}
+
+echo -n "Swap size: [4g]"
+if [ -n "${1:-}" ]; then echo "$1"; swapsize="$1"; shift; else read swapsize; fi
+swapsize=${swapsize:-4g}
+
+echo -n "Lvm size: [20g]"
+if [ -n "${1:-}" ]; then echo "$1"; lvmsize="$1"; shift; else read lvmsize; fi
+lvmsize=${lvmsize:-20g}
+if [ "$lvmsize" = "0" ]; then lvmsize=""; fi
+
+echo -n "ipaddr: ";
+if [ -n "${1:-}" ]; then echo "$1"; ipaddr="$1"; shift; else read ipaddr; fi
+
+echo -n "unique hostno (2-254): ";
+if [ -n "${1:-}" ]; then echo "$1"; hostno="$1"; shift; else read hostno; fi
+
+if awk -v v="$hostno" '$1 == v {print}' "$hostlistfile" | grep .; then
+ echo >&2 "Host number $hostno already used in $hostlistfile."
+ exit 1
+fi
+if awk -v v="$ipaddr" '$2 == v {print}' "$hostlistfile" | grep .; then
+ echo >&2 "IP address $ipaddr already used in $hostlistfile."
+ exit 1
+fi
+if awk -v v="$fqdn" '$3 == v {print}' "$hostlistfile" | grep .; then
+ echo >&2 "FQDN $fqdn already used in $hostlistfile."
+ exit 1
+fi
+
+diskfileprefix="$basedir/$fqdn/"
+if [ -e "$diskfileprefix" ]; then
+ echo >&2 "Error: Disk directory $diskfileprefix already exists."
+ exit 1
+fi
+
+target="/mnt/target-$guest"
+if [ -e "$target" ]; then
+ echo >&2 "Error: Directory $target already exists."
+ exit 1
+fi
+
+
+diskfileroot="${diskfileprefix}$guest-root"
+diskfileswap="${diskfileprefix}$guest-swap"
+diskfilelvm="${diskfileprefix}$guest-lvm"
+
+mkdir "$diskfileprefix"
+(umask 077 && qemu-img create -f qcow2 "$diskfileroot" "$disksize")
+(umask 077 && qemu-img create -f raw "$diskfileswap" "$swapsize")
+if [ "$lvmsize" != "" ] ; then
+ (umask 077 && qemu-img create -f qcow2 "$diskfilelvm" "$lvmsize")
+fi
+
+modprobe nbd max_part=63
+
+rootdev=/dev/nbd0
+lvmdev=/dev/nbd1
+
+qemu-nbd -c "$rootdev" "$diskfileroot"
+cleanup+=("qemu-nbd -d '$rootdev'")
+
+if [ "$lvmsize" != "" ] ; then
+ qemu-nbd -c "$lvmdev" "$diskfilelvm"
+ cleanup+=("qemu-nbd -d '$lvmdev'")
+fi
+
+if [ "$(head -c 65536 "$rootdev" | sha1sum | awk '{print $1}')" != "1adc95bebe9eea8c112d40cd04ab7a8d75c4f961" ]; then
+ echo -n "Warning: Disk appears to be not be empty. Continue anyway? [y/N] "
+ read ans
+ [ "$ans" = "y" ] || exit 0
+fi
+
+ip6addr="$ip6prefix$hostno:1"
+
+echo '2048,,L,*' | sfdisk -u S --Linux "$rootdev"
+kpartx -v -p "-p" -a "$rootdev" -s
+cleanup+=("kpartx -d -p '-p' -v '$rootdev'")
+part1="/dev/mapper/$(basename $rootdev)-p1"
+mkfs.ext4 "$part1"
+
+if [ "$lvmsize" != "" ] ; then
+ pvcreate "$lvmdev"
+ vgcreate "vg_$guest" "$lvmdev"
+fi
+
+mkdir "$target"
+cleanup+=("rmdir '$target'")
+mount "$part1" "$target"
+cleanup+=("umount '$target'")
+cd "$target"
+cleanup+=("cd /")
+
+debootstrap --variant=minbase --keyring=/usr/share/keyrings/debian-archive-keyring.gpg "$SUITE" . "$MIRROR"
+
+### Set up swap and fstab
+mkswap "$diskfileswap"
+
+uuidroot=$(blkid -s UUID -o value ${part1}) &&
+uuidswap=$(blkid -s UUID -o value $diskfileswap) &&
+cat > etc/fstab << EOF
+UUID=$uuidroot / ext4 errors=remount-ro 0 1
+UUID=$uuidswap none swap sw 0 0
+tmpfs /tmp tmpfs defaults,size=512m 0 0
+EOF
+
+### Set up basic networking stuff
+echo "$guest" > etc/hostname
+cat > etc/hosts << EOF
+127.0.0.1 localhost
+$ipaddr $fqdn $guest
+
+# The following lines are desirable for IPv6 capable hosts
+::1 localhost ip6-localhost ip6-loopback
+fe00::0 ip6-localnet
+ff00::0 ip6-mcastprefix
+ff02::1 ip6-allnodes
+ff02::2 ip6-allrouters
+ff02::3 ip6-allhosts
+EOF
+rm -fv etc/udev/rules.d/70-persistent-*
+mkdir -p etc/udev/rules.d/
+touch etc/udev/rules.d/75-persistent-net-generator.rules
+
+cat > etc/network/interfaces << EOF
+auto lo
+iface lo inet loopback
+
+allow-hotplug eth0
+iface eth0 inet static
+ pre-up echo 0 > /proc/sys/net/ipv6/conf/\$IFACE/accept_ra
+ address $ipaddr/$prefixlen
+ gateway $gateway
+
+allow-hotplug eth1
+iface eth1 inet static
+ address 172.22.130.$hostno/23
+iface eth1 inet6 static
+ address $ip6addr/64
+ gateway $ip6gateway
+ accept_ra 0
+EOF
+
+cat > etc/resolv.conf << EOF
+nameserver 172.22.130.1
+search $domainname
+EOF
+
+### A couple packages
+mv etc/apt/sources.list etc/apt/sources.list.d/debian.list
+echo "deb http://security.debian.org/ jessie/updates main" > etc/apt/sources.list.d/security.list
+echo "deb $MIRROR jessie-updates main" > etc/apt/sources.list.d/updates.list
+chroot . apt-get update
+echo "Apt::Install-Recommends 0;" > etc/apt/apt.conf.d/local-recommends
+chroot . apt-get install -y locales-all net-tools iproute ifupdown dialog vim netbase udev psmisc usbutils pciutils
+chroot . apt-get install -y iputils-ping telnet bind9-host lvm2
+sed -i -e 's/issue_discards = 0/issue_discards = 1/' etc/lvm/lvm.conf
+
+### Set up kernel and bootloader
+chroot . apt-get install -y linux-image-amd64
+DEBIAN_FRONTEND=noninteractive chroot . apt-get install -y grub2
+
+! [ -e dev/sda ]
+! [ -e dev/sda1 ]
+cp -av `readlink -f "$rootdev"` dev/new-root
+cp -av `readlink -f "$part1"` dev/new-root1
+chroot . grub-install --modules=part_msdos /dev/new-root
+rm -v dev/new-root*
+
+cp -av `readlink -f "$rootdev"` dev/sda
+cp -av `readlink -f "$part1"` dev/sda1
+rm -f boot/grub/device.map
+sed -i -e 's/^GRUB_CMDLINE_LINUX=""$/GRUB_CMDLINE_LINUX="console=tty0 console=ttyS0,115200n8"/' etc/default/grub
+cat >> etc/default/grub <<EOF
+GRUB_TERMINAL="serial console"
+GRUB_SERIAL_COMMAND="serial --unit=0 --speed=115200 --word=8 --parity=no --stop=1"
+EOF
+chroot . update-grub
+rm -v dev/sda*
+
+rootpw="$(head -c 12 /dev/urandom | base64)"
+echo "root:$rootpw" | chroot . chpasswd
+
+### install ssh
+chroot . apt-get install -y ssh
+sed -i -e "s/`hostname`\$/$guest/" etc/ssh/ssh_host*_key.pub
+sshkeys="$(get_sshkey_fprs)"
+rsahostkey="$(cat etc/ssh/ssh_host_rsa_key.pub)"
+[ -e etc/ssh/ssh_host_ed25519_key.pub ] && ed25519hostkey="$(cat etc/ssh/ssh_host_ed25519_key.pub)"
+mkdir -p root/.ssh
+cp /root/.ssh/authorized_keys root/.ssh
+
+### unattended upgrades
+chroot . apt-get install -y unattended-upgrades
+cat > etc/apt/apt.conf.d/20auto-upgrades << EOF
+APT::Periodic::Update-Package-Lists "1";
+APT::Periodic::Unattended-Upgrade "1";
+EOF
+
+## trim
+mkdir -p etc/cron.daily
+tee > etc/cron.daily/local-trim-ext4 << 'EOF'
+#!/bin/sh
+
+# by weasel
+
+for fs in `awk '$9 == "ext4" && $4 == "/" {print $5}' /proc/self/mountinfo`; do
+ fstrim "$fs"
+done
+EOF
+chmod +x etc/cron.daily/local-trim-ext4
+
+### first boot
+cat > first-boot.sh << EOF
+#!/bin/bash
+set -e
+sleep 10
+apt-get install -y acpi-support-base lldpd
+apt-get install -y libpam-systemd dbus
+apt-get install -y cron
+apt-get install -y rsyslog
+sed -i -e '/first-boot.sh/d' etc/rc.local
+/etc/cron.daily/local-trim-ext4
+rm /first-boot.sh
+(sleep 10 ; /sbin/reboot) &
+disown %1
+EOF
+chmod +x first-boot.sh
+sed -i -e '$ i [ -x /first-boot.sh ] && /first-boot.sh' etc/rc.local
+
+
+### clean up
+chroot . apt-get clean
+
+# prepare virsh file
+( : #)
+cat << EOF
+<domain type='kvm'>
+ <name>$guest</name>
+ <uuid>$(uuidgen)</uuid>
+ <memory unit='MiB'>2048</memory>
+ <vcpu placement='static'>2</vcpu>
+ <os>
+ <type arch='x86_64' machine='pc-i440fx-2.1'>hvm</type>
+ <boot dev='hd'/>
+ </os>
+ <features>
+ <acpi/>
+ <apic/>
+ <pae/>
+ </features>
+ <cpu mode='custom' match='exact'>
+ <model fallback='allow'>SandyBridge</model>
+ </cpu>
+ <clock offset='utc'>
+ <timer name='rtc' tickpolicy='catchup'/>
+ <timer name='pit' tickpolicy='delay'/>
+ <timer name='hpet' present='no'/>
+ </clock>
+ <on_poweroff>destroy</on_poweroff>
+ <on_reboot>restart</on_reboot>
+ <on_crash>restart</on_crash>
+ <pm>
+ <suspend-to-mem enabled='no'/>
+ <suspend-to-disk enabled='no'/>
+ </pm>
+ <devices>
+ <emulator>/usr/bin/kvm</emulator>
+ <controller type='scsi' index='0' model='virtio-scsi'/>
+ <disk type='file' device='disk'>
+ <driver name='qemu' type='qcow2' discard='unmap'/>
+ <source file='$diskfileroot'/>
+ <target dev='sda' bus='scsi'/>
+ </disk>
+ <disk type='file' device='disk'>
+ <driver name='qemu' type='raw' discard='unmap'/>
+ <source file='$diskfileswap'/>
+ <target dev='sdb' bus='scsi'/>
+ </disk>
+EOF
+[ "$lvmsize" = "" ] || cat << EOF
+ <disk type='file' device='disk'>
+ <driver name='qemu' type='qcow2' discard='unmap'/>
+ <source file='$diskfilelvm'/>
+ <target dev='sdc' bus='scsi'/>
+ </disk>
+EOF
+cat << EOF
+ <interface type='bridge'>
+ <mac address='CC:7F:AE:65:$(printf "%02x" $hostno):01'/>
+ <source bridge='br-inet'/>
+ <virtualport type='openvswitch'/>
+ <model type='virtio'/>
+ </interface>
+ <interface type='bridge'>
+ <mac address='CC:7F:AE:65:$(printf "%02x" $hostno):02'/>
+ <source bridge='br-vmint'/>
+ <virtualport type='openvswitch'/>
+ <model type='virtio'/>
+ </interface>
+ <serial type='pty'>
+ <target port='0'/>
+ </serial>
+ <console type='pty'>
+ <target type='serial' port='0'/>
+ </console>
+ <input type='mouse' bus='ps2'/>
+ <input type='keyboard' bus='ps2'/>
+ <graphics type='vnc' port='-1' autoport='yes'/>
+ <memballoon model='virtio'/>
+ </devices>
+</domain>
+EOF
+) > "$diskfileprefix/$guest.xml"
+
+# and done
+trap - EXIT
+do_cleanup
+
+echo "$guest's root password is $rootpw"
+echo "SSH host key fingerprints are:"
+echo "$sshkeys"
+echo "IP addresses:"
+echo " $ipaddr"
+echo " $ip6addr"
+echo
+echo "Maybe run"
+echo " echo '$hostno' '$ipaddr' '$fqdn' >> '$hostlistfile'"
+echo " virsh define '$diskfileprefix/$guest.xml' && rm '$diskfileprefix/$guest.xml'"
+echo " virsh autostart '$guest'"
+echo " virsh start '$guest'"
+echo " virsh console '$guest'"
+echo
+echo ' for i in `/usr/local/sbin/vm_du_ suggest`; do ln -vsf /usr/local/sbin/vm_du_ /etc/munin/plugins/vm_du_$i; done'
+echo ' service munin-node restart; sleep 3; service munin-async restart'