Files
vm-bhyve/lib/vm-datastore

501 lines
14 KiB
Plaintext
Raw Normal View History

2016-04-18 19:03:27 +01:00
#!/bin/sh
#-------------------------------------------------------------------------+
2016-08-03 18:48:27 +01:00
# Copyright (C) 2016 Matt Churchyard (churchers@gmail.com)
2016-04-18 19:03:27 +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.
# 'vm datastore list'
# show configured datastores
#
datastore::list(){
2016-04-19 20:49:36 +01:00
local _format="%-15s %-11s %-25s %s"
2016-06-28 10:43:04 +01:00
local _name _type _dataset _path _spec
2016-04-18 19:03:27 +01:00
# headings
printf "${_format}\n" "NAME" "TYPE" "PATH" "ZFS DATASET"
# add the default datastore
_name="default"
_path="${vm_dir}"
# get the type and path
if [ "${VM_ZFS}" ]; then
_type="zfs"
_dataset="${VM_ZFS_DATASET}"
else
_type="directory"
_dataset="-"
fi
printf "${_format}\n" "${_name}" "${_type}" "${_path}" "${_dataset}"
for _name in ${VM_DATASTORE_LIST}; do
[ "${_name}" = "default" ] && continue
2016-11-11 15:50:13 +00:00
config::core::get "_spec" "path_${_name}"
2016-04-18 19:03:27 +01:00
[ -z "${_spec}" ] && continue
if [ "${_spec%%:*}" = "zfs" ]; then
_type="zfs"
_dataset="${_spec#*:}"
datastore::__resolve_path "_path" "${_spec}"
elif [ "${_spec%%:*}" = "iso" ]; then
_type="iso"
_path="${_spec#*:}"
_dataset="-"
elif [ "${_spec%%:*}" = "img" ]; then
_type="img"
_path="${_spec#*:}"
_dataset="-"
2016-04-18 19:03:27 +01:00
else
_type="directory"
_path="${_spec}"
_dataset="-"
fi
printf "${_format}\n" "${_name}" "${_type}" "${_path}" "${_dataset}"
done
}
# 'vm datastore add'
# create a new datastore
#
# we don't try and create directories or datasets.
# the user should do that first
#
# @param string _name datastore name
# @param string _spec specification (either /path or zfs:dataset)
#
datastore::add(){
2016-04-18 19:03:27 +01:00
local _name="$1"
local _spec="$2"
2016-07-04 10:27:50 +01:00
local _mount _num=0 _curr
2016-04-18 19:03:27 +01:00
[ -z "${_name}" -o -z "${_spec}" ] && util::usage
util::check_name "${_name}" || util::err "invalid datastore name - '${_name}'"
2016-04-18 19:03:27 +01:00
# check name not in use
2016-07-04 10:27:50 +01:00
for _curr in ${VM_DATASTORE_LIST}; do
[ "${_curr}" = "${_name}" ] && util::err "datstore '${_name}' already exists!"
done
2016-04-18 19:03:27 +01:00
# look for zfs
if [ "${_spec%%:*}" = "zfs" ]; then
# try to find mountpoint
_mount=$(mount | grep "^${_spec#*:} " |cut -d' ' -f3)
[ -z "${_mount}" ] && util::err "${_spec} doesn't seem to be a valid, mounted dataset"
2016-04-18 19:03:27 +01:00
else
# make sure it's a directory
[ ! -d "${_spec}" ] && util::err "${_spec} doesn't seem to be a valid directory"
2016-04-18 19:03:27 +01:00
_mount="${_spec}"
fi
# see if this is already our default datastore
[ "${_mount}" = "${vm_dir}" ] && util::err "specified path already exists as default datastore"
2016-04-18 19:03:27 +01:00
# save
2016-11-11 15:50:13 +00:00
config::core::set "datastore_list" "${_name}" "1"
config::core::set "path_${_name}" "${_spec}"
[ $? -eq 0 ] || util::err "error saving settings to configuration file"
2016-04-18 19:03:27 +01:00
}
2016-04-18 19:25:11 +01:00
# remove a datastore
# we don't actually delete anything, just remove from config
#
# @param string _name name of dataset
#
datastore::remove(){
2016-04-18 19:25:11 +01:00
local _name="$1"
local _ds _found
2016-04-18 19:25:11 +01:00
[ "${_name}" = "default" ] && util::err "cannot remove default datastore"
2016-05-29 17:40:40 +01:00
for _ds in ${VM_DATASTORE_LIST}; do
[ "${_ds}" = "${_name}" ] && _found="1"
done
# found the dataset?
[ -z "${_found}" ] && util::err "unable to locate the specified dataset"
2016-04-18 19:25:11 +01:00
2016-11-11 15:50:13 +00:00
config::core::remove "datastore_list" "${_name}"
config::core::remove "path_${_name}"
[ $? -eq 0 ] || util::err "error removing settings from configuration file"
2016-04-18 19:25:11 +01:00
}
2016-04-18 19:03:27 +01:00
# get the filesystem path for the specified dataset spec
# this can either be just a path, or ZFS dataset
#
# @param string _var variable to put path into
# @param string _spec the path spec (either directory or zfs:dataset)
# @return non-zero on error
#
datastore::__resolve_path(){
2016-04-18 19:03:27 +01:00
local _var="$1"
local _spec="$2"
if [ "${_spec%%:*}" = "zfs" ]; then
_mount=$(mount | grep "^${_spec#*:} " |cut -d' ' -f3)
if [ -n "${_mount}" ]; then
setvar "${_var}" "${_mount}"
return 0
fi
2018-10-17 12:05:15 +02:00
elif [ "${_spec%%:*}" = "iso" ] || [ "${_spec%%:*}" = "img" ]; then
setvar "${_var}" "${_spec#*:}"
return 0
2016-04-18 19:03:27 +01:00
else
if [ -d "${_spec}" ]; then
setvar "${_var}" "${_spec}"
return 0
fi
fi
setvar "${_var}" ""
return 1
}
# load list of datastores into VM_DATASTORE_LIST
#
# @modifies VM_DATASTORE_LIST
#
datastore::load(){
2016-11-11 15:50:13 +00:00
config::core::get "VM_DATASTORE_LIST" "datastore_list"
2016-04-18 19:03:27 +01:00
VM_DATASTORE_LIST="default${VM_DATASTORE_LIST:+ }${VM_DATASTORE_LIST}"
}
# init global settings for a vm
# we take a guest name and try to find it in all
# datastores. we don't allow duplicate names, and
# if there is, we will just return the first found.
#
# @param string _guest guest name
# @return non-zero on error
# @modifies VM_DS_NAME VM_DS_PATH VM_DS_ZFS VM_DS_ZFS_DATASET
#
datastore::get_guest(){
2016-04-18 19:03:27 +01:00
local _guest="$1"
local _ds _spec _path _found _zfs _dataset
# look in default store
if [ -f "${vm_dir}/${_guest}/${_guest}.conf" ]; then
_found="1"
_ds="default"
_path="${vm_dir}"
_zfs="${VM_ZFS}"
_dataset="${VM_ZFS_DATASET}"
fi
# look on other datastores
if [ -z "${_found}" ]; then
for _ds in ${VM_DATASTORE_LIST}; do
[ "${_ds}" = "default" ] && continue
2016-11-11 15:50:13 +00:00
config::core::get "_spec" "path_${_ds}"
2018-10-17 12:05:15 +02:00
if [ "${_spec%%:*}" = "iso" ] || [ "${_spec%%:*}" = "img" ]; then
continue
fi
datastore::__resolve_path "_path" "${_spec}"
2016-04-18 19:03:27 +01:00
if [ -f "${_path}/${_guest}/${_guest}.conf" ]; then
[ "${_spec%%:*}" = "zfs" ] && _zfs="1" && _dataset="${_spec#*:}"
_found="1"
break
fi
done
fi
# make sure we have a path
[ -z "${_found}" ] && return 1
# set variables
VM_DS_NAME="${_ds}"
VM_DS_PATH="${_path}"
VM_DS_ZFS="${_zfs}"
VM_DS_ZFS_DATASET="${_dataset}"
}
# get the path and details for a datastore
2016-04-27 16:18:00 +01:00
# put into same variables as datastore_get_guest
2016-04-18 19:03:27 +01:00
#
# @param string _ds datastore name
# @return non-zero on error
# @modifies VM_DS_PATH VM_DS_ZFS VM_DS_ZFS_DATASET
#
datastore::get(){
2016-04-18 19:03:27 +01:00
local _ds="$1"
local _spec _path _zfs _dataset
# check for default
if [ "${_ds}" = "default" ]; then
VM_DS_NAME="default"
VM_DS_PATH="${vm_dir}"
VM_DS_ZFS="${VM_ZFS}"
VM_DS_ZFS_DATASET="${VM_ZFS_DATASET}"
return 0
fi
2016-11-11 15:50:13 +00:00
config::core::get "_spec" "path_${_ds}"
2016-04-18 19:03:27 +01:00
[ -z "${_spec}" ] && return 1
2018-10-17 12:05:15 +02:00
# skip iso and img stores
if [ "${_spec%%:*}" = "iso" ] || [ "${_spec%%:*}" = "img" ]; then
return 1
fi
datastore::__resolve_path "_path" "${_spec}" || return 1
2016-04-18 19:03:27 +01:00
[ "${_spec%%:*}" = "zfs" ] && _zfs="1" && _dataset="${_spec#*:}"
# set variables
VM_DS_NAME="${_ds}"
VM_DS_PATH="${_path}"
VM_DS_ZFS="${_zfs}"
VM_DS_ZFS_DATASET="${_dataset}"
}
# get the path for an iso datastore
#
# @param string _ds datastore name
#
datastore::get_iso(){
local _ds="$1"
# default?
# we use the .iso subdir in that case
if [ "${_ds}" = "default" ]; then
VM_DS_PATH="${vm_dir}/.iso"
return 0
fi
config::core::get "_spec" "path_${_ds}"
[ -z "${_spec}" ] && return 1
# should be an iso ds
[ "${_spec%%:*}" = "iso" ] || return 1
datastore::__resolve_path "_path" "${_spec}" || return 1
VM_DS_PATH="${_path}"
}
# get the path for an img datastore
#
# @param string _ds datastore name
#
datastore::get_img(){
local _ds="$1"
# default?
# we use the .img subdir in that case
if [ "${_ds}" = "default" ]; then
VM_DS_PATH="${vm_dir}/.img"
return 0
fi
config::core::get "_spec" "path_${_ds}"
[ -z "${_spec}" ] && return 1
# should be an img ds
[ "${_spec%%:*}" = "img" ] || return 1
datastore::__resolve_path "_path" "${_spec}" || return 1
VM_DS_PATH="${_path}"
}
# add a datastore for iso files
#
# @param string _name the name of the datastore
# @param string _path filesystem path
#
datastore::iso(){
local _name="$1"
local _path="$2"
[ -z "${_name}" -o -z "${_path}" ] && util::usage
util::check_name "${_name}" || util::err "invalid datastore name - '${_name}'"
# check name not in use
for _curr in ${VM_DATASTORE_LIST}; do
[ "${_curr}" = "${_name}" ] && util::err "datstore '${_name}' already exists!"
done
# make sure directory exists
[ ! -d "${_path}" ] && util::err "specified directory does not appear to be valid"
# save
2016-11-11 15:50:13 +00:00
config::core::set "datastore_list" "${_name}" "1"
config::core::set "path_${_name}" "iso:${_path}"
[ $? -eq 0 ] || util::err "error saving settings to configuration file"
}
# add a datastore for img files
#
# @param string _name the name of the datastore
# @param string _path filesystem path
#
datastore::img(){
local _name="$1"
local _path="$2"
[ -z "${_name}" -o -z "${_path}" ] && util::usage
util::check_name "${_name}" || util::err "invalid datastore name - '${_name}'"
# check name not in use
for _curr in ${VM_DATASTORE_LIST}; do
[ "${_curr}" = "${_name}" ] && util::err "datstore '${_name}' already exists!"
done
# make sure directory exists
[ ! -d "${_path}" ] && util::err "specified directory does not appear to be valid"
# save
config::core::set "datastore_list" "${_name}" "1"
config::core::set "path_${_name}" "img:${_path}"
[ $? -eq 0 ] || util::err "error saving settings to configuration file"
}
# find an iso file by looking in the default location
# and any "iso" datastores
#
# @param string _var variable name to put full iso path into
# @param string _file iso filename to look for
# @return int success if found
#
datastore::iso_find(){
local _var="$1"
local _file="$2"
local _ds _spec
# given a full path?
if [ -z "${_file%%/*}" ] && [ -r "${_file}" ]; then
setvar "${_var}" "${_file}"
return 0
fi
# file exists in current dir?
if [ -r "$(pwd)/${_file}" ]; then
setvar "${_var}" "$(pwd)/${_file}"
return 0
fi
# look in default store
if [ -r "${vm_dir}/.iso/${_file}" ]; then
setvar "${_var}" "${vm_dir}/.iso/${_file}"
return 0
fi
for _ds in ${VM_DATASTORE_LIST}; do
2016-11-11 15:50:13 +00:00
config::core::get "_spec" "path_${_ds}"
[ "${_spec%%:*}" != "iso" ] && continue
if [ -r "${_spec#*:}/${_file}" ]; then
setvar "${_var}" "${_spec#*:}/${_file}"
return 0
fi
done
return 1
}
# find an img file by looking in the default location
# and any "img" datastores
#
# @param string _var variable name to put full img path into
# @param string _file img filename to look for
# @return int success if found
#
datastore::img_find(){
local _var="$1"
local _file="$2"
local _ds _spec
# given a full path?
if [ -z "${_file%%/*}" ] && [ -r "${_file}" ]; then
setvar "${_var}" "${_file}"
return 0
fi
# file exists in current dir?
if [ -r "$(pwd)/${_file}" ]; then
setvar "${_var}" "$(pwd)/${_file}"
return 0
fi
# look in default store
if [ -r "${vm_dir}/.img/${_file}" ]; then
setvar "${_var}" "${vm_dir}/.img/${_file}"
return 0
fi
for _ds in ${VM_DATASTORE_LIST}; do
config::core::get "_spec" "path_${_ds}"
[ "${_spec%%:*}" != "img" ] && continue
if [ -r "${_spec#*:}/${_file}" ]; then
setvar "${_var}" "${_spec#*:}/${_file}"
return 0
fi
done
return 1
}
# list iso files
#
datastore::iso_list(){
local _ds _spec _format="%-20s%s\n"
printf "${_format}" "DATASTORE" "FILENAME"
# look for default iso location
[ -d "${vm_dir}/.iso" ] && ls -1 "${vm_dir}/.iso" | awk '{printf "'${_format}'","default",$1}'
# look for iso datastores
for _ds in ${VM_DATASTORE_LIST}; do
2016-11-11 15:50:13 +00:00
config::core::get "_spec" "path_${_ds}"
[ "${_spec%%:*}" != "iso" ] && continue
[ -d "${_spec#*:}" ] && ls -1 ${_spec#*:} | awk '{printf "'${_format}'","'${_ds}'",$1}'
done
}
# list img files
#
datastore::img_list(){
local _ds _spec _format="%-20s%s\n"
printf "${_format}" "DATASTORE" "FILENAME"
# look for default img location
[ -d "${vm_dir}/.img" ] && ls -1 "${vm_dir}/.img" | awk '{printf "'${_format}'","default",$1}'
# look for img datastores
for _ds in ${VM_DATASTORE_LIST}; do
config::core::get "_spec" "path_${_ds}"
[ "${_spec%%:*}" != "img" ] && continue
[ -d "${_spec#*:}" ] && ls -1 ${_spec#*:} | awk '{printf "'${_format}'","'${_ds}'",$1}'
done
}