#!/bin/bash

# -------------------------------------------------------------------------- #
# Copyright 2002-2021, OpenNebula Project, OpenNebula Systems                #
#                                                                            #
# Licensed under the Apache License, Version 2.0 (the "License"); you may    #
# not use this file except in compliance with the License. You may obtain    #
# a copy of the License at                                                   #
#                                                                            #
# http://www.apache.org/licenses/LICENSE-2.0                                 #
#                                                                            #
# Unless required by applicable law or agreed to in writing, software        #
# distributed under the License is distributed on an "AS IS" BASIS,          #
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   #
# See the License for the specific language governing permissions and        #
# limitations under the License.                                             #
#--------------------------------------------------------------------------- #

# MV <hostA:system_ds/disk.i|hostB:system_ds/disk.i> vmid dsid
#    <hostA:system_ds/|hostB:system_ds/>
#   - hostX is the target host to deploy the VM
#   - system_ds is the path for the system datastore in the host
#   - vmid is the id of the VM
#   - dsid is the target datastore (0 is the system datastore)

SRC=$1
DST=$2

VMID=$3
DSID=$4

#--------------------------------------------------------------------------------

if [ -z "${ONE_LOCATION}" ]; then
    TMCOMMON=/var/lib/one/remotes/tm/tm_common.sh
    LIB_LOCATION=/usr/lib/one
else
    TMCOMMON=$ONE_LOCATION/var/remotes/tm/tm_common.sh
    LIB_LOCATION=$ONE_LOCATION/lib
fi

DRIVER_PATH=$(dirname $0)

#source ${DRIVER_PATH}/../../etc/tm/fs_lvm/fs_lvm.conf

. $TMCOMMON

#--------------------------------------------------------------------------------

SRC=`fix_dir_slashes $SRC`
DST=`fix_dir_slashes $DST`

SRC_PATH=`arg_path $SRC`
DST_PATH=`arg_path $DST`

SRC_HOST=`arg_host $SRC`
DST_HOST=`arg_host $DST`

SRC_DIR=`dirname $SRC_PATH`
DST_DIR=`dirname $DST_PATH`
DS_DIR=`dirname $DST_DIR`

# Activate the disk in the target host
if [ `is_disk $SRC_PATH` -eq 1 ]; then
    #---------------------------------------------------------------------------
    # Get Image information
    #---------------------------------------------------------------------------

    SRC_DS_SYS_ID=$(echo $SRC_DIR | $AWK -F '/' '{print $(NF-1)}')
    DST_DS_SYS_ID=$(echo $DST_DIR | $AWK -F '/' '{print $(NF-1)}')



    DISK_ID=${SRC_PATH##*.}

    XPATH="${DRIVER_PATH}/../../datastore/xpath.rb --stdin"

    unset i j XPATH_ELEMENTS

    while IFS= read -r -d '' element; do
        XPATH_ELEMENTS[i++]="$element"
    done < <(onevm show -x $VMID | $XPATH  \
                        /VM/LCM_STATE )

    LCM_STATE="${XPATH_ELEMENTS[j++]}"

    #rbt
    LV_NAME="lv-one-vm-${VMID}-${DISK_ID}"
    OUTPUT="$(${SUDO} ${LVSCAN})"
    if [[ $OUTPUT != *$LV_NAME* ]]; then
        LV_NAME="lv-one-${VMID}-${DISK_ID}"
    fi

    SRC_VG_NAME="vg-one-${SRC_DS_SYS_ID}"
    SRC_DEV="/dev/${SRC_VG_NAME}/${LV_NAME}"
    DST_VG_NAME="vg-one-${DST_DS_SYS_ID}"
    DST_DEV="/dev/${DST_VG_NAME}/${LV_NAME}"
    SRC_MAP="vg--one--${SRC_DS_SYS_ID}-lv--one--vm--${VMID}--${DISK_ID}"
    DST_MAP="vg--one--${DST_DS_SYS_ID}-lv--one--vm--${VMID}--${DISK_ID}"
    SRC_MAP_DEV="/dev/mapper/${SRC_MAP}"
    DST_MAP_DEV="/dev/mapper/${DST_MAP}"

    #rbt: BREST-2395
    check_host_alive()
    {
        param="-q -o BatchMode=yes -o StrictHostKeyChecking=no -o ConnectTimeout=5"
        $SSH $param $1 'exit 0' 2>/dev/null 1>&2;
        return $?;
    }

    CHECK_MAP=$(cat <<EOF
	set -ex -o pipefail

    #rbt: chek /dev/mapper
	if [ -e "$DST_MAP_DEV" ]; then
    	    ${SYNC}
    	    ${SUDO} ${DMSETUP} remove "${DST_DEV}"
	fi
EOF
)

    ssh_exec_and_log "$DST_HOST" "$CHECK_MAP" "Error remove mapper"


    #rbt: create dirs
    CMD_PRE=$(cat <<EOF
    set -ex -o pipefail

    #rbt: create dirs
    export PATH=$PATH:/sbin
    mkdir -p /var/lib/one/datastores/$DSID
    mkdir -p $DS_DIR
    pdp-one "$DS_DIR"
    mkdir -p $DST_DIR
    pdp-one "$DST_DIR"
    #service opennebula-common restart
EOF
)
    ssh_exec_and_log "$DST_HOST" "$CMD_PRE" "Error create virtual machine home dir"

    # Return if the target path is the same as the source path. No need to move
    # anything. This is *not* a system ds migration.
    if [ "$SRC_PATH" == "$DST_PATH" ]; then
        #rbt: BREST-2395
        check_host_alive "$SRC_HOST"
        if [ $? -eq 0 ]; then
            #rbt: copy VM $HOME
            CMD_SYNC=$(cat <<EOF
                set -ex -o pipefail

		($TAR -C $DST_DIR -cSf - . | $SSH "$DST_HOST" "$TAR -xSf - -C $DST_DIR ;  export PATH=$PATH:/sbin ; pdp-one $DST_DIR") || :
EOF
)
        fi
    fi

    # skip deactivate for `onevm resume` (after stop or undeploy)
    # 9(49) = PROLOG_RESUME(+FAILURE)
    # 31(50) = PROLOG_UNDEPLOY(+FAILURE)
    if ! [[ "$LCM_STATE" =~ ^(9|31|49|50)$ ]]; then
        #rbt: BREST-2395
        check_host_alive "$SRC_HOST"
        if [ $? -eq 0 ]; then
            # deactivate
            CMD=$(cat <<EOF
                set -ex -o pipefail
                if [ -b "${SRC_DEV}" ]; then
                    ${SYNC}
                    ${SUDO} ${LVSCAN}
                    ${SUDO} ${LVCHANGE} -an "${SRC_DEV}"
                fi
                rm -f "${SRC_DIR}/.host" || :
EOF
)
            LOCK="tm-fs_lvm-${SRC_DS_SYS_ID}.lock"

            exclusive "${LOCK}" 120 ssh_exec_and_log "${SRC_HOST}" "${CMD}" \
                "Error deactivating disk ${SRC_PATH}"
        fi
    fi

    # for `onevm stop` or `onevm undeploy` nothing to do
    # 10(41) = EPILOG_STOP(+FAILURE)
    # 30(42) = EPILOG_UNDEPLOY(+FAILURE)
    if [[ "$LCM_STATE" =~ ^(10|30|41|42)$ ]]; then
        exit 0
    fi

    # copy volume between datastores
    if [ "${SRC_PATH}" != "${DST_PATH}" ]; then
        # create new volume
        CREATE_CMD=$(cat <<EOF
            set -e -o pipefail
            ${SYNC}
            ${SUDO} ${LVSCAN}
            SIZE=\$(${SUDO} ${LVS} --noheadings --units B -o lv_size "${SRC_DEV}" | tr -d '[:blank:]')
            ${SUDO} ${LVCREATE} --wipesignatures n -L"\${SIZE}" -n "${LV_NAME}" "${DST_VG_NAME}"
EOF
)

	if [ "${SRC_HOST}" == "${DST_HOST}" ]; then

        LOCK="tm-fs_lvm-${DST_DS_SYS_ID}.lock"
        exclusive "${LOCK}" 120 ssh_exec_and_log "${SRC_HOST}" "${CREATE_CMD}" \
                "Error creating LV named ${LV_NAME}"

        #rbt
        DD_CMD=$(cat <<EOF
            set -e -o pipefail
            ${SYNC}
            ${SUDO} ${LVSCAN}
            ${SUDO} ${LVCHANGE} -ay "${SRC_DEV}"
            ${DD} if=${SRC_DEV} of=${DST_DEV} bs=${DD_BLOCK_SIZE:-64k}
            set +e
            ln -sf "${DST_DEV}" "${DST_PATH}"
            set -e
EOF
)

        # copy volume data
        ssh_exec_and_log "$SRC_HOST" "${DD_CMD}" \
            "Error copying ${SRC} to ${DST}"

	else

	LOCK="tm-fs_lvm-${DST_DS_SYS_ID}.lock"
        exclusive "${LOCK}" 120 ssh_exec_and_log "${DST_HOST}" "${CREATE_CMD}" \
        	"Error creating LV named ${LV_NAME}"

        DD_CMD=$(cat <<EOF
            set -e -o pipefail
            ${SYNC}
            ${SUDO} ${LVSCAN}
            ${SUDO} ${LVCHANGE} -ay "${SRC_DEV}"
            ${DD} if=${SRC_DEV} bs=${DD_BLOCK_SIZE:-64k} | $SSH $DST_HOST '${DD} of=${DST_DEV} bs=${DD_BLOCK_SIZE:-64k}'
            set +e
            ln -sf "${DST_DEV}" "${DST_PATH}"
            set -e
EOF
)
        MAP_CMD=$( cat <<EOF
            set -e -o pipefail
            if [ -e "$SRC_MAP_DEV" ]; then
            ${SYNC}
            ${SUDO} ${DMSETUP} remove "${SRC_DEV}"
            fi
EOF
)

            # copy volume data
            ssh_exec_and_log "$DST_HOST" "${DD_CMD}" \
                "Error copying ${SRC} to ${DST}"

            ssh_exec_and_log "$DST_HOST" "${MAP_CMD}" \
                "Error delete mapper"
        fi


        # delete old volume and update device symlinks
        DELETE_CMD=$(cat <<EOF
            set -e -o pipefail
            ${SUDO} ${LVREMOVE} -f ${SRC_DEV}
            ${SYNC}

            rm -f "${SRC_PATH}"
            ln -s "${DST_DEV}" "${SRC_PATH}"
EOF
)

        LOCK="tm-fs_lvm-${SRC_DS_SYS_ID}.lock"
        exclusive "${LOCK}" 120 ssh_exec_and_log "${SRC_HOST}" "${DELETE_CMD}" \
            "Error deleting old LV ${SRC_DEV}"
    fi

    # Activate the disk in the target host
    if [ `is_disk $DST_PATH` -eq 1 ]; then
        CMD=$(cat <<EOF
            set -ex -o pipefail
            if [ -L "$DST_PATH" ]; then
                DEVICE=\$(readlink "$DST_PATH")
                $SUDO $SYNC
                $SUDO $LVSCAN
                $SUDO $LVCHANGE -ay \$DEVICE
            fi
EOF
)
        if [ "$SRC_HOST" != "$DST_HOST" ] && [ "$SRC_PATH" == "$DST_PATH" ]; then
            #rbt: BREST-2395
            check_host_alive "$SRC_HOST"
            if [ $? -eq 0 ]; then
                ssh_exec_and_log "$SRC_HOST" "$CMD_SYNC" \
                    "Error sync virtual machine home"
            else
                CMD_LN=$(cat <<EOF
                        set -ex -o pipefail
                        #rbt: BREST-1741
                        ${RM} -f ${DST_PATH}
                        ${LN} -s ${DST_DEV} ${DST_PATH}
EOF
)
                ssh_exec_and_log "$DST_HOST" "$CMD_LN" \
                    "Error recovery virtual machine home dir"
            fi
        fi
    CMD_UNLINK=$(cat <<EOF
        set -ex -o pipefail
        if [ -L "$DST_PATH" ]; then
            DEVICE=\$(readlink "$DST_PATH")
            $SUDO $SYNC
            $SUDO $LVSCAN
            $SUDO $LVCHANGE -an \$DEVICE
            rm -rf $DST_DIR
        fi
EOF
)

CMD_CHECKPOINT=$(cat <<EOF
    scp $SRC_DIR/checkpoint* ${DST_HOST}:$DST_DIR
EOF
)

        ssh_exec_and_log "$DST_HOST" "$CMD" "Error activating disk $DST_PATH"
        if [ "$SRC_HOST" != "$DST_HOST" ] && [ "$SRC_PATH" == "$DST_PATH" ]; then
            if ! [ -e $DST_DIR/checkpoint* ]; then
                #rbt: BREST-2395
                check_host_alive "$SRC_HOST"
                if [ $? -eq 0 ]; then
                    ssh_exec_and_log "$SRC_HOST" "$CMD_UNLINK" "Error remove $DST_PATH on $SRC_HOST host"
                fi
            else
                ssh_exec_and_log "$SRC_HOST" "$CMD_CHECKPOINT" "Error copy checkpoint $SRC_HOST to $DST_HOST host"
            fi
        fi
    fi
    exit 0
fi

# Return if the target path is the same as the source path. No need to move
# anything. This is *not* a system ds migration.
if [ "$SRC_PATH" == "$DST_PATH" ]; then
    exit 0
fi

CMD_MV=$(cat <<EOF
        if ! [ -d $SRC_PATH ]; then
	    ssh $SRC_HOST 'cd $SRC_DIR; tar cfp - *' | ssh $DST_HOST "(cd $DST_DIR; tar xfp -)"
	    ssh $SRC_HOST 'rm -rf $SRC_PATH'
        else
            if [ "$SRC_HOST" == "$DST_HOST" ]; then
		cd $SRC_PATH
		tar cfp - * | ssh $DST_HOST "(cd $DST_PATH; tar xfp - )"
                rm -rf $SRC_PATH
            fi
        fi
EOF
)

ssh_exec_and_log "$DST_HOST" "$CMD_MV" \
    "Error moving VM files to another System DS: $SRC_PATH to $DST_PATH in $DST_HOST"
