nlvmi/nlvmi

348 lines
15 KiB
Bash
Executable File

#!/bin/bash
#getting the config
source /etc/nlvmi/nlvmi.conf
if [ $LOGLEVEL -gt "1" ]; then echo "configuration loaded"; fi
#checking directories
#echo $VMDIRECTORY | while read line; do IFS='|' read -r -a array
# for dir in "${array[@]}"; do
# if [ ! -d $dir ]; then echo "VMDIRECTORY wrong, $dir does not exist"; exit 1; fi
# done
#done <<< "$line"
#check if we are root and wrap if we are not
U=`/usr/bin/whoami`
if [ $U != "root" ]; then
if [ ! -z $1 ]; then A=$1; else A="bla"; fi
if [ ! -z $2 ]; then B=$2; else B="bla"; fi
if [ ! -z $3 ]; then C=$3; else C="bla"; fi
/usr/bin/wrap-nlvmi $A $B $C && exit
fi
if [ ! -d $RUNDIRECTORY ]; then if [ $LOGLEVEL -gt "1" ]; then echo "creating $RUNDIRECTORY"; fi; mkdir -p $RUNDIRECTORY; chown root:nlvmi -R $RUNDIRECTORY; chmod g+w $RUNDIRECTORY; fi
#checking database
function checkdb {
if [ $DATABASETYPE == "sqlite" ]; then
if [ ! -e $SQLITEFILE ]; then echo "sqlite configured, but db-file not present! create the db first!"; exit 1; fi
SQCHECK=`sqlite3 $SQLITEFILE "SELECT name FROM sqlite_master WHERE type='table' AND name='vms'"`
if [ $SQCHECK == "vms" ]; then
if [ $LOGLEVEL -gt "1" ]; then echo "DB ready to use"; fi
else
echo "error accessing sqlite!"
exit 1;
fi
echo "sqlite ready to use";
elif [ $DATABASETYPE == "mariadb" ]; then
DBCON="mysql --disable-pager -u $MARIAUSER -p$MARIAPASS -h $MARIAHOST $MARIADB"
declare -a DBC=`echo "SHOW TABLES FROM $MARIADB; " | $DBCON | sed 1d`
if [ -z $DBC ]; then
echo "mariadb connection failed"; exit 1
fi
fi
return 0
}
#creating database
function createdb {
if [ $LOGLEVEL -gt "1" ]; then echo "going to create the db"; fi
if [ $DATABASETYPE == "sqlite" ]; then
sqlite3 $SQLITEFILE "CREATE TABLE vms (
id INTEGER PRIMARY KEY AUTOINCREMENT,
vmname VARCHAR(50) NOT NULL,
user VARCHAR(50) NOT NULL,
server VARCHAR(50),
arch VARCHAR(50) NOT NULL,
cputype VARCHAR(50),
cpus INTEGER,
memory INTEGER,
usbdev VARCHAR(50),
kblang VARCHAR(50),
custom VARCHAR(50),
bootoption VARCHAR(2) NOT NULL,
autostart INTEGER,
drive1 VARCHAR(50),
format1 VARCHAR(50),
drive2 VARCHAR(50),
format2 VARCHAR(50),
drive3 VARCHAR(50),
format3 VARCHAR(50),
cdrom VARCHAR(50),
tapdev1 VARCHAR(50),
macaddr1 VARCHAR(50),
brdev1 VARCHAR(50),
tapdev2 VARCHAR(50),
macaddr2 VARCHAR(50),
brdev2 VARCHAR(50),
tapdev3 VARCHAR(50),
macaddr3 VARCHAR(50),
brdev3 VARCHAR(50),
vncport INTEGER,
websocket INTEGER,
vncpassword VARCHAR(50),
prio INTEGER
);"
sqlite3 $SQLITEFILE "CREATE TABLE users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username VARCHAR(50) NOT NULL,
password VARCHAR(255),
admin INTEGER
);"
sqlite3 $SQLITEFILE "CREATE TABLE servers (
id INTEGER PRIMARY KEY AUTOINCREMENT,
hostname VARCHAR(50) NOT NULL,
connectstring VARCHAR(50),
vmdirectory VARCHAR(255),
isodirectory VARCHAR(255)
);"
chown -R nobody $SQLITEFILE
fi
checkdb && echo "DB creation successful!" || (echo "Failed to create the DB"; exit 1)
}
#create signing key
function createkey {
openssl req -nodes -x509 -sha256 -newkey rsa:4096 -keyout "/etc/nlvmi/nlvmi_priv.key" -out "/etc/nlvmi/nlvmi_sign.crt" -days 9999 -subj "/CN=nlvmi_signing"
}
#check if VM is running
function checkvm {
if [ -z $1 ]; then echo "checkvm needs one argument!"; exit 1; fi
sqlite3 $SQLITEFILE "SELECT connectstring from vms LEFT JOIN servers ON vms.server = servers.hostname WHERE vmname='$1'" | while read line; do
CONN=$line
if [ ! -z $CONN ]; then
su nlvmi -c "ssh $CONN \"RPID=\\\$(<\\\"$RUNDIRECTORY/$1.pid\\\"); ps -ef | grep -v grep | grep \\\$RPID | grep qemu\"" &>/dev/null && echo "VM is running" || echo "VM is not running"
else
ps -ef | grep `cat $RUNDIRECTORY/$1.pid` | grep -v grep >/dev/null && echo "VM is running" || echo "VM is not running"
fi
done
}
#autostart VMs
function vmautostart {
if [ -z $MASTERSERVER ]; then
for startfile in /home/nlvmi/start*.tmp; do
vmname=`echo $startfile | cut -d "." -f1 | rev | cut -d "/" -f 1 | rev | sed 's/start//g'`
checkvm ${vmname} | grep "is not running" >/dev/null && sh $startfile;
done
else
for startsign in /home/nlvmli/start*.tmp.sha512; do
startfile=`echo $startsign | cut -d "." -f 1-2`
vmname=`echo $startfile | cut -d "." -f1 | rev | cut -d "/" -f1 | rev | sed 's/start//g'`
checkvm ${vmname} | grep "is not running" >/dev/null && if [ -e $startfile ]; then
openssl dgst -sha512 -verify <(openssl x509 -in "/etc/nlvmi/nlvmi_sign.crt" -pubkey -noout) -signature $startsign $startfile >/dev/null && /bin/bash $2 || echo "signature failed!!"
fi
done
fi
}
#start single VM
function vmstart {
if [ -z $1 ]; then echo "function start needs an id!"; exit 1; fi
if [ $LOGLEVEL -gt "1" ]; then echo "startvm function entered for vmid $1"; fi
sqlite3 $SQLITEFILE "SELECT * FROM vms LEFT JOIN servers on vms.server = servers.hostname WHERE vms.id=$1" | while read line; do
IFS='|' read -r -a array <<< "$line"
#prepare tmpfile
TMPF=/home/nlvmi/start${array[32]}-${array[1]}.tmp
rm /home/nlvmi/start*-${array[1]}.tmp &>/dev/null
touch $TMPF && chmod 660 $TMPF
#add user if necessary
echo "if ! \`id -u ${array[2]} &>/dev/null\`; then useradd ${array[2]} -d /run/nlvmi -g nlvmi -M -s /bin/false -G kvm; fi" >>$TMPF
#chown mon and pid files
echo "if [ -e $RUNDIRECTORY/${array[1]}.mon ] || [ -e $RUNDIRECTORY/${array[1]}.pid ]; then chown ${array[2]}:nlvmi $RUNDIRECTORY/${array[1]}.*; fi" >>$TMPF
if [ ! -z ${array[5]} ]; then CPUTYPE="-cpu ${array[5]}"; fi
if [ ! -z ${array[6]} ]; then SMP="-smp ${array[6]}"; fi
if [ ! -z ${array[7]} ]; then MEMORY="-m ${array[7]}"; fi
if [ ! -z ${array[8]} ]; then USBDEV="-usb -device usb-ehci,id=ehci -device usb-${array[8]},bus=usb-bus.0"; fi
if [ ! -z ${array[9]} ]; then KB="-k ${array[9]}"; fi
if [ ! -z ${array[10]} ]; then CUSTOM="${array[10]}"; fi
if [ ! -z ${array[13]} ]; then DRIVE1="-drive file=${array[13]}"; echo "chown ${array[2]}:nlvmi ${array[13]}" >>$TMPF; fi
if [ ! -z ${array[14]} ]; then FORMAT1=",format=${array[14]},if=virtio"; fi
if [ ! -z ${array[15]} ]; then DRIVE2="-drive file=${array[15]}"; echo "chown ${array[2]}:nlvmi ${array[15]}" >>$TMPF; fi
if [ ! -z ${array[16]} ]; then FORMAT2=",format=${array[16]},if=virtio"; fi
if [ ! -z ${array[17]} ]; then DRIVE3="-drive file=${array[17]}"; echo "chown ${array[2]}:nlvmi ${array[17]}" >>$TMPF; fi
if [ ! -z ${array[18]} ]; then FORMAT3=",format=${array[18]},if=virtio"; fi
if [ ! -z ${array[19]} ]; then CDROM="-cdrom ${array[19]}"; fi
if [ ! -z ${array[20]} ]; then TAPDEV1="-netdev tap,ifname=${array[20]},script=no,id=net0"; echo "tunctl -t ${array[20]} -u ${array[2]} && ifconfig ${array[20]} up" >>$TMPF; fi
if [ ! -z ${array[21]} ]; then MACADDR1="-net nic,macaddr=${array[21]},model=virtio,netdev=net0"; fi
if [ ! -z ${array[22]} ]; then BRDEV1="${array[22]}"; echo "/sbin/brctl addif ${array[22]} ${array[20]}" >>$TMPF; fi
if [ ! -z ${array[23]} ]; then TAPDEV2="-netdev tap,ifname=${array[23]},script=no,id=net1"; echo "tunctl -t ${array[23]} -u ${array[2]} && ifconfig ${array[23]} up" >>$TMPF; fi
if [ ! -z ${array[24]} ]; then MACADDR2="-net nic,macaddr=${array[24]},model=virtio,netdev=net1"; fi
if [ ! -z ${array[25]} ]; then BRDEV2="${array[25]}"; echo "/sbin/brctl addif ${array[25]} ${array[23]}" >>$TMPF; fi
if [ ! -z ${array[26]} ]; then TAPDEV3="-netdev tap,ifname=${array[26]},script=no,id=net2"; echo "tunctl -t ${array[26]} -u ${array[2]} && ifconfig ${array[26]} up" >>$TMPF; fi
if [ ! -z ${array[27]} ]; then MACADDR3="-net nic,macaddr=${array[27]},model=virtio,netdev=net2"; fi
if [ ! -z ${array[28]} ]; then BRDEV3="${array[28]}"; echo "/sbin/brctl addif ${array[28]} ${array[26]}" >>$TMPF; fi
if [ ! -z ${array[30]} ]; then WEBSOCK=",websocket=${array[30]}"; else WEBSOCK=""; fi
if [ ! -z ${array[31]} ]; then VNCPASS=",password"; fi
if [ ! -z ${array[29]} ]; then VNCPORT="-vnc :${array[29]}$WEBSOCK$VNCPASS"; fi
COMMAND=(su - ${array[2]} -s /bin/bash -c \"${array[4]} -name ${array[1]} -enable-kvm ${array[10]} $CPUTYPE $MEMORY $SMP -device virtio-rng-pci -boot ${array[11]} $USBDEV $KB -daemonize $DRIVE1$FORMAT1 $DRIVE2$FORMAT2 $DRIVE3$FORMAT3 $CDROM $MACADDR1 $TAPDEV1 $MACADDR2 $TAPDEV2 $MACADDR3 $TAPDEV3 $VNCPORT $CUSTOM -pidfile $RUNDIRECTORY/${array[1]}.pid -monitor unix:$RUNDIRECTORY/${array[1]}.mon,server,nowait\")
if [ $LOGLEVEL -gt "1" ]; then echo ${COMMAND[@]}; fi
echo "${COMMAND[@]}" >>$TMPF
#Set VNC Password
echo "echo \"change vnc password ${array[31]}\" | socat - unix-connect:$RUNDIRECTORY/${array[1]}.mon >/dev/null" >>$TMPF
if [ ! -z "${array[35]}" ]; then
echo "chmod g+w $RUNDIRECTORY/${array[1]}.mon" >>$TMPF
echo "chmod g+r $RUNDIRECTORY/${array[1]}.pid" >>$TMPF
openssl dgst -sha512 -sign "/etc/nlvmi/nlvmi_priv.key" -out /home/nlvmi/${array[1]}.tmp.sha512 $TMPF
TMPFR="/home/nlvmi/rstart{array[32]}-${array[1]}.tmp"
mv $TMPF $TMPFR
su nlvmi -c "ssh ${array[35]} \"rm /home/nlvmi/start*-${array[1]}.tmp\""
mv $TMPFR $TMPF
su nlvmi -c "scp $TMPF* ${array[35]}:/home/nlvmi/"
su nlvmi -c "ssh ${array[35]} \"/usr/bin/nlvmi remote $TMPF run\""
if [ ${array[12]} == "0" ]; then
su nlvmi -c "ssh ${array[35]} \"rm $TMPF $TMPF.sha512\""
fi
rm $TMPF $TMPF.sha512
else
/bin/bash $TMPF
if [ ${array[12]} == "0" ]; then
rm $TMPF
fi
fi
done
export VMFOUND="yes"
}
#stop VM
function vmstop {
if [ $LOGLEVEL -gt "1" ]; then echo "stopvm function entered for VM $1"; fi
sqlite3 $SQLITEFILE "SELECT vms.id AS vid,vmname,connectstring FROM vms LEFT JOIN servers ON vms.server = servers.hostname WHERE vmname='$1'" | while read line; do
IFS='|' read -r -a array <<< "$line"
if [ -z ${array[2]} ]; then
if [ ! -e $RUNDIRECTORY/$1.pid ]; then echo "pidfile $RUNDIRECTORY/$1.pid does not exist"; exit 1; fi
echo "system_powerdown" | socat - unix-connect:$RUNDIRECTORY/${array[1]}.mon >/dev/null
else
su nlvmi -c "ssh ${array[2]} \"echo \\\"system_powerdown\\\" | socat - unix-connect:$RUNDIRECTORY/${array[1]}.mon >/dev/null\""
fi
sleep 5s
STOPPED="no"
COUNTER=0
while [ $STOPPED = "no" ]; do
if [ $COUNTER == 10 ]; then
if [ -z ${array[2]} ]; then
kill `cat $RUNDIRECTORY/${array[1]}.pid`
echo "${array[1]} forcefully killed!";
STOPPED="yes"
else
TMPF=/home/nlvmi/${array[1]}.tmp
echo "kill \$(<\"$RUNDIRECTORY/${array[1]}.pid\")" >$TMPF
openssl dgst -sha512 -sign "/etc/nlvmi/nlvmi_priv.key" -out $TMPF.sha512 $TMPF
su nlvmi -c "scp $TMPF* ${array[2]}:/home/nlvmi/"
su nlvmi -c "ssh ${array[2]} \"/usr/bin/nlvmi remote /home/nlvmi/${array[1]}.tmp stop\""
su nlvmi -c "ssh ${array[2]} \"rm $TMPF $TMPF.sha512\""
rm $TMPF $TMPF.sha512
echo "remotekill"
STOPPED="yes"
fi
fi
((COUNTER++))
checkvm ${array[1]} | grep "is not running" >/dev/null && STOPPED="yes"
sleep 1s
done
done
}
#stop local vm
function vmlocalstop {
if [ ! -e $RUNDIRECTORY/$1.pid ]; then echo "pidfile $RUNDIRECTORY/$1.pid does not exist"; exit 1; fi
echo "system_powerdown" | socat - unix-connect:$RUNDIRECTORY/$1.mon >/dev/null
sleep 5s
STOPPED="no"
COUNTER=0
while [ $STOPPED = "no" ]; do
if [ $COUNTER == 10 ]; then
kill `cat $RUNDIRECTORY/$1.pid`
echo "$1 forcefully killed!";
STOPPED="yes"
fi
((COUNTER++))
ps -ef | grep `cat $RUNDIRECTORY/$1.pid` | grep -v grep >/dev/null || STOPPED="yes"
sleep 1s
done
}
#stopall local vms
function stopall {
for vms in $RUNDIRECTORY/*.pid; do
vmname=`echo ${vms} | rev | cut -d "/" -f1 | rev | sed 's/.pid//g'`
vmlocalstop ${vmname}
done
}
function listdir {
#echo $1 $2
sqlite3 $SQLITEFILE "SELECT $2,connectstring FROM servers WHERE hostname='$1'" | while read line; do
IFS='|' read -r -a array <<< "$line"
IFS=';' read -r -a dirs <<< "${array[0]}"
for di in ${dirs[@]}; do
if [ -z ${array[1]} ]; then
ls -lrt -d -1 $di/{*,.*}
else
su nlvmi -c "ssh ${array[1]} \"ls -lrt -d -1 $di/{*,.*}\""
fi
done
done
}
#mainloop
if [ $# -gt 0 ]; then
if [ $1 == "createdb" ]; then
createdb
elif [ $1 == "autostart" ]; then
vmautostart
elif [ $1 == "checkvm" ]; then
if [ -z $2 ]; then echo "checkvm needs two arguments"; fi
checkvm $2
elif [ $1 == "start" ]; then
if [ -z $2 ]; then echo "start needs a vmname!"; exit 1; fi
VMFOUND="no"
if [ $LOGLEVEL -gt "1" ]; then echo "searching for vmname $2"; fi
sqlite3 $SQLITEFILE "SELECT id FROM vms WHERE vmname='$2'" | (while read line; do
IFS='|' read -r -a arrays <<< "$line"
declare VMFOUND="yes"
export "$VMFOUND"
vmstart $arrays
done
if [[ $VMFOUND == "no" ]]; then echo "no VM with that name found!"; exit 1; fi)
elif [ $1 == "stop" ]; then
if [ -z $2 ]; then echo "stop needs a vmname!"; exit 1; fi
VMFOUND="no"
if [ $LOGLEVEL -gt "1" ]; then echo "searching for vmname $2"; fi
sqlite3 $SQLITEFILE "SELECT id FROM vms WHERE vmname='$2'" | (while read line; do
IFS='|' read -r -a arrays <<< "$line"
declare VMFOUND="yes"
export "$VMFOUND"
vmstop $2
done
if [[ $VMFOUND == "no" ]]; then echo "no VM with that name found!"; exit 1; fi)
elif [ $1 == "stopall" ]; then
stopall
elif [ $1 == "listdir" ]; then
if [ -z $2 ] || [ -z $3 ]; then echo listdir needs two more arguments; exit 1; fi
listdir $2 $3
elif [ $1 == "remote" ]; then
if [ ! -z $MASTERSERVER ]; then
if `echo $SSH_CLIENT | grep "$MASTERSERVER " &>/dev/null`; then
if [ -e $2 ]; then
openssl dgst -sha512 -verify <(openssl x509 -in "/etc/nlvmi/nlvmi_sign.crt" -pubkey -noout) -signature $2.sha512 $2 >/dev/null && /bin/bash $2 || echo "signature failed!!"
fi
else
echo "not allowed";
fi
fi
elif [ $1 == "createkey" ]; then
if [ ! -z $MASTERSERVER ]; then
createkey
fi
fi
else
checkdb
fi