vm-bhyve-laylo/lib/vm-cmd

239 lines
7.7 KiB
Bash

#!/bin/sh
#-------------------------------------------------------------------------+
# Copyright (C) 2016 Matt Churchyard (churchers@gmail.com)
# Copyright (C) 2023 LAYLO
# All rights reserved
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted providing that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
CMD_VALID_LIST="init,switch,datastore,image,get,set,list,create,destroy,rename,install,start,stop,restart"
CMD_VALID_LIST="${CMD_VALID_LIST},add,reset,poweroff,startall,stopall,console,iso,img,configure,passthru,_run"
CMD_VALID_LIST="${CMD_VALID_LIST},info,clone,snapshot,rollback,migrate,version,usage"
# cmd: vm ...
#
# handle simple information commands that don't need any
# priviledged access or bhyve support
#
# @param string _cmd the command right after 'vm '
#
cmd::parse_info(){
local _cmd
cmd::find "_cmd" "$1" "${CMD_VALID_LIST}"
case "${_cmd}" in
version) util::version && exit ;;
usage) util::usage ;;
esac
}
# cmd: vm ...
#
# process the vm command line to see which function is requested
#
# @param string _cmd the command right after 'vm '
#
cmd::parse(){
local _cmd
# try to find a matching command
cmd::find "_cmd" "$1" "${CMD_VALID_LIST}" || util::usage
shift
case "${_cmd}" in
init) util::setup
switch::init ;;
switch) cmd::parse_switch "$@" ;;
datastore) cmd::parse_datastore "$@" ;;
image) cmd::parse_image "$@" ;;
get) core::get "$@" ;;
set) core::set "$@" ;;
list) core::list "$@" ;;
create) core::create "$@" ;;
destroy) core::destroy "$@" ;;
rename) core::rename "$@" ;;
install) core::install "$@" ;;
start) core::start "$@" ;;
stop) core::stop "$@" ;;
restart) core::restart "$@" ;;
add) core::add "$@" ;;
reset) core::reset "$@" ;;
poweroff) core::poweroff "$@" ;;
startall) core::startall ;;
stopall) core::stopall ;;
console) core::console "$@" ;;
iso) core::iso "$@" ;;
img) core::img "$@" ;;
configure) core::configure "$@" ;;
passthru) core::passthru ;;
_run) vm::run "$@" ;;
info) info::guest "$@" ;;
clone) zfs::clone "$@" ;;
snapshot) zfs::snapshot "$@" ;;
rollback) zfs::rollback "$@" ;;
migrate) migration::run "$@" ;;
*) util::err "unknown command '${_user_cmd}'. please run 'vm usage' or view the manpage for help" ;;
esac
}
# cmd: vm switch ...
#
# parse switch command
# we've already shifted once, so $1 is the switch function
#
# @param string _cmd the command right after 'vm switch '
#
cmd::parse_switch(){
local _cmd
# try to find a matching command
cmd::find "_cmd" "$1" "create,list,destroy,add,remove,vlan,nat,address,private,info" || util::usage
shift
case "${_cmd}" in
create) switch::create "$@" ;;
list) switch::list ;;
destroy) switch::remove "$@" ;;
add) switch::add_member "$@" ;;
remove) switch::remove_member "$@" ;;
vlan) switch::vlan "$@" ;;
nat) switch::nat "$@" ;;
address) switch::address "$@" ;;
private) switch::private "$@" ;;
info) info::switch "$@" ;;
*) util::err "unknown command '${_user_cmd}'. please run 'vm usage' or view the manpage for help" ;;
esac
}
# cmd: vm datastore ...
#
# parse a datastore command
#
# @param string _cmd the command after 'vm datastore ...'
#
cmd::parse_datastore(){
local _cmd
# try to find a matching command
cmd::find "_cmd" "$1" "list,add,remove,iso,img" || util::usage
shift
case "${_cmd}" in
list) datastore::list ;;
add) datastore::add "$@" ;;
remove) datastore::remove "$@" ;;
iso) datastore::iso "$@" ;;
img) datastore::img "$@" ;;
*) util::err "unknown command '${_user_cmd}'. please run 'vm usage' or view the manpage for help" ;;
esac
}
# cmd 'vm image ...'
# parse the image command set
#
# @param string _cmd the command after 'vm image '
#
cmd::parse_image(){
local _cmd
[ -z "${VM_ZFS}" ] && util::err "\$vm_dir must be a ZFS datastore to use these functions"
# try to find a matching command
cmd::find "_cmd" "$1" "list,create,provision,destroy" || util::usage
shift
case "${_cmd}" in
list) zfs::image_list ;;
create) zfs::image_create "$@" ;;
provision) zfs::image_provision "$@" ;;
destroy) zfs::image_destroy "$@" ;;
*) util::err "unknown command '${_user_cmd}'. please run 'vm usage' or view the manpage for help" ;;
esac
}
# many commands accept the same arguments (force being the obvious one)
# provide a function to parse these so we don't have to keep
# repeating the same getopt code. the return value here is the number
# of arguments the caller needs to shift.
#
# note that start/install/_run use -f for foreground mode
#
# @param _arglist[multiple] the callers $@
# @return number of arguments caller should shift over
#
cmd::parse_args(){
local _opt _count
while getopts fitv _opt; do
case ${_opt} in
f) VM_OPT_FORCE="1"
VM_OPT_FOREGROUND="1" ;;
i) VM_OPT_INTERACTIVE="1" ;;
t) VM_OPT_TMUX="1" ;;
v) VM_OPT_VERBOSE="1" ;;
esac
done
[ -n "${VM_OPT_FOREGROUND}" ] && [ -n "${VM_OPT_INTERACTIVE}" ] && \
util::err "foreground and interactive mode are mutually exclusive"
return $((OPTIND - 1))
}
# try to match part of a command name against a list of valid commands
# if we find more than one match we return an error
# if we only get one match, return the full command name
#
# @param string _var variable to put full command name into
# @param string _user_cmd the value provided by the user
# @param string _valid comma-separated list of valid choices
# @return success if we find one match
#
cmd::find(){
local _var="$1"
local _user_cmd="$2"
local _valid="$3"
local _opt _choice _found=""
local IFS=","
[ -n "${_user_cmd}" ] || util::err "no command specified"
for _opt in ${_valid}; do
# exact match?
if [ "${_user_cmd}" = "${_opt}" ]; then
setvar "${_var}" "${_opt}"
return 0
fi
if echo "${_opt}" | grep -iqs "^${_user_cmd}"; then
[ -n "${_found}" ] && util::err "ambiguous command '${_user_cmd}'"
_found=1
_choice="${_opt}"
fi
done
[ -z "${_found}" ] && return 1
setvar "${_var}" "${_choice}"
}