#!/bin/sh
set -eu

# Default to loop storage pool if /dev/nvme0n1p3 doesn't exist.
storageDevice=""
if [ -f "/dev/nvme0n1p3" ]; then
  storageDevice="/dev/nvme0n1p3"
fi

parentNIC="enp5s0"

waitVMAgent() (
  set +x
  # shellcheck disable=SC3043
  local vmName="$1"
  for i in $(seq 90) # Wait up to 90s.
  do
    if incus info "${vmName}" | grep -qF 127.0.0.1; then
      return 0 # Success.
    fi

    sleep 1
  done

  echo "VM ${vmName} agent not running after ${i}s"
  return 1 # Failed.
)

cleanup() {
    echo ""
    if [ "${FAIL}" = "1" ]; then
        echo "Test failed"
        exit 1
    fi

    echo "Test passed"
    exit 0
}


# Check that all instances have an IPv4 and IPv6 address
networkTests() {
    # shellcheck disable=SC3043
    local FAIL=0

    echo "=> Performing network tests"
    for url in $(incus query "/1.0/instances" | jq -r .[]); do
        name=$(echo "${url}" | cut -d/ -f4)

        case "${name}" in
        c3|c4|v3|v4)
            continue # Can't test instances on different VLAN.
        esac

        echo ""

        # Get the addresses
        address=$(incus query "${url}/state" | jq -r ".network.eth0.addresses | .[] | select(.scope | contains(\"global\")) | .address" 2>/dev/null || true)
        if [ -z "${address}" ]; then
            address=$(incus query "${url}/state" | jq -r ".network.enp5s0.addresses | .[] | select(.scope | contains(\"global\")) | .address" 2>/dev/null || true)
        fi

        if [ -z "${address}" ]; then
            echo "FAIL: No network interface: ${name}"
            FAIL=1
            continue
        fi

        # IPv4 address
        if echo "${address}" | grep "\." -q; then
            echo "PASS: IPv4 address: ${name}"
        else
            echo "FAIL: IPv4 address: ${name}"
            FAIL=1
        fi

        # IPv6 address
        if echo "${address}" | grep ":" -q; then
            echo "PASS: IPv6 address: ${name}"
        else
            echo "FAIL: IPv6 address: ${name}"
            FAIL=1
        fi

        # DNS resolution
        if incus exec "${name}" -- getent hosts linuxcontainers.org >/dev/null 2>&1 || incus exec "${name}" -- ping -c1 -W1 linuxcontainers.org >/dev/null 2>&1; then
            echo "PASS: DNS resolution: ${name}"
        else
            echo "FAIL: DNS resolution: ${name}"
            FAIL=1
        fi
    done

    if [ "${FAIL}" = "1" ]; then
        return 1
    fi

    return 0
}

FAIL=1
trap cleanup EXIT HUP INT TERM

# Install test dependencies
apt-get remove --purge cloud-init --yes
apt-get install --yes curl jq zfsutils-linux

# Install Incus
curl -sL https://pkgs.zabbly.com/get/incus-daily | sh

# Enable SR-IOV on nic and bring up
echo 7 > "/sys/class/net/${parentNIC}/device/sriov_numvfs"
ip link set "${parentNIC}" up
sleep 10
ethtool "${parentNIC}"

# Configure Incus
incus storage create default zfs source="${storageDevice}"
incus profile device add default root disk path=/ pool=default
incus profile device add default eth0 nic nictype=sriov parent="${parentNIC}" name=eth0

# Launch a few VMs.
# Do this first before containers to ensure VF free search handles VFs unbound from host.
echo "==> VM on default VLAN"
incus init images:ubuntu/20.04/cloud v1 --vm
incus start v1

echo "==> VM on default VLAN with filtering"
incus init images:ubuntu/20.04/cloud v2 --vm
incus config device override v2 eth0 security.mac_filtering=true
incus start v2

echo "==> VM on alternate VLAN"
incus init images:ubuntu/20.04/cloud v3 --vm
incus config device override v3 eth0 vlan=4000
incus start v3

echo "==> VM on alternate VLAN with filtering"
incus init images:ubuntu/20.04/cloud v4 --vm
incus config device override v4 eth0 vlan=4000 security.mac_filtering=true
incus start v4

# Wait for VMs to start.
waitVMAgent v1
waitVMAgent v2
waitVMAgent v3
waitVMAgent v4

incus list
networkTests

# Delete all VMs except v1 to free up VFs for container tests.
# Leave v1 so that the first VF is unbound to check that containers don't mistake it as free.
incus delete -f v2 v3 v4

# Launch a few containers.
echo "==> Container on default VLAN"
incus init images:ubuntu/20.04/cloud c1
incus start c1

echo "==> Container on default VLAN with filtering"
incus init images:ubuntu/20.04/cloud c2
incus config device override c2 eth0 security.mac_filtering=true
incus start c2

echo "==> Container on alternate VLAN"
incus init images:ubuntu/20.04/cloud c3
incus config device override c3 eth0 vlan=4000
incus start c3

echo "==> Container on alternate VLAN with filtering"
incus init images:ubuntu/20.04/cloud c4
incus config device override c4 eth0 vlan=4000 security.mac_filtering=true
incus start c4

# Wait for containers to start.
sleep 30s

incus list
networkTests

# Delete remaining instances.
incus delete -f v1 c1 c2 c3 c4

incus profile device remove default eth0
incus profile device remove default root
incus storage delete default

FAIL=0
