#!/bin/bash
#
# clone is run when you instantiate non persistent disk on a system datastore.
#
# Format:
#   clone SOURCE host:remote_system_ds/disk.i vmid source_ds_id
#
# Example:
#   clone iqn.opennebula:11 slcv2-vmc7:/var/lib/one/datastore/192/171/disk.0 171 191
#   - remote_system_ds is the path for the system datastore in the host
#
#
#
# Parameters:
#
# SRC - source image.
#   Will be ISCSI IQN (iqn.2019-07.net.efscloud.slc:11) in case of datastore/efs_zvol_iscsi driver
#   which consists of:
#     - ISCSI_BASE_IQN attribute of the image
#                      (which is inherited from datastore where the image is placed)
#     - IMAGE_ID after ':'
SRC=$1

# DST - destination path
#   HOST:/opennebula/path/DST_DS_ID/VM_ID/disk.DISK_ID
DST=$2

# VM_ID - pretty self explained
VM_ID=$3

# SRC_DS_ID - datasetore id where source image is placed
SRC_DS_ID=$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 $TMCOMMON
source ${DRIVER_PATH}/../../datastore/libfs.sh
source ${DRIVER_PATH}/../../efs_utils

# define SSH env variable to use agent everywhere
SSH="ssh -A "


efs_log_var "SRC"
efs_log_var "DST"
efs_log_var "VM_ID"
efs_log_var "SRC_DS_ID"

#-------------------------------------------------------------------------------
# Compute the destination image name
#-------------------------------------------------------------------------------

# arg_path function is in tm_common.sh module which is included above
# It returns part of an input string placed after ':'
# So it will be image id in our case (ISCSI_BASE_IQN:IMG_ID)
SRC_PATH=`arg_path $SRC`
IMG_ID="${SRC_PATH}"

DISK_ID=$(echo $DST|awk -F. '{print $NF}')
# get destination datased id
DST_DS_ID=$(echo $DST | sed s#//*#/#g | awk -F/ '{print $(NF-2)}')

#-------------------------------------------------------------------------------
# Get Image size information to resize it after clone if necessary
#-------------------------------------------------------------------------------

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 $VM_ID| $XPATH  \
                    /VM/TEMPLATE/CONTEXT/AXC_VMTYPE \
                    /VM/TEMPLATE/DISK[DISK_ID=$DISK_ID]/SIZE \
                    /VM/TEMPLATE/DISK[DISK_ID=$DISK_ID]/ORIGINAL_SIZE \
                    /VM/NAME )

AXC_VMTYPE="${XPATH_ELEMENTS[j++]:-efs}"
SIZE="${XPATH_ELEMENTS[j++]}"
ORIGINAL_SIZE="${XPATH_ELEMENTS[j++]}"
VMNAME="${XPATH_ELEMENTS[j++]}"

case "$AXC_VMTYPE" in
  aa|acr|anc|cf|efs|rb|sp|ve)
    ;;
  *)
    AXC_VMTYPE="efs"
    ;;
esac

#-------------------------------------------------------------------------------
# Get DST Datastore information
#-------------------------------------------------------------------------------

unset i j XPATH_ELEMENTS

while IFS= read -r -d '' element; do
        XPATH_ELEMENTS[i++]="$element"
done < <(onedatastore show -x $DST_DS_ID | $XPATH \
                    /DATASTORE/TEMPLATE/BRIDGE_LIST\
                    /DATASTORE/TEMPLATE/POOL_NAME\
                    /DATASTORE/TEMPLATE/ISCSI_HOST\
                    /DATASTORE/TEMPLATE/ISCSI_LUN)

DST_BRIDGE_LIST="${XPATH_ELEMENTS[j++]}"
DST_POOL_NAME="${XPATH_ELEMENTS[j++]}"
DST_ISCSI_HOST="${XPATH_ELEMENTS[j++]}"
ISCSI_LUN="${XPATH_ELEMENTS[j++]}"

BRIDGE_LIST=${DST_BRIDGE_LIST}
#-------------------------------------------------------------------------------
# Get SRC Datastore information
#-------------------------------------------------------------------------------

unset i j XPATH_ELEMENTS

while IFS= read -r -d '' element; do
        XPATH_ELEMENTS[i++]="$element"
done < <(onedatastore show -x $SRC_DS_ID | $XPATH \
                    /DATASTORE/TEMPLATE/BRIDGE_LIST\
                    /DATASTORE/TEMPLATE/POOL_NAME)

SRC_BRIDGE_LIST="${XPATH_ELEMENTS[j++]}"
SRC_POOL_NAME="${XPATH_ELEMENTS[j++]}"

#-----------
efs_log_var "SRC_PATH"
efs_log_var "IMG_ID"
efs_log_var "SIZE"
efs_log_var "ORIGINAL_SIZE"

# get_destination_host returns a host out of the BRIDGE_LIST
# So we need to change BRIDGE_LIST global variable for source and destination
DST_HOST=`get_destination_host $DST_DS_ID`
BRIDGE_LIST=${SRC_BRIDGE_LIST}
SRC_HOST=`get_destination_host $SRC_DS_ID`
BRIDGE_LIST=${DST_BRIDGE_LIST}

efs_log_var "DST_HOST"
efs_log_var "SRC_HOST"
efs_log_var "ISCSI_LUN"
efs_log_var "DST_POOL_NAME"
efs_log_var "DST_DS_ID"
efs_log_var "DST_BRIDGE_LIST"

# ISCSI config for Linux storages. It's not relevant at the moment
TGTCONF=$(cat <<EOF
<target "${SRC}-${VM_ID}-${DISK_ID}">
    backing-store /dev/zvol/${DST_POOL_NAME}/one/${AXC_VMTYPE}/system/${VM_ID}/${DISK_ID}
</target>
EOF
)

CHECK_SRC_VERSION="sudo zfs get -H -o value one:current $SRC_POOL_NAME/one/${IMG_ID}"
CHECK_DST_VERSION="sudo zfs get -H -o value one:current $DST_POOL_NAME/one/${IMG_ID}"

# Opennebula populates disk config by IMAGE (or source DATASTORE) content
# (Look at the INHERIT_(IMAGE|DATASTORE)_* options in oned.conf)
# We need to change at least ISCSI_HOST to destination in case image and VM disk are placed
# on different storages, because vmm driver takes ISCSI_HOST of the disk config to generate Libvirt XML.
# The only way to change it is to write directly into database.
if [ "x${SRC_HOST}" != "x${DST_HOST}" ]
then
    onedb change-body vm --id ${VM_ID} "/VM/TEMPLATE/DISK[DISK_ID=$DISK_ID]/ISCSI_HOST" "${DST_ISCSI_HOST}"
fi

# Full script which will be run on DST storage server
CLONE_CMD=$(cat <<EOF

    sudo zfs create -p ${DST_POOL_NAME}/one
    sudo zfs set canmount=noauto ${DST_POOL_NAME}/one
    if [ "x${SRC_HOST}" != "x${DST_HOST}" ]
    then
      SRC_VERSION=\$($SSH $SRC_HOST "$CHECK_SRC_VERSION" 2>/dev/null)
      DST_VERSION=\$($CHECK_DST_VERSION 2>/dev/null)
      RC=\$?
      echo "SRC_VERSION: \$SRC_VERSION" >> ~/efs.log
      echo "DST_VERSION: \$DST_VERSION" >> ~/efs.log
      if [ ! \$RC -eq 0 ]
      then
          # dataset [$DST_POOL_NAME/one/${IMG_ID}] doesn't exists. Full copy of $SRC_HOST:[$SRC_POOL_NAME/one/${IMG_ID}]:\$SRC_VERSION
          # full copy
          $SSH $SRC_HOST "sudo zfs send ${SRC_POOL_NAME}/one/${IMG_ID}@\${SRC_VERSION}" | sudo zfs recv ${DST_POOL_NAME}/one/${IMG_ID}
          sudo zfs set volmode=dev ${DST_POOL_NAME}/one/${IMG_ID}
          sudo zfs set one:current=\${SRC_VERSION} ${DST_POOL_NAME}/one/${IMG_ID}
      elif [ "x\${SRC_VERSION}" != "x\${DST_VERSION}" ]
      then
          # dataset [$DST_POOL_NAME/one/${IMG_ID}] exists, but versions are different (\$SRC_VERSION!=\$DST_VERSION). Incremental copy
          # incremental copy
          $SSH $SRC_HOST "sudo zfs send -I ${SRC_POOL_NAME}/one/${IMG_ID}@\${DST_VERSION} ${SRC_POOL_NAME}/one/${IMG_ID}@\${SRC_VERSION}" | sudo zfs recv -F ${DST_POOL_NAME}/one/${IMG_ID}
          sudo zfs set volmode=dev ${DST_POOL_NAME}/one/${IMG_ID}
          sudo zfs set one:current=\${SRC_VERSION} ${DST_POOL_NAME}/one/${IMG_ID}
      fi
      # check version and copy ssh
    elif [ "x${SRC_POOL_NAME}" != "x${DST_POOL_NAME}" ]
      then
      # check version and sopy
      SRC_VERSION=\$($CHECK_SRC_VERSION 2>/dev/null)
      DST_VERSION=\$($CHECK_DST_VERSION 2>/dev/null)
      if [ ! \$? -eq 0 ]
      then
          # dataset [$DST_POOL_NAME/one/${IMG_ID}] doesn't exists. Full copy of $SRC_HOST:[$SRC_POOL_NAME/one/${IMG_ID}]:\$SRC_VERSION
          # full copy
          sudo zfs send ${SRC_POOL_NAME}/one/${IMG_ID}@\${SRC_VERSION} | sudo zfs recv ${DST_POOL_NAME}/one/${IMG_ID}
          sudo zfs set volmode=dev ${DST_POOL_NAME}/one/${IMG_ID}
          sudo zfs set one:current=\${SRC_VERSION} ${DST_POOL_NAME}/one/${IMG_ID}
      elif [ "x\${SRC_VERSION}" != "x\${DST_VERSION}" ]
      then
          # dataset [$DST_POOL_NAME/one/${IMG_ID}] exists, but versions are different (\$SRC_VERSION!=\$DST_VERSION). Incremental copy
          # incremental copy
          sudo zfs send -I ${SRC_POOL_NAME}/one/${IMG_ID}@\${DST_VERSION} ${SRC_POOL_NAME}/one/${IMG_ID}@\${SRC_VERSION} | sudo zfs recv -F ${DST_POOL_NAME}/one/${IMG_ID}
          sudo zfs set volmode=dev ${DST_POOL_NAME}/one/${IMG_ID}
          sudo zfs set one:current=\${SRC_VERSION} ${DST_POOL_NAME}/one/${IMG_ID}
      fi
      # check version and copy ssh
    fi
    echo "Run clone part" >> ~/efs.log
    sleep 20
    set -e -o pipefail
    V=\$(sudo zfs get -H -o value one:current ${DST_POOL_NAME}/one/${IMG_ID})
    echo "V: \$V" >> ~/efs.log
    sudo zfs clone -p ${DST_POOL_NAME}/one/${IMG_ID}@\${V} ${DST_POOL_NAME}/one/${AXC_VMTYPE}/system/${VM_ID}/${DISK_ID}
    sudo zfs set volmode=dev ${DST_POOL_NAME}/one/${AXC_VMTYPE}/system/${VM_ID}/${DISK_ID}
    if [ -n "$ORIGINAL_SIZE" -a "$SIZE" -gt "$ORIGINAL_SIZE" ]; then
        # $RBD resize $RBD_DST --size $SIZE
        sleep 15
        sudo zfs set volsize=${SIZE}M ${DST_POOL_NAME}/one/${AXC_VMTYPE}/system/${VM_ID}/${DISK_ID}
    fi
UNAME=\$(uname)

case "\${UNAME}" in
    "FreeBSD")
       sudo ctladd ${SRC}-${VM_ID}-${DISK_ID} ${ISCSI_LUN} /dev/zvol/${DST_POOL_NAME}/one/${AXC_VMTYPE}/system/${VM_ID}/${DISK_ID} "${VMNAME}.${DISK_ID}"
       sleep 30
       sudo service ctld reload
       ;;
    "Linux")
       sudo /usr/bin/echo '$TGTCONF' > /etc/tgt/conf.d/${VM_ID}.conf
       sudo /usr/sbin/tgt-admin --update ALL -c /etc/tgt/targets.conf
       ;;
    *)
       exit 1
       ;;
esac
sleep 20
EOF
)
efs_log_var "CLONE_CMD"

#echo "${TS} [TM:clone] - CLONE_CMD ${CLONE_CMD}" >>  /var/log/one/efs.log
# CLONE_CMD worker incorrectly when it was run directly by ssh
# May be some length limit
# So generate tmp file with this script, copy to the DST_HOST and run
TFILE=$(mktemp /tmp/foo.XXXXXXXXX)
echo "${CLONE_CMD}" > ${TFILE}
scp ${TFILE} $DST_HOST:${TFILE}
ssh_exec_and_log $DST_HOST "bash ${TFILE}" \
                 "Error cloning $SRC_PATH to ${DST_POOL_NAME}/one/${AXC_VMTYPE}/system/${VM_ID}/${DISK_ID} in $DST_HOST"

exit 0
