diff files/fbhyve.in @ 474:57f253106ed6

Implement fbhyve, a management system that runs bhyve virtual machines within tmux sessions
author Franz Glasner <fzglas.hg@dom66.de>
date Tue, 27 Aug 2024 21:47:57 +0200
parents 6ecd16725818
children 827371176fec
line wrap: on
line diff
--- a/files/fbhyve.in	Mon Aug 26 17:22:26 2024 +0200
+++ b/files/fbhyve.in	Tue Aug 27 21:47:57 2024 +0200
@@ -1,7 +1,8 @@
 #!/bin/sh
+# -*- indent-tabs-mode: nil -*-
 
 # PROVIDE: fbhyve
-# REQUIRE: LOGIN
+# REQUIRE: LOGIN FILESYSTEMS
 # KEYWORD: shutdown nojail
 #
 
@@ -9,140 +10,145 @@
 
 #
 # Add the following lines to /etc/rc.conf to enable bhyve:
+#
 # fbhyve_enable (bool):  Set to "NO" by default.
-#                        Set it to "YES" to enable bhyve
-# bhyve_profiles (str): Set to "" by default.
-#                       Define your profiles here.
-# bhyve_tapdev (str):   Set to "tap0" by default.
-#                       Set to the tap(4) device to use.
-# bhyve_diskdev (str):  Must be set, no default.
-#                       Set to the disk device to use.
-# bhyve_ncpu (int):     Set to 1 by default.
-#                       Set to the number of CPUs for the VM.
-# bhyve_memsize (int):  Set to 512 by default.
-#                       Set to the number of MB of memory for the VM.
+#                        Acts as default for all listed VMs.
+#                        Set it to "YES" to enable bhyve.
+# fbhyve_list (str):     Set to "" by default.
+#                        Define the names of your VMs here.
+# fbhyve_tmux_session_prefix (str):  All tmux session names will have this
+#                                    prefix string.
+# fbhyve_configdir (str): Where by default config files for all VMs live.
+#                         Default: $PREFIX/etc/fbhyve
+#
+#
+# fbhyve_<vm>_enable (bool):  Allow to enable or disable a specific VM.
+#                             Set to $fbhyve_enable by default.
+# fbhyve_<vm>_config (str):   The bhyve configuration file to use to.
+#                             The default is $fbhyve_configdir/<vm>.conf
+#
 
 . /etc/rc.subr
 
 name="fbhyve"
-rcvar=fbhyve_enable
+desc="Manage system bhyve virtual machines"
+rcvar="fbhyve_enable"
 
-start_precmd="bhyve_prestart"
-status_cmd="bhyve_status"
-poll_cmd="bhyve_poll"
-stop_cmd="bhyve_stop"
-_session=$name
-command="/usr/local/bin/tmux"
-procname="-sh"
+start_precmd="fbhyve_pre_start"
+stop_postcmd="fbhyve_post_stop"
+status_cmd="fbhyve_status"
 
-[ -z "$bhyve_tapdev" ]  && bhyve_tapdev="tap0"
-[ -z "$bhyve_diskdev" ] && bhyve_diskdev="none"
-[ -z "$bhyve_ncpu" ]    && bhyve_ncpu="1"
-[ -z "$bhyve_memsize" ] && bhyve_memsize="512"
 
 load_rc_config $name
 
-: {fbhyve_enable:="NO"}
-: {fbhyve_configdir:="%%PREFIX%%/etc/fbhyve"}
+: ${fbhyve_enable:="NO"}
+: ${fbhyve_list=}
+: ${fbhyve_tmux_session_prefix:="${name}_"}
+: ${fbhyve_configdir:="%%FBHYVE_ETCDIR%%"}
+
+
+_fbhyve_vm_exists() {
+    local _p
+    for _p in ${fbhyve_list}; do
+        [ "${_p}" = "$1" ] && return 0;
+    done
+    return 1
+}
+
+
+if [ $# -eq 2 ]; then
+    _vm="$2"
+    if ! _fbhyve_vm_exists "${_vm}"; then
+        echo "ERROR: no VM named \`${_vm}' in \`fbhyve_list'" 1>&2
+        exit 1
+    fi
+    echo "-- VM: ${_vm} --"
+    _session="${fbhyve_tmux_session_prefix}${_vm}"
+    _window="${_session}_console"
+    eval fbhyve_enable="\${fbhyve_${_vm}_enable:-${fbhyve_enable}}"
+    eval fbhyve_config="\${fbhyve_${_vm}_config:-\"${fbhyve_configdir}/${_vm}.conf\"}"
+else
+    _ec=0
+    _swap=$*; shift; _vmarglist=$*
+    _vmlist=${_vmarglist:-${fbhyve_list}}
+    set -- ${_swap}
+    for _vm in ${_vmlist}; do
+        "$0" "$1" "${_vm}"
+        _vmec=$?
+        if [ ${_vmec} -gt ${_ec} ]; then
+            _ec=${_vmec}
+        fi
+    done
+    exit ${_ec}
+fi
+
+_rundir="%%FBHYVE_RUNDIR%%"
+pidfile="${_rundir}/${_vm}.pid"
+procname="bhyve:"         # something like bhyve: <vmname> (bhyve)
+
+required_dirs="${_rundir}"
+required_files="${fbhyve_config}"
+
+command="%%LOCALBASE%%/bin/tmux"
+
+command_args="new-session -ds ${_session} -n ${_window} \"sh -c 'echo \\\$\\\$ >\\\"${pidfile}\\\"; /usr/sbin/bhyve -k \\\"${fbhyve_config}\\\" \\\"${_vm}\\\"'\""
 
 
-if [ -n "$2" ]; then
-	profile="$2"
-	_session="${_session}_${profile}"
-	if [ "x${bhyve_profiles}" != "x" ]; then
-		eval fbhyve_enable="\${${_session}_enable:-${fbhyve_enable}}"
-		eval bhyve_tapdev="\${${_session}_tapdev:-${bhyve_tapdev}}"
-		eval bhyve_diskdev="\${${_session}_diskdev:-${bhyve_diskdev}}"
-		eval bhyve_ncpu="\${${_session}_ncpu:-${bhyve_ncpu}}"
-		eval bhyve_memsize="\${${_session}_memsize:-${bhyve_memsize}}"
-	else
-		echo "$0: extra argument ignored"
-	fi
-else
-	if [ "x${bhyve_profiles}" != "x" -a "x$1" != "x" ]; then
-		for profile in ${bhyve_profiles}; do
-			eval _enable="\${bhyve_${profile}_enable}"
-			case "x${_enable:-${fbhyve_enable}}" in
-			x|x[Nn][Oo]|x[Nn][Oo][Nn][Ee])
-				continue
-				;;
-			x[Yy][Ee][Ss])
-				;;
-			*)
-				if test -z "$_enable"; then
-					_var=fbhyve_enable
-				else
-					_var=bhyve_"${profile}"_enable
-				fi
-				echo "Bad value" \
-				    "'${_enable:-${fbhyve_enable}}'" \
-				    "for ${_var}. " \
-				    "Profile ${profile} skipped."
-				continue
-				;;
-			esac
-			echo "===> bhyve profile: ${profile}"
-			/usr/local/etc/rc.d/bhyve $1 ${profile}
-			retcode="$?"
-			if [ "0${retcode}" -ne 0 ]; then
-				failed="${profile} (${retcode}) ${failed:-}"
-			else
-				success="${profile} ${success:-}"
-			fi
-		done
-		exit 0
-	fi
-	profile=$name
-fi
+fbhyve_status()
+{
+    local _pid _rc
+
+    _rc=0
+    _pid=$(check_pidfile "$pidfile" "$procname")
+    if [ -n "${_pid}" ]; then
+        echo "VM ${_vm} is running as pid $_pid."
+    else
+        echo "VM ${_vm} is not running."
+        _rc=1
+    fi
 
-pidfile="/var/run/${_session}.pid"
+    if ${command} has-session -t ${_session} 2>/dev/null; then
+	echo "tmux session ${_session} exists."
+        if [ ${_rc} -gt 0 ]; then
+            _rc=2
+        fi
+    else
+	echo "tmux session ${_session} does not exist."
+        if [ ${_rc} -gt 0 ]; then
+            _rc=2
+        fi
+    fi
+    return ${_rc}
+}
 
 
-bhyve_prestart()
-{
-	case ${bhyve_diskdev} in
-	[Nn][Oo][Nn][Ee] | '')
-		echo "No ${_session}_diskdev set. Quitting." 1>&2
-		return 1;
-		;;
-	esac
-	if [ ! -c "${bhyve_diskdev}" -a ! -f "${bhyve_diskdev}" ]; then
-		echo "${bhyve_diskdev} doesn't exist or is not suitable as a diskdev" 1>&2
-		return 1;
-	fi
-}
-
-bhyve_status()
-{
-	if ${command} has-session -t ${_session} 2>/dev/null; then
-		echo "${_session} is running."
-	else
-		echo "${_session} is not running."
-		return 1
-	fi
+fbhyve_pre_start() {
+    if ! load_kld -m vmm vmm.ko; then
+        echo "ERROR: Cannot load kernel module \`vmm'" 1>&2
+        return 1
+    fi
+    if [ -e "/dev/vmm/${_vm}" ]; then
+        echo "ERROR: VM \`${_vm}' already created in the VM monitor" 1>&2
+        return 1
+    fi
+    if ${command} has-session -t "${_session}" 2>/dev/null; then
+        echo "ERROR: tmux session \`${_session}' already exists" 1>&2
+        return 1
+    fi
+    return 0
 }
 
-bhyve_poll()
-{
-	echo -n "Waiting for session: ${_session}"
-	while ${command} has-session -t ${_session} 2>/dev/null; do
-		sleep 1
-	done
-	echo
+
+fbhyve_post_stop() {
+    if [ -e "/dev/vmm/${_vm}" ]; then
+        /usr/sbin/bhyvectl --vm="${_vm}" --destroy
+    fi
+    if ${command} has-session -t "${_session}" 2>/dev/null; then
+        ${command} kill-session -t "${_session}"
+    fi
+    rm -f "${pidfile}"
+    return 0
 }
 
-bhyve_stop()
-{
-	if ${command} has-session -t ${_session} 2>/dev/null; then
-		echo "Stopping ${_session}."
-		${command} kill-session -t ${_session}
-		while ${command} has-session -t ${_session} 2>/dev/null; do
-			sleep 1
-		done
-	fi
-	rm -f ${pidfile}
-}
-
-command_args="new-session -ds ${_session} \"sh -c 'echo \\\$PPID >${pidfile}; while true; do /usr/sbin/bhyvectl --vm=${_session} --destroy; /usr/sbin/bhyveload -m ${bhyve_memsize} -d ${bhyve_diskdev} ${_session} && /usr/sbin/bhyve -c ${bhyve_ncpu} -m ${bhyve_memsize} -AI -H -P -g 0 -s 0:0,hostbridge -s 1:0,virtio-net,${bhyve_tapdev} -s 2:0,virtio-blk,${bhyve_diskdev} -s 31,lpc -l com1,stdio ${_session} || break; done'\""
 
 run_rc_command "$1"