#!/bin/bash

# -------------------------------------------------------------------------- #
# Copyright 2002-2019, 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.                                             #
#--------------------------------------------------------------------------- #

# clone fe:SOURCE host:remote_system_ds/disk.i vm_id ds_id
#   - fe is the front-end hostname
#   - SOURCE is the path of the disk image in the form DS_BASE_PATH/disk
#   - host is the target host to deploy the VM
#   - remote_system_ds is the path for the system datastore in the host
#   - vm_id is the id of the VM
#   - ds_id is the source datastore (the images datastore)

SRC=$1
DST=$2
VM_ID=$3
DS_ID=$4

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

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

DRIVER_PATH=$(dirname $0)

source $TMCOMMON
source ${DRIVER_PATH}/../../datastore/libfs.sh
source ${DRIVER_PATH}/../../etc/datastore/linstor_un/linstor_un.conf
source ${DRIVER_PATH}/../../datastore/linstor_un/linstor_utils.sh

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

SRC_DEV=`arg_path $SRC`
SRC_RES="$(echo "$SRC_DEV" | $AWK -F/ '{print $(NF-1)}')"

DST_HOST=`arg_host $DST`
DST_PATH=`arg_path $DST`
DST_DIR=`dirname $DST_PATH`
DS_ID=$(echo $DST_DIR | $AWK -F/ '{print $(NF-1)}')

DISK_ID=$(echo $DST | $AWK -F. '{print $NF}')
DST_RES="one-vm-${VM_ID}-disk-${DISK_ID}"
DST_DEV="/dev/drbd/by-res/${DST_RES}/0"

#-------------------------------------------------------------------------------
# Get Image information
#-------------------------------------------------------------------------------

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

SIZE="${XPATH_ELEMENTS[j++]}"
ORIGINAL_SIZE="${XPATH_ELEMENTS[j++]}"

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

unset i j XPATH_ELEMENTS

while IFS= read -r -d '' element; do
        XPATH_ELEMENTS[i++]="$element"
done < <(onedatastore show -x $DS_ID | $XPATH \
                    /DATASTORE/TEMPLATE/CLONE_MODE \
                    /DATASTORE/TEMPLATE/BRIDGE_LIST \
                    /DATASTORE/TEMPLATE/LS_CONTROLLERS \
                    /DATASTORE/TEMPLATE/LS_CERTFILE \
                    /DATASTORE/TEMPLATE/LS_KEYFILE \
                    /DATASTORE/TEMPLATE/LS_CAFILE \
                    /DATASTORE/TEMPLATE/RESOURCE_GROUP \
                    /DATASTORE/TEMPLATE/NODE_LIST \
                    /DATASTORE/TEMPLATE/LAYER_LIST \
                    /DATASTORE/TEMPLATE/PROVIDERS \
                    /DATASTORE/TEMPLATE/REPLICAS_ON_SAME \
                    /DATASTORE/TEMPLATE/REPLICAS_ON_DIFFERENT \
                    /DATASTORE/TEMPLATE/AUTO_PLACE \
                    /DATASTORE/TEMPLATE/REPLICA_COUNT \
                    /DATASTORE/TEMPLATE/DO_NOT_PLACE_WITH \
                    /DATASTORE/TEMPLATE/DO_NOT_PLACE_WITH_REGEX \
                    /DATASTORE/TEMPLATE/STORAGE_POOL \
                    /DATASTORE/TEMPLATE/DISKLESS_POOL \
                    /DATASTORE/TEMPLATE/PREFER_NODE \
                    /DATASTORE/TEMPLATE/ENCRYPTION)

CLONE_MODE="${XPATH_ELEMENTS[j++]:-copy}"
BRIDGE_LIST="${XPATH_ELEMENTS[j++]}"
LS_CONTROLLERS="${XPATH_ELEMENTS[j++]}"
LS_CERTFILE="${XPATH_ELEMENTS[j++]}"
LS_KEYFILE="${XPATH_ELEMENTS[j++]}"
LS_CAFILE="${XPATH_ELEMENTS[j++]}"
RESOURCE_GROUP="${XPATH_ELEMENTS[j++]}"
NODE_LIST="${XPATH_ELEMENTS[j++]}"
LAYER_LIST="${XPATH_ELEMENTS[j++]}"
PROVIDERS="${XPATH_ELEMENTS[j++]}"
REPLICAS_ON_SAME="${XPATH_ELEMENTS[j++]}"
REPLICAS_ON_DIFFERENT="${XPATH_ELEMENTS[j++]}"
AUTO_PLACE="${XPATH_ELEMENTS[j++]}"
REPLICA_COUNT="${XPATH_ELEMENTS[j++]:-$AUTO_PLACE}"
DO_NOT_PLACE_WITH="${XPATH_ELEMENTS[j++]}"
DO_NOT_PLACE_WITH_REGEX="${XPATH_ELEMENTS[j++]}"
STORAGE_POOL="${XPATH_ELEMENTS[j++]}"
DISKLESS_POOL="${XPATH_ELEMENTS[j++]:-DfltDisklessStorPool}"
PREFER_NODE="${XPATH_ELEMENTS[j++]}"
ENCRYPTION="${XPATH_ELEMENTS[j++]}"

#-------------------------------------------------------------------------------
# Clone the image
#-------------------------------------------------------------------------------

SRC_HOST=`linstor_get_bridge_host "$SRC_RES" 0`
if [ -z "$SRC_HOST" ]; then
    exit -1
fi
if [ -z "$RESOURCE_GROUP" ]; then
    if [ "$CLONE_MODE" = "copy" ] && [ -z "$NODE_LIST" ] && [ -z "$REPLICA_COUNT" ]; then
        error_message "Datastore template missing 'RESOURCE_GROUP', 'NODE_LIST' or 'REPLICA_COUNT' attribute."
        exit -1
    fi
    if [ -z "$STORAGE_POOL" ]; then
        error_message "Datastore template missing 'STORAGE_POOL' attribute."
        exit -1
    fi
fi
if ! [[ "$CLONE_MODE" =~ ^(copy|snapshot)$ ]]; then
    error_message "'$CLONE_MODE' is not valid 'CLONE_MODE'."
    exit -1
fi

linstor_load_keys

# Load cleanup trap
trap linstor_cleanup_trap EXIT

# Deploy new volume
linstor_exec_and_log \
    "resource-definition create $DST_RES $RD_CREATE_ARGS"
LINSTOR_CLEANUP_RD+=" $DST_RES"

if [ "$CLONE_MODE" = "copy" ]; then
    linstor_exec_and_log \
        "volume-definition create $DST_RES ${SIZE}M $VOL_CREATE_ARGS"
    if [ "${PREFER_NODE,,}" = "yes" ]; then
        STORAGE_POOL=${STORAGE_POOL:-$($LINSTOR -m --output-version v0 resource-group list -r "$RESOURCE_GROUP" \
            | $JQ -r '.[][].select_filter.storage_pool_list // [] | join(" ")')}
        if [ -z "$STORAGE_POOL" ]; then
            error_message "Resource group missing 'StoragePool' attribute."
            exit -1
        fi
        NODE_HAS_STORAGE_POOL=$($LINSTOR -m --output-version v0 storage-pool list --node "$DST_HOST" --storage-pool $STORAGE_POOL \
            | $JQ -r ".[].stor_pools[] | .free_space.free_capacity / 1024 > ${SIZE}" )
        if [ "$NODE_HAS_STORAGE_POOL" = "true" ]; then
            linstor_exec_and_log \
                "resource create $DST_HOST $DST_RES $RES_CREATE_SHORT_ARGS --storage-pool $STORAGE_POOL"
        fi
    fi
    if [ -n "$RESOURCE_GROUP" ]; then
        linstor_exec_and_log \
            "resource-definition auto-place $DST_RES $RD_AUTO_PLACE_ARGS"
    else
        linstor_exec_and_log \
            "resource create $NODE_LIST $DST_RES $RES_CREATE_ARGS"
    fi
else
    linstor_exec_and_log \
        "snapshot create $SRC_RES $DST_RES"
    LINSTOR_CLEANUP_SNAPSHOT+=" $SRC_RES:$DST_RES"
    linstor_exec_and_log \
        "snapshot volume-definition restore --from-resource $SRC_RES --from-snapshot $DST_RES --to-resource $DST_RES"
    linstor_exec_and_log \
        "snapshot resource restore --from-resource $SRC_RES --from-snapshot $DST_RES --to-resource $DST_RES"
    linstor_exec_and_log \
        "volume-definition set-size $DST_RES 0 ${SIZE}M"
fi

# Set properties
linstor_exec_and_log \
    "resource-definition set-property $DST_RES Aux/one/VM_ID $VM_ID"
linstor_exec_and_log \
    "resource-definition set-property $DST_RES Aux/one/DS_ID $DS_ID"
linstor_exec_and_log \
    "resource-definition set-property $DST_RES Aux/one/DISK_ID $DISK_ID"


if [ "$CLONE_MODE" = "copy" ]; then

    # Attach diskless resources on SRC_RES side
    if linstor_attach_diskless "$SRC_HOST" "$SRC_RES" "$DISKLESS_POOL"; then
        LINSTOR_CLEANUP_RES+=" $SRC_HOST:$SRC_RES"
    fi
    if linstor_attach_diskless "$SRC_HOST" "$DST_RES" "$DISKLESS_POOL"; then
        if [ "$SRC_HOST" != "$DST_HOST" ]; then
            LINSTOR_CLEANUP_RES+=" $SRC_HOST:$DST_RES"
        fi
    fi
    
    (
        flock -s 200
        # Clone image from SRC_RES to DST_RES
        ssh_exec_and_log "$SRC_HOST" \
            "$DD if=$SRC_DEV of=$DST_DEV bs=${DD_BLOCK_SIZE:-64k} conv=${DD_CONV:-sparse}" \
            "Error cloning $SRC_DEV to $DST_DEV on $SRC_HOST"
    ) 200> "/var/lock/one/linstor_un-detach-${SRC_HOST}-${SRC_RES}.lock"
    RET_CODE=$?
    if [ $RET_CODE -ne 0 ]; then
        exit $RET_CODE
    fi
fi

# Attach diskless resource on destination host
linstor_attach_diskless "$DST_HOST" "$DST_RES" "$DISKLESS_POOL"

# Link device on destination host
LINK_CMD=$(cat <<EOF
    set -e -o pipefail
    mkdir -p $DST_DIR
    rm -f $DST_PATH
    ln -s $DST_DEV $DST_PATH
EOF
)

ssh_exec_and_log "$DST_HOST" "$LINK_CMD" \
    "Error linking $DST_DEV to $DST_PATH on $DST_HOST"

LINSTOR_CLEANUP_RD=
LINSTOR_CLEANUP_SNAPSHOT=
exit 0
