#!/usr/bin/env ruby
#
# frozen_string_literal: true

# ---------------------------------------------------------------------------- #
# Copyright 2002-2025, 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: clones the image from the datastore (non-persistent images)
# ARGUMENTS: fe:SOURCE host:remote_system_ds/disk.i vmid dsid
#   - 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
#   - vmid is the id of the VM
#   - dsid is the target datastore (0 is the system datastore)

require_relative '../lib/tm_action'

arg_src   = ARGV[0]
arg_dst   = ARGV[1]
arg_vmid  = ARGV[2]
_arg_dsid = ARGV[3]

clone = TransferManager::Action.new(:vm_id => arg_vmid, :action_name => 'clone')

src = TransferManager::Action::Location.new(arg_src)
dst = TransferManager::Action::Location.new(arg_dst)

clone.make_dst_path(dst, false)
clone.enable_local_monitoring(dst, 'ssh')

# CLONE DISK

OpenNebula::DriverLogger.log_info "Cloning #{src.path} to #{dst.path}"

disk_id     = dst.base.to_s.split('.')[-1]
disk_format = clone.disk_format(disk_id)
disk_sparse = clone.sparse?(disk_id)

tar_opt = if disk_sparse
              'Sf'
          else
              'f'
          end

script, host =
    case disk_format
    when :raw
        # File layout at destination:
        #
        # └── 7
        #    ├── deployment.0
        #    ├── disk.0
        #    ├── disk.1
        #    ├── ds.xml
        #    └── vm.xml
        [<<~SCRIPT, src.host]
            set -e -o pipefail

            if [ -d "#{src.path}.snap" ]; then
                SRC_SNAP="#{src.base}.snap"
            fi

            tar -C #{src.dir} --transform='flags=r;s|#{src.base}|#{dst.base}|' -c#{tar_opt} - #{src.base} \$SRC_SNAP | \
                ssh #{dst.host} "tar -x#{tar_opt} - -C #{dst.dir}"
        SCRIPT
    when :qcow2
        # File layout at destination:
        #
        # └── 7
        #    ├── deployment.0
        #    ├── disk.0 -> disk.0.snap/0
        #    ├── disk.0.snap
        #    │   ├── 0
        #    ├── disk.1
        #    ├── ds.xml
        #    └── vm.xml
        #
        # Notes of snapshots structure:
        #  - If exists, rebuild the symlink to point <hash>.snap/<snapid>,
        #    instead of disk.<disk_id>.snap/<snapid>
        #  - If it does not exist, create it.
        [<<~SCRIPT, dst.host]
            set -e -o pipefail

            pushd #{dst.dir}
            rm -rf #{dst.path} #{dst.path}.snap

            ssh -n #{src.host} "
                if [ -d '#{src.path}.snap' ]; then
                    SRC_SNAP='#{src.base}.snap'
                fi

                tar -C #{src.dir} --transform='flags=r;s|#{src.base}|#{dst.base}|' \
                    -c#{tar_opt} - #{src.base} \\\$SRC_SNAP" \
            | tar -x#{tar_opt} - -C #{dst.dir}

            if [ -L '#{dst.path}' ] && [ -d '#{dst.path}.snap' ]; then
                PREVIOUS_SNAP="$(readlink "#{dst.path}" | xargs basename)"
                ln -sf disk.#{disk_id}.snap/\$PREVIOUS_SNAP disk.#{disk_id}
            elif [ -f '#{dst.path}' ] && [ ! -e '#{dst.path}.snap' ]; then
                mkdir '#{dst.path}.snap'
                mv '#{dst.path}' '#{dst.path}.snap/0'
                ln -s '#{dst.base}.snap/0' '#{dst.path}'
            else
                echo 'Invalid qcow2 directory structure' >&2
                exit 1
            fi

            popd
        SCRIPT
    end

rc = clone.ssh(:cmds => script,
               :host => host,
               :forward => true,
               :nostdout => false,
               :nostderr => false,
               :err_msg => "copying #{src} to #{dst} from #{host} (format: #{disk_format})")

exit(rc.code) if rc.code != 0

# RESIZE DISK IF NEEDED
(size, original_size) = clone.disk_sizes(disk_id).values_at(:current, :original)

if original_size && size > original_size
    rc = clone.resize_disk(dst, size, disk_sparse)

    exit(rc.code) if rc.code != 0
end
