239 lines
7.7 KiB
Bash
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}"
|
|
}
|