#!/usr/local/bin/cbsd
#v12.1.0
MYARG="old new"
MYOPTARG="host_hostname restart"
MYDESC="Rename bhyve"
CBSDMODULE="bhyve"
ADDHELP="restart=1 - for online jails. Force to stop if online, rename and start again\n"
EXTHELP="wf_jrename"

. ${subrdir}/nc.subr
. ${tools}
. ${jfs}
. ${strings}

restart=0
. ${cbsdinit}

[ -z "${old}" ] && err 1 "${N1_COLOR}Give me old jname${N0_COLOR}"
[ -z "${new}" ] && err 1 "${N1_COLOR}Give me new jname${N0_COLOR}"

if [ "${mod_cbsd_queue_enabled}" = "YES" -a -z "${MOD_CBSD_QUEUE_DISABLED}" ]; then
	readconf cbsd_queue.conf
	[ -z "${cbsd_queue_backend}" ] && MOD_CBSD_QUEUE_DISABLED="1"
fi

DST="${jaildatadir}/${new}-${jaildatapref}"
SRC="${jaildatadir}/${old}-${jaildatapref}"
JAILDIR="${jaildir}/${new}"
JAILFSTAB="${jailfstabdir}/${jailfstabpref}${new}"
JAILFSTABORIG="${jailfstabdir}/${jailfstabpref}${old}"
JAILLOCALFSTAB="${jailfstabdir}/${jailfstabpref}${new}.local"
JAILLOCALFSTABORIG="${jailfstabdir}/${jailfstabpref}${old}.local"
SYSDIROLD="${jailsysdir}/${old}"
SYSDIRNEW="${jailsysdir}/${new}"

# store new params in tmp variable
host_hostname_new="${host_hostname}"
ip4_addr_new="${ip4_addr}"

jname="${old}"
. ${subrdir}/rcconf.subr

[ -z "${host_hostname_new}" ] && host_hostname_new="${host_hostname}"
[ -z "${ip4_addr_new}" ] && ip4_addr_new="${ip4_addr}"
[ "${emulator}" != "bhyve" ] && err 1 "${N1_COLOR}Not bhyve mode${N0_COLOR}"

. ${subrdir}/time.subr
st_time=$( ${DATE_CMD} +%s )

myjid=$( cbsdsqlro local SELECT jname FROM jails WHERE jname=\"${new}\" 2>/dev/null)
[ -n "${myjid}" ] && err 1 "${N1_COLOR}Jail already exist: ${N2_COLOR}${new}${N0_COLOR}"

if [ "${mod_cbsd_queue_enabled}" = "YES" -a -z "${MOD_CBSD_QUEUE_DISABLED}" ]; then
	[ -n "${cbsd_jail_queue_name}" ] && ${cbsd_queue_backend} cbsd_queue_name=${cbsd_bhyve_queue_name} id="${old}" jname="${new}" cmd=brename status=1
fi

oldjid=${status}

case ${status} in
	0)
		;;
	*)
		if [ ${restart} -eq 1 ]; then
			cbsdlogger NOTICE ${CBSD_APP}: vm ${jname} will be stopped due to restart flags on
			bstop jname=${old}
		else
			if [ "${mod_cbsd_queue_enabled}" = "YES" -a -z "${MOD_CBSD_QUEUE_DISABLED}" ]; then
				[ -n "${cbsd_jail_queue_name}" ] && ${cbsd_queue_backend} cbsd_queue_name=${cbsd_bhyve_queue_name} id="${new}" jname="${new}" cmd=brename status=2
			fi
			log_err 1 "${N1_COLOR}VM is online. Please stop them: ${N2_COLOR}${old}${N1_COLOR} or add ${N2_COLOR}restart=1${N1_COLOR} to auto stop and start VM during rename${N0_COLOR}"
		fi
		;;
esac

[ ! -d "${SRC}" ] && log_err 1 "${N1_COLOR}No VM data: ${N2_COLOR}${SRC}${N0_COLOR}"

if [ -d "${DST}" ]; then
	${RMDIR_CMD} ${DST} >/dev/null 2>&1
	[ -d "${DST}" ] && log_err 1 "${N1_COLOR}VM data already exist and it not empty: ${N2_COLOR}${DST}${N0_COLOR}"
fi

[ ! -d "${JAILDIR}" ] && ${MKDIR_CMD} -p ${JAILDIR}
[ ! -d "${jailfstab}"  ] && ${MKDIR_CMD} -p ${jailfstabdir}

printf "${N1_COLOR}Rename${N0_COLOR}"

if [ -f "${JAILFSTABORIG}" ]; then
	${MV_CMD} ${JAILFSTABORIG} ${JAILFSTAB}
	${SED_CMD} -i${SED_DELIMER}'' s:/${old}:/${new}:g ${JAILFSTAB}
fi

[ -f "${JAILLOCALFSTABORIG}" ] && ${MV_CMD} ${JAILLOCALFSTABORIG} ${JAILLOCALFSTAB} && dot "mv fstablocal"

cbsdsqlrw local UPDATE jails SET jname=\"${new}\",path=\"${jaildir}/${new}\",mount_fstab=\"${jailfstabdir}/${jailfstabpref}${new}\",data=\"${jaildatadir}/${new}-${jaildatapref}\",rcconf=\"${jailsysdir}/${jname}/rc.conf_${new}\" WHERE jname=\"${old}\"
cbsdsqlrw local UPDATE rctl SET jname=\"${new}\" WHERE jname=\"${old}\" 2>/dev/null
cbsdsqlrw local UPDATE bhyve SET jname=\"${new}\" WHERE jname=\"${old}\" 2>/dev/null
cbsdsqlrw ${jailsysdir}/${jname}/local.sqlite UPDATE bhyvedsk SET jname=\"${new}\" WHERE jname=\"${old}\" 2>/dev/null
cbsdsqlrw ${jailsysdir}/${jname}/local.sqlite UPDATE bhyvenic SET jname=\"${new}\" WHERE jname=\"${old}\" 2>/dev/null

# rename rc.conf values if exist
if [ -r ${jailsysdir}/${jname}/rc.conf_${old} ]; then
	is_cloud=0
	eval $( ${GREP_CMD} -E "^is_cloud" ${jailsysdir}/${jname}/rc.conf_${old} )
	[ -z "${is_cloud}" ] && is_cloud=0
	/usr/local/cbsd/misc/cbsdsysrc -qf ${jailsysdir}/${jname}/rc.conf_${old} \
		path=${jaildir}/${new} \
		jname="${new}" \
		rcconf=${jailsysdir}/${new}/rc.conf_${new} \
		data=\"${jaildatadir}/${new}-${jaildatapref} > /dev/null 2>&1

	if [ ${is_cloud} -eq 0 ]; then
		/usr/local/cbsd/misc/cbsdsysrc -qf ${jailsysdir}/${jname}/rc.conf_${old} \
			ci_jname="${new}" > /dev/null 2>&1
	fi
	${MV_CMD} ${jailsysdir}/${jname}/rc.conf_${old} ${jailsysdir}/${jname}/rc.conf_${new}
fi

#
# rename ppt
if [ "${new}" = "ppt" ]; then
	# sqlite3 use name of columt to insert data?? when new="ppt", we got 'ppt' value from column as jname, bug?
	cbsdsqlrw local UPDATE bhyveppt SET jname=\'ppt\' WHERE jname=\"${old}\" 2>/dev/null
else
	cbsdsqlrw local UPDATE bhyveppt SET jname=\"${new}\" WHERE jname=\"${old}\" 2>/dev/null
fi

if [ -n "${ip4_addr_new}" ]; then
	cbsdsqlrw local UPDATE jails SET ip4_addr=\"${ip4_addr_new}\" WHERE jname=\"${new}\"
fi

if [ -n "${host_hostname_new}" ]; then
	cbsdsqlrw local UPDATE jails SET host_hostname=\"${host_hostname_new}\" WHERE jname=\"${new}\"
fi

jname=${new}

# Update in media table: vhd and ISO
# Update records only for vhd + (/jails-data/|/vm/) location, don't touch/manage disks outside jails-data/vm dir or /dev/RAW devices
for i in $( ${FIND_CMD} ${SRC}/ -mindepth 1 -maxdepth 1 -name dsk\* -type l -or -type f ); do
	dskname=$( ${BASENAME_CMD} ${i} )
	idx=$( cbsdsqlro storage_media "SELECT idx FROM media WHERE path=\"${workdir}/jails-data/${old}-data/${dskname}\" OR path=\"${workdir}/vm/${old}/${dskname}\" AND jname=\"${old}\"" 2>/dev/null )
	[ -z "${idx}" ] && continue
	_old_path=$( cbsdsqlro storage_media "SELECT path FROM media WHERE idx=${idx}" 2>/dev/null )
	_new_path=$( echo ${_old_path} | ${SED_CMD} -e s:${old}-data:${new}-data:g -e s:vm/${old}/:vm/${new}/:g )		# change datadir name, old and new style
	cbsdlogger NOTICE ${CBSD_APP}: update storage: path:${_new_path},jname=${new} for idx:${idx}
	cbsdsqlrw storage_media UPDATE media SET path=\"${_new_path}\",jname=\"${new}\" WHERE idx=${idx}
done
# update ISO
cbsdsqlrw storage_media UPDATE media SET jname=\"${new}\" WHERE jname=\"${old}\" AND type=\"iso\"

case ${zfsfeat} in
	1)
		. ${subrdir}/zfs.subr
		[ ! -d ${workdir}/vm ] && ${MKDIR_CMD} -p ${workdir}/vm
		DATA=$( ${ZFS_CMD} get -Ho value name ${jaildatadir} )
		_old_mpt=$( ${ZFS_CMD} get -Ho value mountpoint ${DATA}/${old} 2>&1 )
		_ret=$?
		if [ ${_ret} -eq 0 -a -n "${_old_mpt}" ]; then

			# loop to check for symlink and zvol
			for i in $( ${FIND_CMD} ${SRC}/ -mindepth 1 -maxdepth 1 -type l ); do
				if is_getzvol ${i}; then
					# back compatible for CBSD <= 12.1.0
					_new_zvol_dsk_name=$( echo ${is_zvol} | ${SED_CMD} s:bcbsd-${old}-dsk:bcbsd-${new}-dsk:g )
					# replace "jails/OLDVM/dskX.vhd" path
					_new_zvol_dsk_name=$( echo ${_new_zvol_dsk_name} | ${SED_CMD} s:/${old}/:/${new}/:g )		# valid name for symlink
																	# (after renaming parent vol)
					if [ -n "${_new_zvol_dsk_name}" -a "${_new_zvol_dsk_name}" != "${is_zvol}" ]; then
						_ret=$( echo "${is_zvol}" | ${GREP_CMD} "bcbsd-" )
						if [ $? -eq 0 ]; then
							# back compatible for CBSD <= 12.1.0
							# rename from bcbsd- to new form
							cbsdlogger NOTICE ${CBSD_APP}: zfs rename ${is_zvol} ${_new_zvol_dsk_name}
							${ZFS_CMD} rename ${is_zvol} ${_new_zvol_dsk_name} > /dev/null 2>&1
							_ret=$?
							if [ ${_ret} -ne 0 ]; then
								${ECHO} "${LRED}Error: ${MAGENTA}${ZFS_CMD} rename ${is_zvol} ${_new_zvol_dsk_name} failed${NORMAL}"
								cbsdlogger NOTICE ${CBSD_APP}: zfs rename ${is_zvol} ${_new_zvol_dsk_name} failed
							fi
						fi
						# re-link
						${LN_CMD} -sf /dev/zvol/${_new_zvol_dsk_name} ${i}
					fi
				fi
			done

			cbsdlogger NOTICE ${CBSD_APP}: ${ZFS_CMD} unmount -f ${_old_mpt}
			${ZFS_CMD} unmount -f ${_old_mpt} > /dev/null 2>&1
			cbsdlogger NOTICE ${CBSD_APP}: ${ZFS_CMD} rename ${DATA}/${old} ${DATA}/${new}
			${ZFS_CMD} rename ${DATA}/${old} ${DATA}/${new}
			cbsdlogger NOTICE ${CBSD_APP}: ${ZFS_CMD} set mountpoint=${workdir}/vm/${new} ${DATA}/${new}
			${ZFS_CMD} set mountpoint=${workdir}/vm/${new} ${DATA}/${new}
			${ZFS_CMD} mount ${DATA}/${new}
		fi
		if [ -h ${SRC} ]; then
			${RM_CMD} ${SRC}
			${LN_CMD} -sf ${workdir}/vm/${new} ${DST}
		else
			# todo: mixed content? symlink + md?
			mvdata ${SRC} ${DST}
			dot "mvdata"
		fi

#		# additional loop to check for symlink and zvol
#		for i in $( ${FIND_CMD} ${DST}/ -mindepth 1 -maxdepth 1 -type l ); do
#			if is_getzvol ${i}; then
#				old_zvol="${is_zvol}"
#				new_zvol=$( echo ${is_zvol} | ${SED_CMD} s:${old}:${new}:g )
#				${ZFS_CMD} get -Ho value name ${old_zvol} > /dev/null 2>&1
#				_ret=$?
#				[ ${_ret} -eq 0 ] && ${ZFS_CMD} rename ${old_zvol} ${new_zvol}
#				${LN_CMD} -sf /dev/zvol/${new_zvol} ${i}
#			fi
#		done
		;;
	*)
		mvdata ${SRC} ${DST}
		dot "mvdata"
		;;
esac

#rename zfs fs source
#case ${zfsfeat} in
#	1)
#		zfsmnt ${DST}
#		_err=$?
#		if [ ${_err} -eq 2 -o ${_err} -eq 1 ]; then
#			OLDPOOL=${ZPOOL}
#			DATA=$( ${ZFS_CMD} get -Ho value name ${jaildatadir} )
#			NEWPOOL="${DATA}/${new}"
#			if [ ${_err} -eq 1 ]; then
#				# we need unmount this first
#				${ZFS_CMD} unmount -f ${DST}
#				sleep 5  #Hack - sometimes we got "cannot rename: dataset is busy"
#				${ZFS_CMD} unmount -f ${DST} >/dev/null 2>&1
#			fi
#			cbsdlogger NOTICE ${CBSD_APP}: rename zfs from ${OLDPOOL} to ${NEWPOOL}
#			${ZFS_CMD} rename ${OLDPOOL} ${NEWPOOL}
#			if [ $? -eq 0 ]; then
#				${ZFS_CMD} mount ${NEWPOOL}
#				[ -d ${SRC} ] && ${RMDIR_CMD} ${SRC}
#			fi
#		fi
#
#		# additional loop to check for symlink and zvol
#		for i in $( ${FIND_CMD} ${DST}/ -mindepth 1 -maxdepth 1 -type l ); do
#			if is_getzvol ${i}; then
#				old_zvol="${is_zvol}"
#				new_zvol=$( echo ${is_zvol} |${SED_CMD} s:${old}:${new}:g )
#				${ZFS_CMD} rename ${old_zvol} ${new_zvol}
#				/bin/ln -sf /dev/zvol/${new_zvol} ${i}
#			fi
#		done
#	;;
#esac


case ${zfsfeat} in
	1)
		if [ -h "${SYSDIROLD}" ]; then
			${RM_CMD} -f ${SYSDIROLD}
			${LN_CMD} -sf ${workdir}/vm/${new} ${SYSDIRNEW}
		elif [ -d "${SYSDIROLD}" ]; then
			${MV_CMD} ${SYSDIROLD} ${SYSDIRNEW}
		fi
		;;
	*)
		if [ -d "${SYSDIROLD}" ]; then
			${MV_CMD} ${SYSDIROLD} ${SYSDIRNEW}
		fi
esac

[ -r ${jailsysdir}/${jname}/local.sqlite ] && cbsdsqlrw ${jailsysdir}/${jname}/local.sqlite "UPDATE bhyvedsk set jname=\"${new}\""
[ -r ${jailsysdir}/${jname}/local.sqlite ] && cbsdsqlrw ${jailsysdir}/${jname}/local.sqlite "UPDATE bhyvenic set jname=\"${new}\""

if [ ${restart} -eq 1 -a ${oldjid} -ne 0 ]; then
	cbsdlogger NOTICE ${CBSD_APP}: vm ${jname} will be started due to restart flags on
	bstart jname=${new}
	data_status=1
else
	data_status=0
fi

if [ "${mod_cbsd_queue_enabled}" = "YES" -a -z "${MOD_CBSD_QUEUE_DISABLED}" ]; then
	[ -n "${cbsd_jail_queue_name}" ] && ${cbsd_queue_backend} cbsd_queue_name=${cbsd_bhyve_queue_name} id="${new}" cmd=brename status=2 data_status=${data_status}
fi

[ -d "${SRC}" ] && ${RMDIR_CMD} ${SRC}

end_time=$( ${DATE_CMD} +%s )
diff_time=$(( end_time - st_time ))
diff_time=$( displaytime ${diff_time} )
${ECHO} " ${N2_COLOR}ok${N0_COLOR}"

${ECHO} "${N1_COLOR}${CBSD_APP} done ${N2_COLOR}in ${diff_time}${N0_COLOR}"
cbsdlogger NOTICE ${CBSD_APP}: vm ${old} has been renamed to ${new} in ${diff_time}

exit 0
