diff --git a/README.md b/README.md index 555ec7c..9f42bd2 100644 --- a/README.md +++ b/README.md @@ -215,7 +215,7 @@ If you are using the default csh/tcsh shell built into FreeBSD, the following co autocomplete to work for all the currently supported functions: complete vm \ - 'p@1@(list create install start stop console configure reset poweroff switch iso)@' \ + 'p@1@(list create install start stop console configure reset poweroff destroy clone switch iso)@' \ 'n@create@n@' \ 'n@list@n@' \ 'n@iso@n@' \ diff --git a/lib/vm-cmd b/lib/vm-cmd index 2327636..8569f74 100644 --- a/lib/vm-cmd +++ b/lib/vm-cmd @@ -82,6 +82,9 @@ __parse_cmd(){ configure) __vm_configure "$@" ;; + clone) + __zfs_clone "$@" + ;; *) __usage ;; diff --git a/lib/vm-common b/lib/vm-common index a3e592d..643b644 100644 --- a/lib/vm-common +++ b/lib/vm-common @@ -77,12 +77,13 @@ Usage: vm ... install start stop - console + console [com1|com2] startall stopall reset poweroff configure + clone iso [url] image list image create [-d description] diff --git a/lib/vm-zfs b/lib/vm-zfs index 11c8f07..2265fed 100644 --- a/lib/vm-zfs +++ b/lib/vm-zfs @@ -86,6 +86,38 @@ __zfs_make_zvol(){ [ $? -ne 0 ] && __err "failed to create new ZVOL ${VM_ZFS_DATASET}/${_name}" } +# clone a vm +__zfs_clone(){ + local _old="$1" + local _new="$2" + local _uuid _uuidpart _fs _newfs + + [ -z "${_old}" -o -z "${_new}" ] && __usage + [ ! "${VM_ZFS}" = "1" ] && __err "cannot clone guests unless ZFS support is enabled" + [ ! -e "${vm_dir}/${_old}/${_old}.conf" ] && __err "${_old} does not appear to be an existing virtual machine" + [ -e "${vm_dir}/${_old}/run.lock" ] && __err "${_old} must be powered off first" + [ -d "${vm_dir}/${_new}" ] && __err "directory ${vm_dir}/${_new} already exists" + + _uuid=$(uuidgen) + _uuidpart=$(echo "${_uuid}" |awk -F- '{print $1}') + + zfs snapshot -r ${VM_ZFS_DATASET}/${_old}@${_uuidpart} + [ $? -ne 0 ] && __err "failed to create snapshot ${VM_ZFS_DATASET}/${_old}@${_uuidpart}" + + zfs list -o name -rHt filesystem,volume ${VM_ZFS_DATASET}/${_old} | \ + while read _fs; do + _newfs=$(echo "${_fs}" | sed "s@${VM_ZFS_DATASET}/${_old}@${VM_ZFS_DATASET}/${_new}@") + + zfs clone ${_fs}@${_uuidpart} ${_newfs} + [ $? -ne 0 ] && __err "error while cloning datasets" + done + + # update new guest + mv "${vm_dir}/${_new}/${_old}.conf" "${vm_dir}/${_new}/${_new}.conf" + sysrc -inqf "${vm_dir}/${_new}/${_new}.conf" "uuid=${_uuid}" >/dev/null 2>&1 + rm "${vm_dir}/${_new}/vm-bhyve.log" >/dev/null 2>&1 +} + # create an image of a vm __zfs_image_create(){ local _name diff --git a/vm.8 b/vm.8 index b894df1..48a33c4 100644 --- a/vm.8 +++ b/vm.8 @@ -65,6 +65,10 @@ .Nm .Cm configure .Ar name +.Nm +.Cm clone +.Ar name +.Ar new-name .Pp .Nm .Cm iso @@ -456,6 +460,15 @@ The command simply opens the virtual machine configuration file in your default editor, allowing you to easily make changes. Please note, changes do not take effect until the virtual machine is fully shutdown and restarted. +.It Cm clone Ar name Ar new-name +Create a clone of the virtual machine +.Pa name , +as long as it is currently powered off. The new machine will be called +.Pa new-name , +and will be ready to boot with a newly assigned UUID and empty log file. +.Pp +Please note that this function requires ZFS, and a snapshot will be taken of the original +guest, along with any descendant datasets. .It Cm iso Op Ar url List all the ISO files currently stored in the .Pa $vm_dir/.iso