base_role/overlay/Linux/usr/local/bin/toxcore_create-vm.bash

426 lines
14 KiB
Bash
Executable File

#!/bin/bash
# -*- mode: sh; fill-column: 75; tab-width: 8; coding: utf-8-unix -*-
# from https://github.com/earlruby/create-vm/
[ -f /usr/local/bin/usr_local_tput.bash ] && \
. /usr/local/bin/usr_local_tput.bash || {
DBUG() { echo DEBUG $* ; }
INFO() { echo INFO $* ; }
WARN() { echo WARN $* ; }
ERROR() { echo ERROR $* ; }
}
prog=`basename $0 .bash`
PREFIX=/usr/local
ROLE=toxcore
export PATH=$PATH:$PREFIX/bin
have_genisoimage=true
# create-vm - Quickly create guest VMs using cloud image files and cloud-init.
# Copyright 2018-2023 Earl C. Ruby III
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# Set BOX_NBD_OVERLAY_DIR environment variable to override default storage location for VMs
HOSTNAME=
IMG_FQN=
AUTH_KEYS_FQN=
RAM=2048
VCPUS=1
STORAGE=20
BRIDGE=virbr1
MAC='52:54:00:1d:9c:6f'
VERBOSE=
PASS=
OSINFO=gentoo
password=ansible
OUTDIR=${BOX_NBD_OVERLAY_DIRs:-"${HOME}/vms/virsh"}
usage()
{
cat << EOF
usage: $0 options
Quickly create guest VMs using cloud image files and cloud-init.
OPTIONS:
-h Show this message
-n Host name (required)
-i Full path and name of the base .img file to use (required)
-k Full path and name of the ansible user's public key file (required)
-d Output directory for the overlay qcow2 and related files
-r RAM in MB (defaults to ${RAM})
-c Number of VCPUs (defaults to ${VCPUS})
-s Amount of storage to allocate in GB (defaults to ${STORAGE})
-b Bridge interface to use (defaults to ${BRIDGE})
-m MAC address to use (default is to use a randomly-generated MAC)
-o OSINFO name like win11, win10, fedora32, gentoo, ububtu20
-p ansible users plaintext password
-v Verbose
EOF
}
while getopts "h:n:i:k:r:c:s:b:m:o:p:d:v" option; do
case "${option}"
in
h)
usage
exit 0
;;
n) HOSTNAME=${OPTARG};;
i) IMG_FQN=${OPTARG};;
k) AUTH_KEYS_FQN=${OPTARG};;
r) RAM=${OPTARG};;
c) VCPUS=${OPTARG};;
s) STORAGE=${OPTARG};;
b) BRIDGE=${OPTARG};;
m) MAC=${OPTARG};;
p) PASS=${OPTARG};;
o) password=${OPTARG};;
d) OUTDIR=${OPTARG};
BOX_NBD_OVERLAY_DIR=${OUTDIR};;
v) VERBOSE=1;;
*)
ERROR unhandled option "${option}" ${OPTARG}
usage
exit 1
;;
esac
done
if [[ -z $HOSTNAME ]]; then
ERROR "Host name is required"
usage
exit 1
fi
if [[ -z $IMG_FQN ]]; then
ERROR "Base cloud image file name is required"
usage
exit 1
fi
if [[ -z $BOX_NBD_OVERLAY_DIR ]]; then
ERROR "Output image directory is required BOX_NBD_OVERLAY_DIR"
usage
exit 1
fi
if [[ -z $AUTH_KEYS_FQN ]]; then
ERROR "ansible public key file $AUTH_KEYS_FQN not found"
usage
exit 1
fi
if ! [[ -f $IMG_FQN ]]; then
ERROR "$IMG_FQN file not found"
usage
exit 1
fi
if [[ -n $VERBOSE ]]; then
INFO "Building ${HOSTNAME} in $BOX_NBD_OVERLAY_DIR"
set -xv
fi
mkdir -p "$BOX_NBD_OVERLAY_DIR"/{images,xml,init,base} || exit 2
echo "Creating a qcow2 image file ${BOX_NBD_OVERLAY_DIR}/images/${HOSTNAME}.img that uses the cloud image file ${IMG_FQN} as its base"
INFO qemu-img create -b "${IMG_FQN}" -f qcow2 -F qcow2 \
"${BOX_NBD_OVERLAY_DIR}/images/${HOSTNAME}.img" "${STORAGE}G"
qemu-img create -b "${IMG_FQN}" -f qcow2 -F qcow2 \
"${BOX_NBD_OVERLAY_DIR}/images/${HOSTNAME}.img" "${STORAGE}G" || \
exit 3
echo "Creating meta-data file $BOX_NBD_OVERLAY_DIR/init/meta-data"
cat > "$BOX_NBD_OVERLAY_DIR/init/meta-data" << EOF
instance-id: ${HOSTNAME}
local-hostname: ${HOSTNAME}
EOF
# echo "Creating meta-data file $BOX_NBD_OVERLAY_DIR/init/meta-data.json"
# cat > "$BOX_NBD_OVERLAY_DIR/init/meta-data.json" << EOF
cat > /dev/null << EOF
{
"admin_pass": "root",
"availability_zone": "nova",
"hostname": "test.novalocal",
"launch_index": 0,
"name": "gentoo6",
"meta": {
"role": "webservers",
"essential": "false"
},
"public_keys": {
"mykey": " ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDRCJCQ1UD9QslWDSw5Pwsvba0Wsf1pO4how5BtNaZn0xLZpTq2nqFEJshUkd/zCWF7DWyhmNphQ8c+U+wcmdNVcg2pI1kPxq0VZzBfZ7cDwhjgeLsIvTXvU+HVRtsXh4c5FlUXpRjf/x+a3vqFRvNsRd1DE+5ZqQHbOVbnsStk3PZppaByMg+AZZMx56OUk2pZCgvpCwj6LIixqwuxNKPxmJf45RyOsPUXwCwkq9UD4me5jksTPPkt3oeUWw1ZSSF8F/141moWsGxSnd5NxCbPUWGoRfYcHc865E70nN4WrZkM7RFI/s5mvQtuj8dRL67JUEwvdvEDO0EBz21FV/iOracXd2omlTUSK+wYrWGtiwQwEgr4r5bimxDKy9L8UlaJZ+ONhLTP8ecTHYkaU1C75sLX9ZYd5YtqjiNGsNF+wdW6WrXrQiWeyrGK7ZwbA7lagSxIa7yeqnKDjdkcJvQXCYGLM9AMBKWeJaOpwqZ+dOunMDLd5VZrDCU2lpCSJ1M="
},
"uuid": "83679162-1378-4288-a2d4-70e13ec132aa"
}
EOF
# password=`openssl passwd -1 -stdin <<< $password`
echo "Creating user-data file $BOX_NBD_OVERLAY_DIR/init/user-data"
# https://techglimpse.com/nova-boot-instance-with-password/
cat > "$BOX_NBD_OVERLAY_DIR/init/user-data" << EOF
#cloud-config
# password: ansible
# chpasswd: { expire: False }
ssh_pwauth: true
runcmd:
- "rc-update add qemu-guest-agent"
- "chmod 755 /etc/init.d/qemu-guest-agent"
- "/etc/init.d/qemu-guest-agent start"
- "echo /etc/init.d/qemu-guest-agent start >> /etc/rc.local"
users:
- default
- name: ansible
sudo: ["ALL=(ALL) NOPASSWD:ALL"]
groups:
- wheel
- adm
shell: /bin/bash
plain_text_password: "$password"
chpasswd: { expire: False }
homedir: /home/ansible
ssh_pwauth: true
ssh_authorized_keys:
- "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDRCJCQ1UD9QslWDSw5Pwsvba0Wsf1pO4how5BtNaZn0xLZpTq2nqFEJshUkd/zCWF7DWyhmNphQ8c+U+wcmdNVcg2pI1kPxq0VZzBfZ7cDwhjgeLsIvTXvU+HVRtsXh4c5FlUXpRjf/x+a3vqFRvNsRd1DE+5ZqQHbOVbnsStk3PZppaByMg+AZZMx56OUk2pZCgvpCwj6LIixqwuxNKPxmJf45RyOsPUXwCwkq9UD4me5jksTPPkt3oeUWw1ZSSF8F/141moWsGxSnd5NxCbPUWGoRfYcHc865E70nN4WrZkM7RFI/s5mvQtuj8dRL67JUEwvdvEDO0EBz21FV/iOracXd2omlTUSK+wYrWGtiwQwEgr4r5bimxDKy9L8UlaJZ+ONhLTP8ecTHYkaU1C75sLX9ZYd5YtqjiNGsNF+wdW6WrXrQiWeyrGK7ZwbA7lagSxIa7yeqnKDjdkcJvQXCYGLM9AMBKWeJaOpwqZ+dOunMDLd5VZrDCU2lpCSJ1M="
EOF
echo "Adding keys from the public key file $AUTH_KEYS_FQN to the user-data file"
while IFS= read -r key; do
echo " - $key" >> "$BOX_NBD_OVERLAY_DIR/init/user-data"
done < <(grep -v '^ *#' < "$AUTH_KEYS_FQN")
VM_IMAGE_DIR="$BOX_NBD_OVERLAY_DIR"
#old . /usr/local/bin/toxcore_create-ga.sh || exit 4
cat > "$BOX_NBD_OVERLAY_DIR/init/user-data" << \EOF
#!/bin/bash
# typically only executes on first boot
echo "############# user_data executing ##############"
#grep gentoo /etc/shadow
sed -e 's/#-:ALL:ALL/+:gentoo:ALL/' -i /etc/security/access.conf
PW=`echo $PASS | openssl passwd -1 --stdin `
grep -q ^gentoo /etc/passwd || \
useradd --gid 4 --uid 1000 --home-dir /home/gentoo \
--comment Gentoo --password "$PW" \
-G adm,wheel --shell /bin/bash gentoo
usermod --password "$PW" -G adm,wheel gentoo
# root
usermod --password '$1$1Ho4y/W8$5VymfKWWAhLxwkkPZiWTZ1' root
# unlock account
passwd -u gentoo
passwd -u root
sed -e 's/# %wheel /%wheel /' -i /etc/sudoers
sed -e 's/PasswordAuthentication no/PasswordAuthentication yes/' -i /etc//ssh/sshd_config
sed -e 's/PermitRootLogin.*/PermitRootLogin yes/' -i /etc//ssh/sshd_config
grep net.ipv4.ip_forward=1 /etc/sysctl.conf || \
echo net.ipv4.ip_forward=1 >> /etc/sysctl.conf
cd /etc/init.d
[ -e net.eth0 ] || ln -s net.lo net.eth0
for elt in i o linuxPen19 ; do
grep -q $elt /etc/fstab && continue
echo "$elt /mnt/$elt virtiofs defaults 0 0" >> /etc/fstab
done
#grep gentoo /etc/shadow
EOF
echo "Generating the cidata ISO file $BOX_NBD_OVERLAY_DIR/images/${HOSTNAME}-cidata.iso"
(
cd "$BOX_NBD_OVERLAY_DIR/init/"
genisoimage \
-output "$BOX_NBD_OVERLAY_DIR/images/${HOSTNAME}-cidata.img" \
-volid cidata \
-rational-rock \
-joliet \
-input-charset utf-8 \
user-data meta-data
) || exit 5
MACCMD=
if [[ -n $MAC ]]; then
MACCMD="--mac=${MAC}"
fi
[ -f ${BOX_NBD_OVERLAY_DIR}/images/${HOSTNAME}.img ] || exit 5
[ -f $BOX_NBD_OVERLAY_DIR/images/${HOSTNAME}-cidata.img ] || exit 6
# libvirt.libvirtError: /usr/lib/qemu/qemu-bridge-helper --use-vnet --br=-c --fd=31: failed to communicate with bridge helper: stderr=failed to parse default acl file `/etc/qemu/bridge.conf'
if [ ! -f "/etc/qemu/bridge.conf" ] ; then
echo allow $BRIDGE >> "/etc/qemu/bridge.conf"
elif ! grep $BRIDGE "/etc/qemu/bridge.conf" ; then
echo allow $BRIDGE >> "/etc/qemu/bridge.conf"
fi
if [ $BRIDGE = virbr0 ] ; then
network=default
# 192.168.122.248/24
elif [ $BRIDGE = virbr1 ] ; then
network=Whonix-External
else
WARN unrecognized $BRIDGE
fi
if [ "$network" != '' ] ; then
virsh net-list | grep -q $network || \
virsh net-start $network
else
network=default
fi
file=/etc/libvirt/qemu/networks/$network.xml
if [ ! -f $file ] ; then
WARN no network file $file
elif ! grep '<range ' $file ; then
WARN no 'DHCP <range> in network file' $file
fi
declare -a LARGS
LARGS=(
--name="${HOSTNAME}" \
--osinfo "$OSINFO" \
--import \
--disk "path=${BOX_NBD_OVERLAY_DIR}/images/${HOSTNAME}.img,format=qcow2" \
--disk "path=$BOX_NBD_OVERLAY_DIR/images/${HOSTNAME}-cidata.img,device=cdrom" \
--ram="${RAM}" \
--vcpus="${VCPUS}" \
--autostart \
--hvm \
--arch x86_64 \
--accelerate \
--check-cpu \
--force \
--watchdog=default \
--channel type=spicevmc,target.type=virtio,target.name=com.redhat.spice.0 \
--channel type=unix,target.type=virtio,target.name=org.qemu.guest_agent.0 \
--rng /dev/urandom \
--os-variant detect=on,name=$OSINFO \
--noautoconsole \
)
# not type=qemu-vdagent
NETWORK="--network network=$network,model=virtio"
if [ -n "$NETWORK" ] ; then
LARGS+=(
$NETWORK \
)
fi
LARGS+=(
# --graphics spice,listen=socket \
--boot init=/sbin/init
--console pty
--video vga
--memorybacking source.type=memfd,access.mode=shared
--filesystem /,/mnt/linuxPen19 \
)
INFO virt-install "${LARGS[@]}"
# squelch warnings
python3.sh `which virt-install` "${LARGS[@]}" || exit 7
# --debug
#? --shmem name=shmem_server,type="memfd",mode="shared"
# --shmem name=shmem0 ivshmem device is not supported with this QEMU binary
# was --graphics vnc,listen=0.0.0.0
# --osinfo "$OSINFO" \
# Make a backup of the VM's XML definition file
virsh dumpxml "${HOSTNAME}" > "${BOX_NBD_OVERLAY_DIR}/xml/${HOSTNAME}.xml" || exit 8
INFO wrote xml `ls -l ${BOX_NBD_OVERLAY_DIR}/xml/${HOSTNAME}.xml`
if [ -n "$VERBOSE" ]; then
set +xv
fi
# problems: type=qemu-vdagent unix unix=on
# problems: type="spicevmc
# ERROR Unknown --channel options: ['unix']
cp "${BOX_NBD_OVERLAY_DIR}/xml/${HOSTNAME}.xml" \
"${BOX_NBD_OVERLAY_DIR}/xml/${HOSTNAME}.xml".new
cat > /tmp/ga.works <<EOF
<channel type="unix">
<source mode="bind" path="/var/lib/libvirt/qemu/channel/target/domain-25-gentoo1/org.qemu.guest_agent.0"/>
<target type="virtio" name="org.qemu.guest_agent.0" state="connected"/>
<address type="virtio-serial" controller="0" bus="0" port="2"/>
</channel>
EOF
cat > /tmp/sp.works <<EOF
<channel type="spicevmc">
<target type="virtio" name="com.redhat.spice.0" state="disconnected"/>
<address type="virtio-serial" controller="0" bus="0" port="1"/>
</channel>
EOF
# Show running VMs
virsh list | grep "${HOSTNAME}" && INFO "${HOSTNAME}" || {
ERROR "${HOSTNAME}" ; exit 9$? ; }
# use the following passwordless demonstration key for testing or
# replace with your own key pair
#
# -----BEGIN OPENSSH PRIVATE KEY-----
# b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABlwAAAAdzc2gtcn
# NhAAAAAwEAAQAAAYEA0QiQkNVA/ULJVg0sOT8LL22tFrH9aTuIaMOQbTWmZ9MS2aU6tp6h
# RCbIVJHf8wlhew1soZjaYUPHPlPsHJnTVXINqSNZD8atFWcwX2e3A8IY4Hi7CL0171Ph1U
# bbF4eHORZVF6UY3/8fmt76hUbzbEXdQxPuWakB2zlW57ErZNz2aaWgcjIPgGWTMeejlJNq
# WQoL6QsI+iyIsasLsTSj8ZiX+OUcjrD1F8AsJKvVA+JnuY5LEzz5Ld6HlFsNWUkhfBf9eN
# ZqFrBsUp3eTcQmz1FhqEX2HB3POuRO9JzeFq2ZDO0RSP7OZr0Lbo/HUS+uyVBML3bxAztB
# Ac9tRVf4jq2nF3dqJpU1EivsGK1hrYsEMBIK+K+W4psQysvS/FJWiWfjjYS0z/HnEx2JGl
# NQu+bC1/WWHeWLao4jRrDRfsHVulq160Ilnsqxiu2cGwO5WoEsSGu8nqpyg43ZHCb0FwmB
# izPQDASlniWjqcKmfnTrpzAy3eVWawwlNpaQkidTAAAFgGKSj8diko/HAAAAB3NzaC1yc2
# EAAAGBANEIkJDVQP1CyVYNLDk/Cy9trRax/Wk7iGjDkG01pmfTEtmlOraeoUQmyFSR3/MJ
# YXsNbKGY2mFDxz5T7ByZ01VyDakjWQ/GrRVnMF9ntwPCGOB4uwi9Ne9T4dVG2xeHhzkWVR
# elGN//H5re+oVG82xF3UMT7lmpAds5VuexK2Tc9mmloHIyD4BlkzHno5STalkKC+kLCPos
# iLGrC7E0o/GYl/jlHI6w9RfALCSr1QPiZ7mOSxM8+S3eh5RbDVlJIXwX/XjWahawbFKd3k
# 3EJs9RYahF9hwdzzrkTvSc3hatmQztEUj+zma9C26Px1EvrslQTC928QM7QQHPbUVX+I6t
# pxd3aiaVNRIr7BitYa2LBDASCvivluKbEMrL0vxSVoln442EtM/x5xMdiRpTULvmwtf1lh
# 3li2qOI0aw0X7B1bpatetCJZ7KsYrtnBsDuVqBLEhrvJ6qcoON2Rwm9BcJgYsz0AwEpZ4l
# o6nCpn5066cwMt3lVmsMJTaWkJInUwAAAAMBAAEAAAGAEuz77Hu9EEZyujLOdTnAW9afRv
# XDOZA6pS7yWEufjw5CSlMLwisR83yww09t1QWyvhRqEyYmvOBecsXgaSUtnYfftWz44apy
# /gQYvMVELGKaJAC/q7vjMpGyrxUPkyLMhckALU2KYgV+/rj/j6pBMeVlchmk3pikYrffUX
# JDY990WVO194Dm0buLRzJvfMKYF2BcfF4TvarjOXWAxSuR8www050oJ8HdKahW7Cm5S0po
# FRnNXFGMnLA62vN00vJW8V7j7vui9ukBbhjRWaJuY5rdG/UYmzAe4wvdIEnpk9xIn6JGCp
# FRYTRn7lTh5+/QlQ6FXRP8Ir1vXZFnhKzl0K8Vqh2sf4M79MsIUGAqGxg9xdhjIa5dmgp8
# N18IEDoNEVKUbKuKe/Z5yf8Z9tmexfH1YttjmXMOojBvUHIjRS5hdI9NxnPGRLY2kjAzcm
# gV9Rv3vtdF/+zalk3fAVLeK8hXK+di/7XTvYpfJ2EZBWiNrTeagfNNGiYydsQy3zjZAAAA
# wBNRak7UrqnIHMZn7pkCTgceb1MfByaFtlNzd+Obah54HYIQj5WdZTBAITReMZNt9S5NAR
# M8sQB8UoZPaVSC3ppILIOfLhs6KYj6RrGdiYwyIhMPJ5kRWF8xGCLUX5CjwH2EOq7XhIWt
# MwEFtd/gF2Du7HUNFPsZGnzJ3e7pDKDnE7w2khZ8CIpTFgD769uBYGAtk45QYTDo5JroVM
# ZPDq08Gb/RhIgJLmIpMwyreVpLLLe8SwoMJJ+rihmnJZxO8gAAAMEA0lhiKezeTshht4xu
# rWc0NxxD84a29gSGfTphDPOrlKSEYbkSXhjqCsAZHd8S8kMr3iF6poOk3IWSvFJ6mbd3ie
# qdRTgXH9Thwk4KgpjUhNsQuYRHBbI59Mo+BxSI1B1qzmJSGdmCBL54wwzZmFKDQPQKPxiL
# n0Mlc7GooiDMjT1tbuW/O1EL5EqTRqwgWPTKhBA6r4PnGF150hZRIMooZkD2zX6b1sGojk
# QpvKkEykTwnKCzF5TXO8+wJ3qbcEo9AAAAwQD+Z0r68c2YMNpsmyj3ZKtZNPSvJNcLmyD/
# lWoNJq3djJN4s2JbK8l5ARUdW3xSFEDI9yx/wpfsXoaqWnygP3PoFw2CM4i0EiJiyvrLFU
# r3JLfDUFRy3EJ24RsqbigmEsgQOzTl3xfzeFPfxFoOhokSvTG88PQji1AYHz5kA7p6Zfaz
# Ok11rJYIe7+e9B0lhku0AFwGyqlWQmS/MhIpnjHIk5tP4heHGSmzKQWJDbTskNWd6aq1G7
# 6HWfDpX4HgoM8AAAALaG9sbWFuYkBhcmM=
# -----END OPENSSH PRIVATE KEY-----
#