#!/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.                                             #
#--------------------------------------------------------------------------- #

###############################################################################
# This script is used to copy a VM image (SRC) to the image repository as DST
# Several SRC types are supported
###############################################################################

# -------- Set up the environment to source common tools & conf ------------

if [ -z "${ONE_LOCATION}" ]; then
    LIB_LOCATION=/usr/lib/one
else
    LIB_LOCATION=$ONE_LOCATION/lib
fi

. $LIB_LOCATION/sh/scripts_common.sh

DRIVER_PATH=$(dirname $0)
UTILS_PATH="${DRIVER_PATH}/.."

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

# -------- Get cp and datastore arguments from OpenNebula core ------------

if [ -n "$2" ]; then
    DRV_ACTION=$1
    ID=$2
else
    DRV_ACTION=$(cat -)
    ID=$1
fi

XPATH="${UTILS_PATH}/xpath.rb -b $DRV_ACTION"

unset i j XPATH_ELEMENTS

while IFS= read -r -d '' element; do
    XPATH_ELEMENTS[i++]="$element"
done < <($XPATH     /DS_DRIVER_ACTION_DATA/DATASTORE/BASE_PATH \
                    /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/RESTRICTED_DIRS \
                    /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/SAFE_DIRS \
                    /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/BRIDGE_LIST \
                    /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/STAGING_DIR \
                    /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/LS_CONTROLLERS \
                    /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/LS_CERTFILE \
                    /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/LS_KEYFILE \
                    /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/LS_CAFILE \
                    /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/RESOURCE_GROUP \
                    /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/NODE_LIST \
                    /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/LAYER_LIST \
                    /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/PROVIDERS \
                    /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/REPLICAS_ON_SAME \
                    /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/REPLICAS_ON_DIFFERENT \
                    /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/AUTO_PLACE \
                    /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/REPLICA_COUNT \
                    /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/DO_NOT_PLACE_WITH \
                    /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/DO_NOT_PLACE_WITH_REGEX \
                    /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/STORAGE_POOL \
                    /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/DISKLESS_POOL \
                    /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/ENCRYPTION \
                    /DS_DRIVER_ACTION_DATA/IMAGE/PATH \
                    /DS_DRIVER_ACTION_DATA/IMAGE/SIZE \
                    /DS_DRIVER_ACTION_DATA/IMAGE/TEMPLATE/MD5 \
                    /DS_DRIVER_ACTION_DATA/IMAGE/TEMPLATE/SHA1 \
                    /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/NO_DECOMPRESS \
                    /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/LIMIT_TRANSFER_BW)

BASE_PATH="${XPATH_ELEMENTS[j++]}"
RESTRICTED_DIRS="${XPATH_ELEMENTS[j++]}"
SAFE_DIRS="${XPATH_ELEMENTS[j++]}"
BRIDGE_LIST="${XPATH_ELEMENTS[j++]}"
STAGING_DIR="${XPATH_ELEMENTS[j++]:-$STAGING_DIR}"
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}"
ENCRYPTION="${XPATH_ELEMENTS[j++]}"
SRC="${XPATH_ELEMENTS[j++]}"
SIZE="${XPATH_ELEMENTS[j++]}"
MD5="${XPATH_ELEMENTS[j++]}"
SHA1="${XPATH_ELEMENTS[j++]}"
NO_DECOMPRESS="${XPATH_ELEMENTS[j++]}"
LIMIT_TRANSFER_BW="${XPATH_ELEMENTS[j++]}"

DST_HOST=`linstor_get_bridge_host "" "" $ID`

if [ -z "$DST_HOST" ]; then
    exit -1
fi
if [ -z "$RESOURCE_GROUP" ]; then
    if [ -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

linstor_load_keys

# Load cleanup trap
trap linstor_cleanup_trap EXIT

set_up_datastore "$BASE_PATH" "$RESTRICTED_DIRS" "$SAFE_DIRS"

# ------------------------------------------------------------------------------
# Get image and datastore arguments from OpenNebula core
# ------------------------------------------------------------------------------

IMAGE_HASH=`generate_image_hash`
DST_RES="one-image-${ID}"
DST_DEV="/dev/drbd/by-res/${DST_RES}/0"
DST="$DST_DEV"
TMP_DST="$STAGING_DIR/$IMAGE_HASH"

DOWNLOADER_ARGS=`set_downloader_args "$MD5" "$SHA1" "$NO_DECOMPRESS" "$LIMIT_TRANSFER_BW" "$SRC" -`

COPY_COMMAND="$UTILS_PATH/downloader.sh $DOWNLOADER_ARGS"

case $SRC in
http://*|https://*)
    log "Downloading $SRC to the image repository"

    DUMP="$COPY_COMMAND"
    ;;

s3://*)
    log "Downloading $SRC to the image repository"

    DUMP="DRV_ACTION=$DRV_ACTION $COPY_COMMAND"
    ;;

*)
    if [ `check_restricted $SRC` -eq 1 ]; then
        log_error "Not allowed to copy images from $RESTRICTED_DIRS"
        error_message "Not allowed to copy image file $SRC"
        exit -1
    fi

    log "Copying local image $SRC to the image repository"

    DUMP="$COPY_COMMAND"
    ;;
esac

# Trick to save both image size and type from fs_size function
FS_SIZE_OUTPUT=$(
    fs_size "${SRC}" "${NO_DECOMPRESS}" "${LIMIT_TRANSFER_BW}" || exit $?
    echo "$TYPE"
)
if [ $? -ne 0 ]; then
    error_message "Can not calculate the size for $SRC: $FS_SIZE_OUTPUT"
    exit -1
fi
SIZE="${FS_SIZE_OUTPUT%%$'\n'*}"
TYPE="${FS_SIZE_OUTPUT##*$'\n'}"

# ------------------------------------------------------------------------------
# Deploy new volume
# ------------------------------------------------------------------------------

linstor_exec_and_log \
    "resource-definition create $DST_RES $RD_CREATE_ARGS"
LINSTOR_CLEANUP_RD+=" $DST_RES"
linstor_exec_and_log \
    "volume-definition create $DST_RES ${SIZE}M $VOL_CREATE_ARGS"
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

# Attach diskless resource for DST_RES on DST_HOST
if linstor_attach_diskless "$DST_HOST" "$DST_RES" "$DISKLESS_POOL"; then
    LINSTOR_CLEANUP_RES+=" $DST_HOST:$DST_RES"
fi

# ------------------------------------------------------------------------------
# Dumping image to volume
# ------------------------------------------------------------------------------

# Import image from fs
IMPORT_CMD=$(cat <<EOF
    $QEMU_IMG convert -O raw $TMP_DST $DST_DEV
    EC=$?
    $RM -f $TMP_DST
    exit $EC
EOF
)

if [ "$TYPE" = "raw" ]; then
    # If image type is raw save image directly to destination
    multiline_exec_and_log \
        "set -e -o pipefail; $DUMP | $SSH $DST_HOST '$DD of=$DST_DEV bs=${DD_BLOCK_SIZE:-64k} conv=${DD_CONV:-sparse}'" \
        "Error importing $SRC to $DST_HOST:$DST_DEV"
else
    # Otherwise save locally and convert it
    multiline_exec_and_log \
        "set -e -o pipefail; $DUMP | $SSH $DST_HOST '$DD of=$TMP_DST bs=${DD_BLOCK_SIZE:-64k} conv=${DD_CONV:-sparse}'" \
        "Error saving $SRC to $DST_HOST:$TMP_DST"
    ssh_exec_and_log "$DST_HOST" "$IMPORT_CMD" \
        "Error importing $DST_DEV on $DST_HOST"
fi

LINSTOR_CLEANUP_RD=
echo "$DST raw"
