#!/bin/bash

set -x

# this test requires fully functional dd in VM, cirros (busybox) version will not work
# this test requires sudo and pkill in VM

SPT_FLOATING_NET=${SPT_FLOATING_NET:-"admin_floating_net"}
SPT_FLAVOR=${SPT_FLAVOR:-"m1.small"}
SPT_IMAGE=${SPT_IMAGE:-"xenial"}
SPT_VM_USER=${SPT_VM_USER:-"ubuntu"}
SPT_VM_COOLDOWN=${SPT_VM_COOLDOWN:-"120"}
DD_OPTIONS=${DD_OPTIONS:-"oflag=direct"}
DD_TIMEOUT=${DD_TIMEOUT:-"10"}
SSH_OPTS="-q -n -oStrictHostKeyChecking=no -oUserKnownHostsFile=/dev/null -l $SPT_VM_USER"
VM_BOOT_TIMEOUT_MINUTES=${VM_BOOT_TIMEOUT_MINUTES:-"5"}

vm_boot_check_tries=$(($VM_BOOT_TIMEOUT_MINUTES*2))
vm_boot_check_delay=30
vm_active_check_tries=10
vm_active_check_delay=10
vm_volume_available_check_tries=10
vm_volume_available_check_delay=30
vm_volume_attached_check_tries=10
vm_volume_attached_check_delay=10

function cleanup {
  echo "cleaning up..."
  [ -n "$instance_id" ] && [ -n "$floatingip" ] && nova floating-ip-disassociate $instance_id $floatingip
  [ -n "$floatingip_id" ] && neutron floatingip-delete $floatingip_id
  [ -n "$instance_id" ] && [ -n "$secgroup_id" ] && nova remove-secgroup $instance_id $secgroup_id
  [ -n "$instance_id" ] && nova delete $instance_id
  [ -n "$secgroup_id" ] && neutron security-group-delete $secgroup_id
  [ -n "$router_id" ] && [ -n "$subnet_id" ] && neutron router-interface-delete $router_id $subnet_id
  [ -n "$router_id" ] && neutron router-delete $router_id
  [ -n "$subnet_id" ] && neutron subnet-delete $subnet_id
  [ -n "$net_id" ] && neutron net-delete $net_id
  [ -n "$keypair_name" ] && nova keypair-delete $keypair_name
  [ -f "spt-temporary-keypair" ] && rm "spt-temporary-keypair"
  [ -n "$volume_id" ] && cinder delete $volume_id
}

function check_code {
  # arguments:
  # $1 - exit code
  # $2 - error message
  # $3 - command output to print
  if [ "$1" -ne "0" ]
  then
    echo "$2:"
    echo "$3"
    cleanup
    exit 1
  fi
}

. openrc

# create keypair
result="$(nova keypair-add "spt-temporary-keypair" >"spt-temporary-keypair" 2>&1)"
code=$?
[ "$code" -eq "0" ] && keypair_name="spt-temporary-keypair"
check_code $? "failed to create keypair" "$result"
chmod 600 "spt-temporary-keypair"

# create network
result="$(neutron net-create "spt-temporary-net" 2>&1)"
check_code $? "failed to create network" "$result"
net_id=$(echo "$result" | grep "^| id " | awk '{print $4}')

# create subnet
result="$(neutron subnet-create --name "spt-temporary-subnet" $net_id 10.20.30.0/24 2>&1)"
check_code $? "failed to create subnet" "$result"
subnet_id=$(echo "$result" | grep "^| id " | awk '{print $4}')

# create router
result="$(neutron router-create "spt-temporary-router" 2>&1)"
check_code $? "failed to create router" "$result"
router_id=$(echo "$result" | grep "^| id " | awk '{print $4}')

# add router interface
result="$(neutron router-interface-add $router_id $subnet_id 2>&1)"
check_code $? "failed to add router interface to subnet" "$result"

# set router gateway
result="$(neutron router-gateway-set $router_id $SPT_FLOATING_NET 2>&1)"
check_code $? "failed to set router gateway" "$result"

# create security group
result="$(neutron security-group-create "spt-temporary-security-group" 2>&1)"
check_code $? "failed to create security group" "$result"
secgroup_id=$(echo "$result" | grep "^| id " | awk '{print $4}')

# create security group rule
result="$(neutron security-group-rule-create $secgroup_id --protocol TCP 2>&1)"
check_code $? "failed to create security group rule" "$result"

# create floating ip
result="$(neutron floatingip-create $SPT_FLOATING_NET 2>&1)"
check_code $? "failed to create floatingip" "$result"
floatingip=$(echo "$result" | grep "^| floating_ip_address " | awk '{print $4}')
floatingip_id=$(echo "$result" | grep "^| id " | awk '{print $4}')

# boot VM
result="$(nova boot --image $SPT_IMAGE --flavor $SPT_FLAVOR --nic net-id=$net_id --key-name "spt-temporary-keypair" --security-groups $secgroup_id "spt-temporary-vm" 2>&1)"
check_code $? "failed to boot VM" "$result"
instance_id=$(echo "$result" | grep "^| id " | awk '{print $4}')

# wait for instance to become active
for i in $(seq 1 $vm_active_check_tries)
do
  result="$(nova show $instance_id 2>&1)"
  vm_status=$(echo "$result" | grep "^| *status" | awk '{printf $4}')
  [ "$vm_status" == "ACTIVE" ] && break
  [ $i -lt $vm_active_check_tries ] && sleep $vm_active_check_delay
done
! [ "$vm_status" == "ACTIVE" ] && check_code 1 "timeout waiting for VM to become active" "$result"

# associate floatingip
result="$(nova floating-ip-associate $instance_id $floatingip 2>&1)"
check_code $? "failed to associate floatingip" "$result"

# create volume
result="$(cinder create --name "spt-temporary-volume" 4 2>&1)"
check_code $? "failed to create volume" "$result"
volume_id=$(echo "$result" | grep "^| *id " | awk '{print $4}')

# wait for volume to become available
for i in $(seq 1 $vm_volume_available_check_tries)
do
  result="$(cinder show $volume_id 2>&1)"
  volume_status=$(echo "$result" | grep "^| *status" | awk '{printf $4}')
  [ "$volume_status" == "available" ] && break
  [ $i -lt $vm_volume_available_check_tries ] && sleep $vm_volume_available_check_delay
done
! [ "$volume_status" == "available" ] && check_code 1 "timeout waiting for volume to become available" "$result"

# test connection to VM
for i in $(seq 1 $vm_boot_check_tries)
do
  result="$(ssh $SSH_OPTS -i spt-temporary-keypair $floatingip "uptime" 2>&1)"
  code=$?
  [ $code -eq 0 ] && break
  [ $i -lt $vm_boot_check_tries ] && sleep $vm_boot_check_delay
done
check_code $code "failed to connect to VM" "$result"

# due to unreliable cinder attachment procedure, first get the list of current drives
vm_drives="$(ssh $SSH_OPTS -i spt-temporary-keypair $floatingip "find /dev/vd*" 2>&1)"
check_code $? "failed to find /dev/vd* in VM" "$result"

# attach volume
result="$(nova volume-attach $instance_id $volume_id 2>&1)"
check_code $? "failed to attach volume" "$result"

# wait for volume to appear in VM
for i in $(seq 1 $vm_volume_attached_check_tries)
do
  vm_drives_2="$(ssh $SSH_OPTS -i spt-temporary-keypair $floatingip "find /dev/vd*" 2>&1)"
  new_drive="$((echo "$vm_drives"; echo "$vm_drives_2") | sort | uniq -u)"
  [ -n "$new_drive" ] && break
  if [ $i -lt $vm_volume_attached_check_tries ]
  then
    nova volume-attach $instance_id $volume_id &> /dev/null # retry attaching since it is known to fail silently sometimes
    sleep $vm_volume_attached_check_delay
  fi
done
! [ -n "$new_drive" ] && check_code 1 "timeout waiting for volume to appear in VM" "$vm_drives_2"

# mkfs in VM
result="$(ssh $SSH_OPTS -i spt-temporary-keypair $floatingip "sudo -i mkfs.ext4 -m 0 $new_drive" 2>&1)"
check_code $? "failed to create ext4 filesystem on VM" "$result"

# mount volume in VM
result="$(ssh $SSH_OPTS -i spt-temporary-keypair $floatingip "sudo -i mount $new_drive /mnt && sudo -i chown $SPT_VM_USER /mnt" 2>&1)"
check_code $? "failed to mount ext4 filesystem on VM" "$result"

# cooldown to allow VM to finish init activities
sleep $SPT_VM_COOLDOWN

# give 10 seconds to dd to run, then getting intermediate stats, waiting 1 second and killing. Need to wait before killing, otherwise intermediate stats may not be printed
timelimit_cmd="pid=\$!; sleep $DD_TIMEOUT; kill -USR1 \$pid; sleep 1; kill \$pid"

# run dd write tests
echo "running dd write test - writing 1GB with bs=4k"
result="$(ssh $SSH_OPTS -i spt-temporary-keypair $floatingip "dd if=/dev/zero of=/mnt/spt-write-test $DD_OPTIONS bs=4k count=262144 &:; $timelimit_cmd" 2>&1)"
echo "$result" | xargs -I@ echo "DD_TEST_1: @"

echo "running dd write test - writing 1GB with bs=1M"
result="$(ssh $SSH_OPTS -i spt-temporary-keypair $floatingip "dd if=/dev/zero of=/mnt/spt-write-test-2 $DD_OPTIONS bs=1M count=1024 &:; $timelimit_cmd" 2>&1)"
echo "$result" | xargs -I@ echo "DD_TEST_2: @"

echo "running dd write test - writing 1GB with bs=1G"
result="$(ssh $SSH_OPTS -i spt-temporary-keypair $floatingip "dd if=/dev/zero of=/mnt/spt-write-test-3 $DD_OPTIONS bs=1G count=1" 2>&1)" # no time limit here because it's a single block operation, no intermediate results available
echo "$result" | xargs -I@ echo "DD_TEST_3: @"

cleanup
