2015-06-29 11:14:42 +01:00
|
|
|
#!/bin/sh
|
|
|
|
|
#-------------------------------------------------------------------------+
|
2016-08-03 18:48:27 +01:00
|
|
|
# Copyright (C) 2016 Matt Churchyard (churchers@gmail.com)
|
2015-06-29 11:14:42 +01:00
|
|
|
# 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.
|
|
|
|
|
|
2015-10-19 13:27:55 +01:00
|
|
|
# 'vm list'
|
2015-07-10 13:51:40 +01:00
|
|
|
# list virtual machines
|
2015-10-19 13:27:55 +01:00
|
|
|
#
|
2016-06-09 10:47:47 +01:00
|
|
|
core::list(){
|
2016-05-27 14:28:24 +01:00
|
|
|
local _name _loader _cpu _our_host
|
2016-05-27 10:23:35 +01:00
|
|
|
local _memory _run _vm _auto _num _uefi _graphics _vnc
|
2016-08-30 14:36:25 +01:00
|
|
|
local _format="%s^%s^%s^%s^%s^%s^%s^%s\n"
|
2015-11-14 14:04:19 +00:00
|
|
|
|
2016-05-27 14:28:24 +01:00
|
|
|
_our_host=$(hostname)
|
|
|
|
|
|
2016-06-09 10:47:47 +01:00
|
|
|
vm::running_load
|
2016-04-18 19:02:37 +01:00
|
|
|
|
2016-08-30 14:36:25 +01:00
|
|
|
# pass everything below here to column(1)
|
|
|
|
|
{
|
|
|
|
|
printf "${_format}" "NAME" "DATASTORE" "LOADER" "CPU" "MEMORY" "VNC" "AUTOSTART" "STATE"
|
2016-04-18 19:02:37 +01:00
|
|
|
|
2016-08-30 14:36:25 +01:00
|
|
|
for _ds in ${VM_DATASTORE_LIST}; do
|
|
|
|
|
datastore::get "${_ds}" || continue
|
2016-04-18 19:02:37 +01:00
|
|
|
|
2016-08-30 14:36:25 +01:00
|
|
|
ls -1 "${VM_DS_PATH}" 2>/dev/null | \
|
|
|
|
|
while read _name; do
|
|
|
|
|
[ ! -e "${VM_DS_PATH}/${_name}/${_name}.conf" ] && continue
|
2016-04-18 19:02:37 +01:00
|
|
|
|
2016-08-30 14:36:25 +01:00
|
|
|
config::load "${VM_DS_PATH}/${_name}/${_name}.conf"
|
|
|
|
|
config::get "_loader" "loader" "none"
|
|
|
|
|
config::get "_cpu" "cpu"
|
|
|
|
|
config::get "_memory" "memory"
|
|
|
|
|
config::get "_uefi" "uefi"
|
|
|
|
|
config::get "_graphics" "graphics"
|
2016-05-27 11:22:08 +01:00
|
|
|
|
2016-08-30 14:36:25 +01:00
|
|
|
# show uefi in loader column if it's a uefi guest
|
|
|
|
|
util::checkyesno "${_uefi}" && _loader="uefi"
|
2016-05-27 11:22:08 +01:00
|
|
|
|
2016-08-30 14:36:25 +01:00
|
|
|
# defaults
|
|
|
|
|
_vnc="-"
|
2016-05-27 11:22:08 +01:00
|
|
|
|
2016-08-30 14:36:25 +01:00
|
|
|
# check if the guest is running
|
|
|
|
|
if vm::running_check "_run" "${_name}" || \
|
|
|
|
|
[ -e "${VM_DS_PATH}/${_name}/run.lock" -a "$(head -n1 ${VM_DS_PATH}/${_name}/run.lock 2>/dev/null)" = "${_our_host}" ]; then
|
|
|
|
|
|
|
|
|
|
# if running and graphics, try to get vnc port
|
|
|
|
|
if util::checkyesno "${_graphics}"; then
|
|
|
|
|
_vnc=$(grep vnc "${VM_DS_PATH}/${_name}/console" 2>/dev/null |cut -d= -f2)
|
|
|
|
|
[ -z "${_vnc}" ] && _vnc="-"
|
|
|
|
|
fi
|
2016-05-27 11:22:08 +01:00
|
|
|
fi
|
2016-05-12 16:09:08 +01:00
|
|
|
|
2016-08-30 14:36:25 +01:00
|
|
|
_num=1
|
|
|
|
|
_auto="No"
|
2016-04-18 19:02:37 +01:00
|
|
|
|
2016-08-30 14:36:25 +01:00
|
|
|
# find out if we auto-start this vm, and get sequence number
|
|
|
|
|
for _vm in ${vm_list}; do
|
|
|
|
|
[ "${_vm}" = "${_name}" ] && _auto="Yes [${_num}]"
|
|
|
|
|
_num=$(($_num + 1))
|
|
|
|
|
done
|
2016-04-18 19:02:37 +01:00
|
|
|
|
2016-08-30 14:36:25 +01:00
|
|
|
# if stopped, see if it's locked by another host
|
|
|
|
|
if [ "${_run}" = "Stopped" -a -e "${VM_DS_PATH}/${_name}/run.lock" ]; then
|
|
|
|
|
_run=$(head -n1 "${VM_DS_PATH}/${_name}/run.lock")
|
|
|
|
|
_run="Locked (${_run})"
|
|
|
|
|
fi
|
2015-11-14 14:04:19 +00:00
|
|
|
|
2016-08-30 14:36:25 +01:00
|
|
|
printf "${_format}" "${_name}" "${_ds}" "${_loader}" "${_cpu}" "${_memory}" "${_vnc}" "${_auto}" "${_run}"
|
|
|
|
|
done
|
2016-04-18 19:02:37 +01:00
|
|
|
done
|
2016-08-30 14:36:25 +01:00
|
|
|
} | column -ts^
|
2015-07-10 13:51:40 +01:00
|
|
|
}
|
|
|
|
|
|
2016-02-14 12:13:59 +06:00
|
|
|
# 'vm check name'
|
|
|
|
|
# check name of virtual machine
|
|
|
|
|
#
|
2016-04-15 16:59:48 +01:00
|
|
|
# @return int 0 if name is valid
|
|
|
|
|
#
|
2016-06-09 10:47:47 +01:00
|
|
|
core::check_name(){
|
2016-10-18 16:00:06 +01:00
|
|
|
echo "$1" | egrep -iqs '^[a-z0-9][.a-z0-9-]{0,30}[a-z0-9]$'
|
2016-02-14 12:13:59 +06:00
|
|
|
}
|
|
|
|
|
|
2015-10-19 13:27:55 +01:00
|
|
|
# 'vm create'
|
2015-06-29 11:14:42 +01:00
|
|
|
# create a new virtual machine
|
2015-10-19 13:27:55 +01:00
|
|
|
#
|
|
|
|
|
# @param optional string (-t) _template the template to use (default = default)
|
|
|
|
|
# @param optional string (-s) _size guest size (default = 20G)
|
|
|
|
|
# @param string _name the name of the guest to create
|
|
|
|
|
#
|
2016-06-09 10:47:47 +01:00
|
|
|
core::create(){
|
2016-07-27 07:38:13 -04:00
|
|
|
local _name _opt _size _vmdir _disk _disk_dev _num=0
|
2016-04-18 19:25:11 +01:00
|
|
|
local _zfs_opts _disk_size _template="default" _ds="default" _ds_path
|
2015-11-14 14:04:19 +00:00
|
|
|
|
2016-04-18 19:02:37 +01:00
|
|
|
while getopts d:t:s: _opt ; do
|
2015-11-14 14:04:19 +00:00
|
|
|
case $_opt in
|
2016-04-14 16:24:47 +01:00
|
|
|
t) _template=${OPTARG} ;;
|
|
|
|
|
s) _size=${OPTARG} ;;
|
2016-04-18 19:02:37 +01:00
|
|
|
d) _ds=${OPTARG} ;;
|
2016-06-09 10:47:47 +01:00
|
|
|
*) util::usage ;;
|
2015-11-14 14:04:19 +00:00
|
|
|
esac
|
|
|
|
|
done
|
|
|
|
|
|
|
|
|
|
shift $((OPTIND - 1))
|
|
|
|
|
_name=$1
|
|
|
|
|
|
2016-06-09 10:47:47 +01:00
|
|
|
[ -z "${_name}" ] && util::usage
|
2016-04-18 19:02:37 +01:00
|
|
|
|
|
|
|
|
# check guest name
|
2016-06-09 10:47:47 +01:00
|
|
|
core::check_name "${_name}" || util::err "invalid virtual machine name - '${_name}'"
|
|
|
|
|
datastore::get_guest "${_name}" && util::err "virtual machine already exists in ${VM_DS_PATH}/${_name}"
|
|
|
|
|
datastore::get "${_ds}" || util::err "unable to load datastore - '${_ds}'"
|
2015-11-14 14:04:19 +00:00
|
|
|
|
|
|
|
|
[ ! -f "${vm_dir}/.templates/${_template}.conf" ] && \
|
2016-06-09 10:47:47 +01:00
|
|
|
util::err "unable to find template ${vm_dir}/.templates/${_template}.conf"
|
2015-11-14 14:04:19 +00:00
|
|
|
|
|
|
|
|
# we need to get disk0 name and device type from the template
|
2016-06-09 10:47:47 +01:00
|
|
|
config::load "${vm_dir}/.templates/${_template}.conf"
|
|
|
|
|
config::get "_disk" "disk0_name"
|
|
|
|
|
config::get "_disk_dev" "disk0_dev"
|
|
|
|
|
config::get "_disk_size" "disk0_size" "20G"
|
|
|
|
|
config::get "_zfs_opts" "zfs_dataset_opts"
|
2015-11-14 14:04:19 +00:00
|
|
|
|
2016-04-14 16:24:47 +01:00
|
|
|
# make sure template has a disk before we start creating anything
|
2016-06-09 10:47:47 +01:00
|
|
|
[ -z "${_disk}" ] && util::err "template is missing disk0_name specification"
|
2015-11-14 14:04:19 +00:00
|
|
|
|
|
|
|
|
# if we're on zfs, make a new filesystem
|
2016-06-09 10:47:47 +01:00
|
|
|
zfs::make_dataset "${VM_DS_ZFS_DATASET}/${_name}" "${_zfs_opts}"
|
2015-11-14 14:04:19 +00:00
|
|
|
|
2016-04-18 19:02:37 +01:00
|
|
|
[ ! -d "${VM_DS_PATH}/${_name}" ] && mkdir "${VM_DS_PATH}/${_name}" >/dev/null 2>&1
|
2016-06-09 10:47:47 +01:00
|
|
|
[ ! -d "${VM_DS_PATH}/${_name}" ] && util::err "unable to create virtual machine directory ${VM_DS_PATH}/${_name}"
|
2015-11-14 14:04:19 +00:00
|
|
|
|
2016-04-18 19:02:37 +01:00
|
|
|
cp "${vm_dir}/.templates/${_template}.conf" "${VM_DS_PATH}/${_name}/${_name}.conf"
|
2018-02-06 17:39:11 -05:00
|
|
|
[ $? -eq 0 ] || util::err "unable to copy template to virtual machine directory"
|
2015-11-14 14:04:19 +00:00
|
|
|
|
|
|
|
|
# generate a uuid
|
2016-07-27 07:38:13 -04:00
|
|
|
config::set "${_name}" "uuid" $(uuidgen)
|
2015-11-14 14:04:19 +00:00
|
|
|
|
|
|
|
|
# get any zvol options
|
2016-06-09 10:47:47 +01:00
|
|
|
config::get "_zfs_opts" "zfs_zvol_opts"
|
2015-11-14 14:04:19 +00:00
|
|
|
|
2016-04-14 16:24:47 +01:00
|
|
|
# use cmd line size for disk 0 if specified
|
|
|
|
|
[ -n "${_size}" ] && _disk_size="${_size}"
|
|
|
|
|
|
|
|
|
|
# create each disk
|
|
|
|
|
while [ -n "${_disk}" ]; do
|
|
|
|
|
case "${_disk_dev}" in
|
|
|
|
|
zvol)
|
2016-06-09 10:47:47 +01:00
|
|
|
zfs::make_zvol "${VM_DS_ZFS_DATASET}/${_name}/${_disk}" "${_disk_size}" "0" "${_zfs_opts}"
|
2016-04-14 16:24:47 +01:00
|
|
|
;;
|
|
|
|
|
sparse-zvol)
|
2016-06-09 10:47:47 +01:00
|
|
|
zfs::make_zvol "${VM_DS_ZFS_DATASET}/${_name}/${_disk}" "${_disk_size}" "1" "${_zfs_opts}"
|
2016-04-14 16:24:47 +01:00
|
|
|
;;
|
|
|
|
|
*)
|
2016-04-18 19:02:37 +01:00
|
|
|
truncate -s "${_disk_size}" "${VM_DS_PATH}/${_name}/${_disk}"
|
2018-02-06 17:39:11 -05:00
|
|
|
[ $? -eq 0 ] || util::err "failed to create sparse file for disk image"
|
2018-02-09 09:52:23 +00:00
|
|
|
|
|
|
|
|
# make sure only owner can read the disk image
|
|
|
|
|
chmod 600 "${VM_DS_PATH}/${_name}/${_disk}"
|
2016-04-14 16:24:47 +01:00
|
|
|
;;
|
|
|
|
|
esac
|
|
|
|
|
|
|
|
|
|
# scrap size option from guest template
|
2016-04-18 19:02:37 +01:00
|
|
|
sysrc -inxqf "${VM_DS_PATH}/${_name}/${_name}.conf" "disk${_num}_size"
|
2016-04-14 16:24:47 +01:00
|
|
|
|
|
|
|
|
# look for another disk
|
|
|
|
|
_num=$((_num + 1))
|
2016-06-09 10:47:47 +01:00
|
|
|
config::get "_disk" "disk${_num}_name"
|
|
|
|
|
config::get "_disk_dev" "disk${_num}_dev"
|
|
|
|
|
config::get "_disk_size" "disk${_num}_size" "20G"
|
2016-04-14 16:24:47 +01:00
|
|
|
done
|
2015-06-29 11:14:42 +01:00
|
|
|
}
|
|
|
|
|
|
2015-10-19 13:27:55 +01:00
|
|
|
# 'vm add'
|
2015-10-16 10:57:48 +01:00
|
|
|
# add a device to an existing guest
|
2015-10-19 13:27:55 +01:00
|
|
|
#
|
|
|
|
|
# @param string (-d) _device=network|disk the type of device to add
|
|
|
|
|
# @param string (-t) _type for disk, the type of disk - file|zvol|sparse-zvol
|
|
|
|
|
# @param string (-s) _sopt for disk the size, for network the virtual switch name
|
|
|
|
|
# @param string _name name of the guest
|
|
|
|
|
#
|
2016-06-09 10:47:47 +01:00
|
|
|
core::add(){
|
2015-11-14 14:04:19 +00:00
|
|
|
local _name _device _type _sopt _opt
|
|
|
|
|
|
|
|
|
|
while getopts d:t:s: _opt; do
|
|
|
|
|
case $_opt in
|
2016-04-21 14:33:12 +01:00
|
|
|
d) _device=${OPTARG} ;;
|
|
|
|
|
t) _type=${OPTARG} ;;
|
|
|
|
|
s) _sopt=${OPTARG} ;;
|
2016-06-09 10:47:47 +01:00
|
|
|
*) util::usage ;;
|
2015-11-14 14:04:19 +00:00
|
|
|
esac
|
|
|
|
|
done
|
|
|
|
|
|
|
|
|
|
shift $((OPTIND - 1))
|
|
|
|
|
_name="$1"
|
|
|
|
|
|
|
|
|
|
# check guest
|
2016-06-09 10:47:47 +01:00
|
|
|
[ -z "${_name}" ] && util::usage
|
|
|
|
|
datastore::get_guest "${_name}" || "${_name} does not appear to be a valid virtual machine"
|
2015-11-14 14:04:19 +00:00
|
|
|
|
|
|
|
|
case "${_device}" in
|
2016-06-09 10:47:47 +01:00
|
|
|
disk) core::add_disk "${_name}" "${_type}" "${_sopt}" ;;
|
|
|
|
|
network) core::add_network "${_name}" "${_sopt}" ;;
|
|
|
|
|
*) util::err "device must be one of the following: disk network" ;;
|
2015-11-14 14:04:19 +00:00
|
|
|
esac
|
2015-10-16 10:57:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# add a disk to guest
|
2015-10-19 13:27:55 +01:00
|
|
|
# this creates the disk image or zvol and updates configuration file
|
|
|
|
|
# we use the same emulation as the existing disk(s)
|
|
|
|
|
#
|
|
|
|
|
# @private
|
|
|
|
|
# @param string _name name of the guest
|
|
|
|
|
# @param string _device type of device file|zvol|sparse-zvol
|
|
|
|
|
# @param string _size size of the disk to create
|
|
|
|
|
#
|
2016-06-09 10:47:47 +01:00
|
|
|
core::add_disk(){
|
2015-11-14 14:04:19 +00:00
|
|
|
local _name="$1"
|
|
|
|
|
local _device="$2"
|
|
|
|
|
local _size="$3"
|
2015-11-16 16:29:55 +00:00
|
|
|
local _num=0 _curr _diskname _emulation _zfs_opts
|
2015-11-14 14:04:19 +00:00
|
|
|
|
|
|
|
|
: ${_device:=file}
|
|
|
|
|
|
2016-06-09 10:47:47 +01:00
|
|
|
[ -z "${_size}" ] && util::usage
|
2015-11-14 14:04:19 +00:00
|
|
|
|
|
|
|
|
# get the last existing disk
|
2016-06-09 10:47:47 +01:00
|
|
|
config::load "${VM_DS_PATH}/${_name}/${_name}.conf"
|
|
|
|
|
config::get "_zfs_opts" "zfs_zvol_opts"
|
2015-11-14 14:04:19 +00:00
|
|
|
|
|
|
|
|
while [ 1 ]; do
|
2016-06-09 10:47:47 +01:00
|
|
|
config::get "_curr" "disk${_num}_name"
|
2015-11-14 14:04:19 +00:00
|
|
|
[ -z "${_curr}" ] && break
|
2016-06-09 10:47:47 +01:00
|
|
|
config::get "_emulation" "disk${_num}_type"
|
2015-11-14 14:04:19 +00:00
|
|
|
_num=$((_num + 1))
|
|
|
|
|
done
|
|
|
|
|
|
2016-06-09 10:47:47 +01:00
|
|
|
[ -z "${_emulation}" ] && util::err "failed to get emulation type of the existing guest disks"
|
2015-11-14 14:04:19 +00:00
|
|
|
|
|
|
|
|
# create the disk first, then update config if no problems
|
|
|
|
|
case "${_device}" in
|
|
|
|
|
zvol)
|
2016-06-09 10:47:47 +01:00
|
|
|
zfs::make_zvol "${VM_DS_ZFS_DATASET}/${_name}/disk${_num}" "${_size}" "0" "${_zfs_opts}"
|
2015-11-14 14:04:19 +00:00
|
|
|
_diskname="disk${_num}"
|
|
|
|
|
;;
|
|
|
|
|
sparse-zvol)
|
2016-06-09 10:47:47 +01:00
|
|
|
zfs::make_zvol "${VM_DS_ZFS_DATASET}/${_name}/disk${_num}" "${_size}" "1" "${_zfs_opts}"
|
2015-11-14 14:04:19 +00:00
|
|
|
_diskname="disk${_num}"
|
|
|
|
|
;;
|
|
|
|
|
file)
|
2016-04-18 19:02:37 +01:00
|
|
|
truncate -s "${_size}" "${VM_DS_PATH}/${_name}/disk${_num}.img"
|
2018-02-06 17:39:11 -05:00
|
|
|
[ $? -eq 0 ] || util::err "failed to create sparse file for disk image"
|
2015-11-14 14:04:19 +00:00
|
|
|
_diskname="disk${_num}.img"
|
|
|
|
|
;;
|
|
|
|
|
*)
|
2016-06-09 10:47:47 +01:00
|
|
|
util::err "device type must be one of the following: zvol sparse-zvol file"
|
2015-11-14 14:04:19 +00:00
|
|
|
;;
|
|
|
|
|
esac
|
|
|
|
|
|
|
|
|
|
# update configuration
|
2016-06-15 13:06:54 +01:00
|
|
|
config::set "${_name}" "disk${_num}_name" "${_diskname}"
|
|
|
|
|
config::set "${_name}" "disk${_num}_type" "${_emulation}" "1"
|
|
|
|
|
config::set "${_name}" "disk${_num}_dev" "${_device}" "1"
|
2018-02-06 17:39:11 -05:00
|
|
|
[ $? -eq 0 ] || util::err "disk image created but errors while updating guest configuration"
|
2015-10-16 10:57:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# add network interface to guest
|
2015-10-19 13:27:55 +01:00
|
|
|
#
|
|
|
|
|
# @private
|
|
|
|
|
# @param string _name name of the guest
|
|
|
|
|
# @param string _switch the switch name for this interface
|
|
|
|
|
#
|
2016-06-09 10:47:47 +01:00
|
|
|
core::add_network(){
|
2015-11-14 14:04:19 +00:00
|
|
|
local _name="$1"
|
|
|
|
|
local _switch="$2"
|
|
|
|
|
local _num=0 _curr _emulation
|
2015-10-16 10:57:48 +01:00
|
|
|
|
2016-06-09 10:47:47 +01:00
|
|
|
[ -z "${_switch}" ] && util::usage
|
2015-10-16 10:57:48 +01:00
|
|
|
|
2016-06-09 10:47:47 +01:00
|
|
|
config::load "${VM_DS_PATH}/${_name}/${_name}.conf"
|
2015-10-16 10:57:48 +01:00
|
|
|
|
2015-11-14 14:04:19 +00:00
|
|
|
while [ 1 ]; do
|
|
|
|
|
_emulation="${_curr}"
|
2016-06-09 10:47:47 +01:00
|
|
|
config::get "_curr" "network${_num}_type"
|
2015-11-14 14:04:19 +00:00
|
|
|
[ -z "${_curr}" ] && break
|
|
|
|
|
_num=$((_num + 1))
|
|
|
|
|
done
|
2015-10-16 10:57:48 +01:00
|
|
|
|
2015-11-14 14:04:19 +00:00
|
|
|
# handle no existing network
|
|
|
|
|
: ${_emulation:=virtio-net}
|
2015-10-16 11:08:22 +01:00
|
|
|
|
2015-11-14 14:04:19 +00:00
|
|
|
# update configuration
|
2016-06-15 13:06:54 +01:00
|
|
|
config::set "${_name}" "network${_num}_type" "${_emulation}"
|
|
|
|
|
config::set "${_name}" "network${_num}_switch" "${_switch}" "1"
|
2018-02-06 17:39:11 -05:00
|
|
|
[ $? -eq 0 ] || util::err "errors encountered while updating guest configuration"
|
2015-10-16 10:57:48 +01:00
|
|
|
}
|
|
|
|
|
|
2015-10-19 13:27:55 +01:00
|
|
|
# 'vm install'
|
2015-06-29 11:14:42 +01:00
|
|
|
# install os to a virtual machine
|
2015-10-19 13:27:55 +01:00
|
|
|
#
|
|
|
|
|
# @param string _name the guest to install to
|
|
|
|
|
# @param string _iso the iso file in $vm_dir/.iso to use
|
|
|
|
|
#
|
2016-06-09 10:47:47 +01:00
|
|
|
core::install(){
|
2015-11-14 14:04:19 +00:00
|
|
|
local _name="$1"
|
|
|
|
|
local _iso="$2"
|
2016-08-01 16:02:57 +01:00
|
|
|
local _fulliso
|
2015-06-29 11:14:42 +01:00
|
|
|
|
2016-06-09 10:47:47 +01:00
|
|
|
[ -z "${_name}" -o -z "${_iso}" ] && util::usage
|
2015-06-29 11:14:42 +01:00
|
|
|
|
2015-11-14 14:04:19 +00:00
|
|
|
# just run start with an iso
|
2016-08-01 16:02:57 +01:00
|
|
|
datastore::iso_find "_fulliso" "${_iso}" || util::err "unable to locate iso file - '${_iso}'"
|
|
|
|
|
core::__start "${_name}" "${_fulliso}"
|
2015-06-29 11:14:42 +01:00
|
|
|
}
|
|
|
|
|
|
2015-10-19 13:27:55 +01:00
|
|
|
# 'vm startall'
|
2015-08-10 09:18:46 +01:00
|
|
|
# start all virtual machines listed in rc.conf:$vm_list
|
2015-10-19 13:27:55 +01:00
|
|
|
#
|
2016-06-09 10:47:47 +01:00
|
|
|
core::startall(){
|
2015-11-14 14:04:19 +00:00
|
|
|
[ -z "${vm_list}" ] && exit
|
2016-07-28 10:17:23 -04:00
|
|
|
core::start ${vm_list}
|
2015-06-29 11:14:42 +01:00
|
|
|
}
|
|
|
|
|
|
2015-10-19 13:27:55 +01:00
|
|
|
# 'vm stopall'
|
2015-08-10 09:18:46 +01:00
|
|
|
# stop all bhyve instances
|
|
|
|
|
# note this will also stop instances not started by vm-bhyve
|
2015-10-19 13:27:55 +01:00
|
|
|
#
|
2016-06-09 10:47:47 +01:00
|
|
|
core::stopall(){
|
2015-11-14 14:04:19 +00:00
|
|
|
local _pids=$(pgrep -f 'bhyve:')
|
2015-06-29 11:14:42 +01:00
|
|
|
|
2015-11-14 14:04:19 +00:00
|
|
|
echo "Shutting down all bhyve virtual machines"
|
|
|
|
|
killall bhyve
|
2016-04-16 08:32:51 +01:00
|
|
|
sleep 1
|
|
|
|
|
killall bhyve
|
2015-11-14 14:04:19 +00:00
|
|
|
wait_for_pids ${_pids}
|
2015-06-29 11:14:42 +01:00
|
|
|
}
|
|
|
|
|
|
2015-10-19 13:27:55 +01:00
|
|
|
# 'vm start'
|
2015-08-10 09:18:46 +01:00
|
|
|
# start a virtual machine
|
2015-10-19 13:27:55 +01:00
|
|
|
#
|
2016-07-28 10:17:23 -04:00
|
|
|
# @param string[multiple] _name the name of the guest(s) to start
|
|
|
|
|
#
|
|
|
|
|
core::start(){
|
|
|
|
|
local _name="$1"
|
|
|
|
|
local _done
|
|
|
|
|
|
|
|
|
|
[ -z "${_name}" ] && util::usage
|
|
|
|
|
: ${vm_delay:=5}
|
|
|
|
|
|
|
|
|
|
# disable foreground/interactive if we're starting more than one
|
|
|
|
|
if [ $# -ge 2 ]; then
|
|
|
|
|
VM_OPT_FOREGROUND=""
|
|
|
|
|
VM_OPT_INTERACTIVE=""
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
while [ -n "${_name}" ]; do
|
|
|
|
|
[ -n "${_done}" ] && echo "Waiting ${vm_delay} second(s)" && sleep ${vm_delay}
|
|
|
|
|
core::__start "${_name}"
|
|
|
|
|
shift
|
|
|
|
|
_name="$1"
|
|
|
|
|
_done="1"
|
|
|
|
|
done
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# actually start a virtual machine
|
|
|
|
|
#
|
2015-10-19 13:27:55 +01:00
|
|
|
# @param string _name the name of the guest to start
|
|
|
|
|
# @param optional string _iso iso file is this is an install (can only be provided through 'vm install' command)
|
|
|
|
|
#
|
2016-07-28 10:17:23 -04:00
|
|
|
core::__start(){
|
2015-11-14 14:04:19 +00:00
|
|
|
local _name="$1"
|
|
|
|
|
local _iso="$2"
|
2018-06-20 12:30:41 +00:00
|
|
|
local _cpu _memory _disk _guest _loader _console
|
|
|
|
|
local _tmux_cmd _tmux_name _util
|
2015-06-29 11:14:42 +01:00
|
|
|
|
2016-06-09 10:47:47 +01:00
|
|
|
[ -z "${_name}" ] && util::usage
|
2016-04-18 19:02:37 +01:00
|
|
|
|
2016-07-28 10:17:23 -04:00
|
|
|
echo "Starting ${_name}"
|
|
|
|
|
|
2016-04-18 19:02:37 +01:00
|
|
|
# try to find guest
|
2016-06-09 10:47:47 +01:00
|
|
|
if ! datastore::get_guest "${_name}"; then
|
2016-07-28 10:17:23 -04:00
|
|
|
echo " ! ${_name} does not seem to be a valid virtual machine"
|
2016-04-18 19:02:37 +01:00
|
|
|
return 1
|
|
|
|
|
fi
|
2015-10-10 20:32:16 +01:00
|
|
|
|
2016-07-28 10:17:23 -04:00
|
|
|
echo " * found guest in ${VM_DS_PATH}/${_name}"
|
|
|
|
|
|
2015-11-14 14:04:19 +00:00
|
|
|
# confirm we aren't running
|
2016-07-28 10:17:23 -04:00
|
|
|
if ! vm::confirm_stopped "${_name}" "1" >/dev/null 2>&1; then
|
|
|
|
|
echo " ! guest appears to be running already"
|
2018-02-06 17:39:11 -05:00
|
|
|
return 1
|
2016-07-28 10:17:23 -04:00
|
|
|
fi
|
2015-10-18 11:27:32 +01:00
|
|
|
|
2015-11-14 14:04:19 +00:00
|
|
|
# check basic settings before going into background mode
|
2016-06-09 10:47:47 +01:00
|
|
|
config::load "${VM_DS_PATH}/${_name}/${_name}.conf"
|
|
|
|
|
config::get "_cpu" "cpu"
|
|
|
|
|
config::get "_memory" "memory"
|
|
|
|
|
config::get "_disk" "disk0_name"
|
|
|
|
|
config::get "_loader" "loader"
|
2015-08-08 12:21:01 +01:00
|
|
|
|
2016-05-12 16:09:08 +01:00
|
|
|
# check minimum configuration
|
2016-04-11 11:27:16 +01:00
|
|
|
if [ -z "${_cpu}" -o -z "${_memory}" -o -z "${_disk}" ]; then
|
2016-07-28 10:17:23 -04:00
|
|
|
echo " ! incomplete virtual machine configuration"
|
2015-11-14 14:04:19 +00:00
|
|
|
return 1
|
|
|
|
|
fi
|
2015-08-08 12:21:01 +01:00
|
|
|
|
2016-05-12 16:09:08 +01:00
|
|
|
# we can only load freebsd without unrestricted guest support
|
2016-05-04 14:37:10 +01:00
|
|
|
if [ -n "${VM_NO_UG}" -a "${_loader}" != "bhyveload" ]; then
|
2016-07-28 10:17:23 -04:00
|
|
|
echo " ! no unrestricted guest support in cpu. only single vcpu FreeBSD guests supported"
|
2016-05-04 14:37:10 +01:00
|
|
|
return 1
|
|
|
|
|
fi
|
2016-05-04 14:31:14 +01:00
|
|
|
|
2018-06-20 12:30:41 +00:00
|
|
|
# check loader
|
|
|
|
|
if [ "${_loader}" = "grub" ]; then
|
|
|
|
|
_util=$(which grub-bhyve)
|
|
|
|
|
|
|
|
|
|
if [ -z "${_util}" ]; then
|
|
|
|
|
echo " ! grub requested but sysutils/grub2-bhyve not installed?"
|
|
|
|
|
return 1
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
|
2016-06-28 10:38:14 +01:00
|
|
|
# check for tmux
|
2016-11-11 15:50:13 +00:00
|
|
|
config::core::get "_console" "console" "nmdm"
|
2016-06-28 10:38:14 +01:00
|
|
|
_tmux_cmd=$(which tmux)
|
|
|
|
|
|
|
|
|
|
if [ "${_console}" = "tmux" -a -z "${_tmux_cmd}" ]; then
|
2016-07-28 10:17:23 -04:00
|
|
|
echo " ! tmux support enabled but misc/tmux not found"
|
2016-06-28 10:38:14 +01:00
|
|
|
return 1
|
|
|
|
|
fi
|
|
|
|
|
|
2016-07-28 10:17:23 -04:00
|
|
|
echo " * booting..."
|
|
|
|
|
|
2015-11-14 14:04:19 +00:00
|
|
|
# run background process to actually start bhyve
|
|
|
|
|
# this will run as long as vm is running, including restarting bhyve after guest reboot
|
2016-07-18 13:56:50 +01:00
|
|
|
if [ -n "${VM_OPT_FOREGROUND}" ]; then
|
2016-04-18 19:02:37 +01:00
|
|
|
$0 -f _run "${_name}" "${_iso}"
|
2016-06-28 10:38:14 +01:00
|
|
|
elif [ "${_console}" = "tmux" ]; then
|
2016-08-19 09:59:37 +01:00
|
|
|
# can't have dots in tmux session :( (looks like it may use . to separate window.pane)
|
|
|
|
|
# use ~ which we don't normally allow
|
|
|
|
|
_tmux_name=$(echo "${_name}" | tr "." "~")
|
2016-07-18 13:56:50 +01:00
|
|
|
|
|
|
|
|
# start session and connect if in interactive mode
|
|
|
|
|
if [ -n "${VM_OPT_INTERACTIVE}" ]; then
|
2016-08-19 09:59:37 +01:00
|
|
|
${_tmux_cmd} new -s "${_tmux_name}" $0 -tf _run "${_name}" "${_iso}"
|
2016-07-18 13:56:50 +01:00
|
|
|
else
|
2016-08-19 09:59:37 +01:00
|
|
|
${_tmux_cmd} new -ds "${_tmux_name}" $0 -tf _run "${_name}" "${_iso}"
|
2016-07-18 13:56:50 +01:00
|
|
|
fi
|
2016-04-08 21:26:09 +01:00
|
|
|
else
|
2016-04-18 19:02:37 +01:00
|
|
|
$0 _run "${_name}" "${_iso}" >/dev/null 2>&1 &
|
2016-04-08 21:26:09 +01:00
|
|
|
fi
|
2015-06-29 11:14:42 +01:00
|
|
|
}
|
|
|
|
|
|
2015-10-19 13:27:55 +01:00
|
|
|
# 'vm stop'
|
|
|
|
|
# send a kill signal to the specified guest
|
|
|
|
|
#
|
2016-07-28 10:17:23 -04:00
|
|
|
# @param string[multiple] _name name of the guest to stop
|
2015-10-19 13:27:55 +01:00
|
|
|
#
|
2016-06-09 10:47:47 +01:00
|
|
|
core::stop(){
|
2015-11-14 14:04:19 +00:00
|
|
|
local _name="$1"
|
|
|
|
|
local _pid _loadpid
|
|
|
|
|
|
2016-06-09 10:47:47 +01:00
|
|
|
[ -z "${_name}" ] && util::usage
|
2015-11-14 14:04:19 +00:00
|
|
|
|
2016-04-05 10:29:47 +01:00
|
|
|
while [ -n "${_name}" ]; do
|
2016-04-16 08:13:16 +01:00
|
|
|
if [ ! -e "/dev/vmm/${_name}" ]; then
|
2016-06-09 10:47:47 +01:00
|
|
|
util::warn "${_name} doesn't appear to be a running virtual machine"
|
2016-04-05 10:29:47 +01:00
|
|
|
else
|
2016-04-16 08:13:16 +01:00
|
|
|
_pid=$(pgrep -fx "bhyve: ${_name}")
|
|
|
|
|
_loadpid=$(pgrep -fl "grub-bhyve|bhyveload" | grep " ${_name}\$" |cut -d' ' -f1)
|
|
|
|
|
|
|
|
|
|
if [ -n "${_pid}" ]; then
|
|
|
|
|
echo "Sending ACPI shutdown to ${_name}"
|
2016-07-11 19:30:07 +01:00
|
|
|
kill "${_pid}" >/dev/null 2>&1
|
2016-04-16 08:27:19 +01:00
|
|
|
sleep 1
|
2016-07-11 19:30:07 +01:00
|
|
|
kill "${_pid}" >/dev/null 2>&1
|
2016-04-16 08:13:16 +01:00
|
|
|
elif [ -n "${_loadpid}" ]; then
|
2016-06-09 10:47:47 +01:00
|
|
|
if util::confirm "Guest ${_name} is in bootloader stage, do you wish to force exit"; then
|
2016-04-16 08:13:16 +01:00
|
|
|
echo "Killing ${_name}"
|
|
|
|
|
kill "${_loadpid}"
|
|
|
|
|
bhyvectl --destroy --vm=${_name} >/dev/null 2>&1
|
|
|
|
|
fi
|
|
|
|
|
else
|
2016-06-09 10:47:47 +01:00
|
|
|
util::warn "unable to locate process id for ${_name}"
|
2016-04-16 08:13:16 +01:00
|
|
|
fi
|
2016-04-05 10:29:47 +01:00
|
|
|
fi
|
|
|
|
|
shift
|
|
|
|
|
_name="$1"
|
|
|
|
|
done
|
2015-06-29 11:14:42 +01:00
|
|
|
}
|
|
|
|
|
|
2015-10-19 13:27:55 +01:00
|
|
|
# 'vm reset'
|
2015-06-30 10:21:30 +01:00
|
|
|
# force reset
|
2015-10-19 13:27:55 +01:00
|
|
|
#
|
|
|
|
|
# @param string _name name of the guest
|
|
|
|
|
#
|
2016-06-09 10:47:47 +01:00
|
|
|
core::reset(){
|
2015-11-14 14:04:19 +00:00
|
|
|
local _name="$1"
|
2015-06-30 10:21:30 +01:00
|
|
|
|
2016-06-09 10:47:47 +01:00
|
|
|
[ -z "${_name}" ] && util::usage
|
|
|
|
|
[ ! -e "/dev/vmm/${_name}" ] && util::err "${_name} doesn't appear to be a running virtual machine"
|
2015-06-30 10:21:30 +01:00
|
|
|
|
2018-06-20 11:59:09 +00:00
|
|
|
if [ -z "${VM_OPT_FORCE}" ]; then
|
|
|
|
|
util::confirm "Are you sure you want to forcefully reset this virtual machine" || exit 0
|
|
|
|
|
fi
|
|
|
|
|
|
2015-11-14 14:04:19 +00:00
|
|
|
bhyvectl --force-reset --vm=${_name}
|
2015-06-30 10:21:30 +01:00
|
|
|
}
|
|
|
|
|
|
2015-10-19 13:27:55 +01:00
|
|
|
# 'vm poweroff'
|
2015-06-30 10:21:30 +01:00
|
|
|
# force poweroff
|
2015-10-19 13:27:55 +01:00
|
|
|
#
|
|
|
|
|
# @param string _name name of the guest
|
|
|
|
|
#
|
2016-06-09 10:47:47 +01:00
|
|
|
core::poweroff(){
|
2015-11-14 14:04:19 +00:00
|
|
|
local _name="$1"
|
2015-06-30 10:21:30 +01:00
|
|
|
|
2016-06-09 10:47:47 +01:00
|
|
|
[ -z "${_name}" ] && util::usage
|
|
|
|
|
[ ! -e "/dev/vmm/${_name}" ] && util::err "${_name} doesn't appear to be a running virtual machine"
|
2015-06-30 10:21:30 +01:00
|
|
|
|
2018-06-20 11:59:09 +00:00
|
|
|
if [ -z "${VM_OPT_FORCE}" ]; then
|
|
|
|
|
util::confirm "Are you sure you want to forcefully poweroff this virtual machine" || exit 0
|
|
|
|
|
fi
|
|
|
|
|
|
2015-11-14 14:04:19 +00:00
|
|
|
bhyvectl --force-poweroff --vm=${_name}
|
2015-06-30 10:21:30 +01:00
|
|
|
}
|
|
|
|
|
|
2015-10-19 13:27:55 +01:00
|
|
|
# 'vm destroy'
|
|
|
|
|
# completely remove a guest
|
|
|
|
|
#
|
|
|
|
|
# @param string _name name of the guest
|
|
|
|
|
#
|
2016-06-09 10:47:47 +01:00
|
|
|
core::destroy(){
|
2015-11-14 14:04:19 +00:00
|
|
|
local _name="$1"
|
2015-08-04 16:17:11 +01:00
|
|
|
|
2016-06-09 10:47:47 +01:00
|
|
|
[ -z "${_name}" ] && util::usage
|
2018-06-25 12:20:47 +00:00
|
|
|
|
|
|
|
|
# trying to remove a snapshot?
|
|
|
|
|
echo "${_name}" | grep -qs "@"
|
|
|
|
|
|
|
|
|
|
if [ $? -eq 0 ]; then
|
|
|
|
|
zfs::remove_snapshot "${_name}"
|
|
|
|
|
exit $?
|
|
|
|
|
fi
|
2015-08-04 16:17:11 +01:00
|
|
|
|
2015-11-14 14:04:19 +00:00
|
|
|
# make sure it's stopped!
|
2018-06-25 12:20:47 +00:00
|
|
|
datastore::get_guest "${_name}" || util::err "${_name} doesn't appear to be a valid virtual machine"
|
2016-06-09 10:47:47 +01:00
|
|
|
vm::confirm_stopped "${_name}" || exit 1
|
2015-10-21 11:36:04 +01:00
|
|
|
|
2018-06-20 11:59:09 +00:00
|
|
|
if [ -z "${VM_OPT_FORCE}" ]; then
|
|
|
|
|
util::confirm "Are you sure you want to completely remove this virtual machine" || exit 0
|
|
|
|
|
fi
|
|
|
|
|
|
2016-06-09 10:47:47 +01:00
|
|
|
zfs::destroy_dataset "${VM_DS_ZFS_DATASET}/${_name}"
|
2016-04-18 19:02:37 +01:00
|
|
|
[ -e "${VM_DS_PATH}/${_name}" ] && rm -R "${VM_DS_PATH}/${_name}"
|
2015-08-04 16:17:11 +01:00
|
|
|
}
|
|
|
|
|
|
2015-10-21 10:51:38 +01:00
|
|
|
# 'vm rename'
|
|
|
|
|
# rename an existing guest
|
|
|
|
|
#
|
|
|
|
|
# @param string _old the existing guest name
|
|
|
|
|
# @param string _new the new guest name
|
|
|
|
|
#
|
2016-06-09 10:47:47 +01:00
|
|
|
core::rename(){
|
2015-11-14 14:04:19 +00:00
|
|
|
local _old="$1"
|
|
|
|
|
local _new="$2"
|
2015-10-21 10:51:38 +01:00
|
|
|
|
2016-06-09 10:47:47 +01:00
|
|
|
[ -z "${_old}" -o -z "${_new}" ] && util::usage
|
|
|
|
|
core::check_name "${_new}" || util::err "invalid virtual machine name - '${_name}'"
|
2015-10-21 10:51:38 +01:00
|
|
|
|
2016-06-09 10:47:47 +01:00
|
|
|
datastore::get_guest "${_new}" && util::err "directory ${VM_DS_PATH}/${_new} already exists"
|
|
|
|
|
datastore::get_guest "${_old}" || util::err "${_old} doesn't appear to be a valid virtual machine"
|
2016-04-18 19:02:37 +01:00
|
|
|
|
2015-11-14 14:04:19 +00:00
|
|
|
# confirm guest stopped
|
2016-06-09 10:47:47 +01:00
|
|
|
vm::confirm_stopped "${_old}" || exit 1
|
2015-10-21 11:36:04 +01:00
|
|
|
|
2015-11-14 14:04:19 +00:00
|
|
|
# rename zfs dataset
|
2016-06-09 10:47:47 +01:00
|
|
|
zfs::rename_dataset "${_old}" "${_new}"
|
2015-10-21 10:51:38 +01:00
|
|
|
|
2015-11-14 14:04:19 +00:00
|
|
|
# rename folder if it still exists (shouldn't if zfs mode and rename worked)
|
2016-04-19 14:52:37 +01:00
|
|
|
if [ -d "${VM_DS_PATH}/${_old}" ]; then
|
2016-04-18 19:02:37 +01:00
|
|
|
mv "${VM_DS_PATH}/${_old}" "${VM_DS_PATH}/${_new}" >/dev/null 2>&1
|
2018-02-06 17:39:11 -05:00
|
|
|
[ $? -eq 0 ] || util::err "failed to rename guest directory"
|
2015-11-14 14:04:19 +00:00
|
|
|
fi
|
2015-10-21 10:51:38 +01:00
|
|
|
|
2015-11-14 14:04:19 +00:00
|
|
|
# rename config file
|
2016-04-18 19:02:37 +01:00
|
|
|
mv "${VM_DS_PATH}/${_new}/${_old}.conf" "${VM_DS_PATH}/${_new}/${_new}.conf" >/dev/null 2>&1
|
2018-02-06 17:39:11 -05:00
|
|
|
[ $? -eq 0 ] || util::err "changed guest directory but failed to rename configuration file"
|
2015-10-21 10:51:38 +01:00
|
|
|
}
|
|
|
|
|
|
2015-10-19 13:27:55 +01:00
|
|
|
# 'vm console'
|
|
|
|
|
# connect to the console (nmdm) of the specified guest
|
|
|
|
|
# we store the nmdm for com1 & com2 in $vm_dir/{guest}/console
|
|
|
|
|
# if no port is specified, we use the first one that is specified in the configuration file
|
|
|
|
|
# so if comports="com2 com1", it will connect to com2
|
|
|
|
|
# the boot loader always using the nmdm device of the first com port listed
|
|
|
|
|
#
|
|
|
|
|
# @param string _name name of the guest
|
|
|
|
|
# @param string _port the port to connect to (default = first in configuration)
|
|
|
|
|
#
|
2016-06-09 10:47:47 +01:00
|
|
|
core::console(){
|
2015-11-14 14:04:19 +00:00
|
|
|
local _name="$1"
|
|
|
|
|
local _port="$2"
|
2016-06-28 10:38:14 +01:00
|
|
|
local _console _tmux _tmux_cmd
|
2015-11-14 14:04:19 +00:00
|
|
|
|
2016-06-09 10:47:47 +01:00
|
|
|
[ -z "${_name}" ] && util::usage
|
2016-04-18 19:02:37 +01:00
|
|
|
|
2016-06-09 10:47:47 +01:00
|
|
|
datastore::get_guest "${_name}" || util::err "${_name} doesn't appear to be a valid virtual machine"
|
|
|
|
|
[ ! -e "/dev/vmm/${_name}" ] && util::err "${_name} doesn't appear to be a running virtual machine"
|
2015-11-14 14:04:19 +00:00
|
|
|
|
2016-06-28 10:38:14 +01:00
|
|
|
if [ -e "${VM_DS_PATH}/${_name}/console" ]; then
|
2016-06-28 10:54:10 +01:00
|
|
|
|
2016-06-28 10:38:14 +01:00
|
|
|
# did user specify a com port?
|
|
|
|
|
# if not, get first in the file (the first will also be the console used for loader)
|
|
|
|
|
if [ -n "${_port}" ]; then
|
|
|
|
|
_console=$(grep "${_port}=" "${VM_DS_PATH}/${_name}/console" | cut -d= -f2)
|
|
|
|
|
else
|
2016-06-28 10:54:10 +01:00
|
|
|
_console=$(head -n 1 "${VM_DS_PATH}/${_name}/console" | grep "^com" | cut -d= -f2)
|
2016-06-28 10:38:14 +01:00
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
|
2016-08-03 10:10:44 +01:00
|
|
|
# is this a tmux console?
|
|
|
|
|
if [ "${_console%%/*}" = "tmux" ]; then
|
2016-06-28 10:38:14 +01:00
|
|
|
_tmux_cmd=$(which tmux)
|
|
|
|
|
|
|
|
|
|
if [ -n "${_tmux_cmd}" ]; then
|
2016-06-28 10:43:04 +01:00
|
|
|
_tmux=$("${_tmux_cmd}" ls |grep "^${_name}:")
|
2016-06-28 10:38:14 +01:00
|
|
|
|
|
|
|
|
if [ -n "${_tmux}" ]; then
|
2016-08-19 09:59:37 +01:00
|
|
|
${_tmux_cmd} attach -t ${_console##*/}
|
2016-06-28 10:38:14 +01:00
|
|
|
exit
|
|
|
|
|
fi
|
|
|
|
|
fi
|
2015-11-14 14:04:19 +00:00
|
|
|
fi
|
|
|
|
|
|
2016-06-09 10:47:47 +01:00
|
|
|
[ -z "${_console}" ] && util::err "unable to locate console device for this virtual machine"
|
2015-11-14 14:04:19 +00:00
|
|
|
cu -l "${_console}"
|
2015-06-29 11:14:42 +01:00
|
|
|
}
|
|
|
|
|
|
2015-10-19 13:27:55 +01:00
|
|
|
# 'vm configure'
|
2015-08-03 14:07:46 +01:00
|
|
|
# configure a machine (edit the configuration file)
|
2015-10-19 13:27:55 +01:00
|
|
|
#
|
|
|
|
|
# @param string _name name of the guest
|
|
|
|
|
#
|
2016-06-09 10:47:47 +01:00
|
|
|
core::configure(){
|
2015-11-14 14:04:19 +00:00
|
|
|
local _name="$1"
|
2015-08-03 14:07:46 +01:00
|
|
|
|
2016-06-09 10:47:47 +01:00
|
|
|
[ -z "${_name}" ] && util::usage
|
2015-11-14 14:04:19 +00:00
|
|
|
[ -z "${EDITOR}" ] && EDITOR=vi
|
2016-04-18 19:02:37 +01:00
|
|
|
|
2016-06-09 10:47:47 +01:00
|
|
|
datastore::get_guest "${_name}" || \
|
|
|
|
|
util::err "cannot locate configuration file for virtual machine: ${_name}"
|
2015-08-04 16:17:11 +01:00
|
|
|
|
2016-04-18 19:02:37 +01:00
|
|
|
$EDITOR "${VM_DS_PATH}/${_name}/${_name}.conf"
|
2015-08-03 14:07:46 +01:00
|
|
|
}
|
|
|
|
|
|
2015-10-19 13:27:55 +01:00
|
|
|
# 'vm iso'
|
2015-06-29 11:14:42 +01:00
|
|
|
# list iso images or get a new one
|
2015-10-19 13:27:55 +01:00
|
|
|
#
|
|
|
|
|
# @param string _url if specified, the url will be fetch'ed into $vm_dir/.iso
|
|
|
|
|
#
|
2016-06-09 10:47:47 +01:00
|
|
|
core::iso(){
|
2015-11-14 14:04:19 +00:00
|
|
|
local _url="$1"
|
|
|
|
|
|
|
|
|
|
if [ -n "${_url}" ]; then
|
|
|
|
|
fetch -o "${vm_dir}/.iso" "${_url}"
|
|
|
|
|
else
|
2016-08-01 16:02:57 +01:00
|
|
|
datastore::iso_list
|
2015-11-14 14:04:19 +00:00
|
|
|
fi
|
2015-06-30 10:21:30 +01:00
|
|
|
}
|
2015-11-16 16:29:55 +00:00
|
|
|
|
2016-03-26 12:50:28 +00:00
|
|
|
# 'vm passthru'
|
|
|
|
|
# show a list of available passthrough devices
|
|
|
|
|
# and their device number
|
|
|
|
|
#
|
2016-06-09 10:47:47 +01:00
|
|
|
core::passthru(){
|
2016-03-26 12:50:28 +00:00
|
|
|
local _dev _sbf _desc _ready
|
|
|
|
|
local _format="%-10s %-12s %-12s %s\n"
|
|
|
|
|
|
|
|
|
|
printf "${_format}" "DEVICE" "BHYVE ID" "READY" "DESCRIPTION"
|
|
|
|
|
|
2016-03-29 15:18:11 +01:00
|
|
|
pciconf -l | awk -F'[@:]' '{ print $1,$3 "/" $4 "/" $5}' | \
|
2016-03-26 12:50:28 +00:00
|
|
|
while read _dev _sbf; do
|
|
|
|
|
|
|
|
|
|
_ready=$(echo "${_dev}" | grep ^ppt)
|
|
|
|
|
[ -n "${_ready}" ] && _ready="Yes"
|
|
|
|
|
|
|
|
|
|
_desc=$(pciconf -lv | grep -A2 "^${_dev}@" | tail -n1 | grep device | cut -d\' -f2)
|
|
|
|
|
printf "${_format}" "${_dev}" "${_sbf}" "${_ready:-No}" "${_desc:--}"
|
|
|
|
|
done
|
|
|
|
|
}
|
2016-07-19 15:05:03 +01:00
|
|
|
|
|
|
|
|
# 'vm get'
|
|
|
|
|
# get a core configuration setting
|
|
|
|
|
#
|
|
|
|
|
core::get(){
|
|
|
|
|
local _var="$1"
|
|
|
|
|
local _val _format="%-20s%s\n"
|
|
|
|
|
local IFS=";"
|
|
|
|
|
|
|
|
|
|
printf "${_format}" "SETTING" "VALUE"
|
|
|
|
|
|
|
|
|
|
if [ "${_var}" = "all" ]; then
|
|
|
|
|
|
|
|
|
|
for _var in ${VM_CONFIG_USER}; do
|
2016-11-11 15:50:13 +00:00
|
|
|
config::core::get "_val" "${_var}"
|
2016-07-19 15:05:03 +01:00
|
|
|
printf "${_format}" "${_var}" "${_val:--}"
|
|
|
|
|
done
|
|
|
|
|
else
|
|
|
|
|
while [ -n "${_var}" ]; do
|
|
|
|
|
if core::__valid_config_setting "${_var}"; then
|
2016-11-11 15:50:13 +00:00
|
|
|
config::core::get "_val" "${_var}"
|
2016-07-19 15:05:03 +01:00
|
|
|
printf "${_format}" "${_var}" "${_val:--}"
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
shift
|
|
|
|
|
_var="$1"
|
|
|
|
|
done
|
|
|
|
|
fi
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# 'vm set'
|
|
|
|
|
# set a core configuration setting
|
|
|
|
|
#
|
|
|
|
|
core::set(){
|
|
|
|
|
local _pair="$1"
|
|
|
|
|
local _key _val
|
|
|
|
|
|
|
|
|
|
while [ -n "${_pair}" ]; do
|
|
|
|
|
_key="${_pair%%=*}"
|
|
|
|
|
_val="${_pair#*=}"
|
|
|
|
|
|
|
|
|
|
if core::__valid_config_setting "${_key}"; then
|
2016-11-11 15:50:13 +00:00
|
|
|
config::core::set "${_key}" "${_val}"
|
2016-07-19 15:05:03 +01:00
|
|
|
else
|
|
|
|
|
util::err "invalid configuration setting - '${_key}'"
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
shift
|
|
|
|
|
_pair="$1"
|
|
|
|
|
done
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# check if the specified string is a valid core configuration
|
|
|
|
|
# setting that the user can change
|
|
|
|
|
#
|
|
|
|
|
# @param string the setting name to look for
|
|
|
|
|
#
|
|
|
|
|
core::__valid_config_setting(){
|
|
|
|
|
echo "${VM_CONFIG_USER}" | grep -iqs "${1};"
|
|
|
|
|
}
|