#!/bin/bash -e
set -euo pipefail
instances="${instances:-20}"
bridge="${bridge:-"br1"}"
ethernet="${ethernet:-"br0"}"
zone="${zone:-"trusted"}"

# Set the MTU of the bridge interface according to https://docs.openvswitch.org/en/latest/faq/issues
# * See https://progress.opensuse.org/issues/151310
# * Use default of 1460 (instead of 1450 as the FAQ suggests) because 1460 should be low enough but
#   is still higher than 1458 which openSUSE MM tests configure within the SUT.
mtu=${mtu:-1460}

usage() {
    cat << EOF
Usage: os-autoinst-setup-multi-machine

Options:
 -h, --help         display this help
EOF
    exit "$1"
}

opts=$(getopt -o h -l help -n "$0" -- "$@") || usage 1
eval set -- "$opts"
while true; do
    case "$1" in
        -h | --help) usage 0 ;;
        --)
            shift
            break
            ;;
        *) break ;;
    esac
done

ensure_ip_forwarding() {
    grep -q 1 /proc/sys/net/ipv4/ip_forward || echo -e 'net.ipv4.ip_forward = 1\nnet.ipv6.conf.all.forwarding = 1' > /etc/sysctl.d/ip_forward.conf
}

install_packages() {
    command -v retry > /dev/null || zypper -n in retry
    retry -e -s 30 -r 7 -- sh -c "zypper ref && zypper -n in openvswitch os-autoinst-openvswitch firewalld libcap-progs"
}

configure_firewall() {
    systemctl enable --now firewalld
    firewall-cmd --permanent --get-services | grep -q isotovideo && firewall-cmd --permanent --delete-service=isotovideo
    firewall-cmd --permanent --new-service isotovideo
    for i in $(seq 1 "$instances"); do firewall-cmd --permanent --service=isotovideo --add-port=$((i * 10 + 20003))/tcp; done
    firewall-cmd --permanent --zone="$zone" --add-service=isotovideo
    firewall-cmd --get-default-zone | grep -q "$zone" || firewall-cmd --set-default-zone="$zone"
    cat > /etc/firewalld/zones/"$zone".xml << EOF
<?xml version="1.0" encoding="utf-8"?>
<zone target="ACCEPT">
  <short>"${zone^}"</short>
  <description>All network connections are accepted.</description>
  <service name="isotovideo"/>
  <interface name="$bridge"/>
  <interface name="ovs-system"/>
  <interface name="$ethernet"/>
  <masquerade/>
</zone>
EOF
    systemctl restart firewalld
}

create_gre_preup_script() {
    local location=$1
    cat > "$location" << EOF
#!/bin/sh
action="\$1"
bridge="\$2"
ovs-vsctl set bridge \$bridge rstp_enable=true
# TODO add entries according to your network topology
#ovs-vsctl --may-exist add-port \$bridge gre1 -- set interface gre1 type=gre options:remote_ip=<IP address of other host>
EOF
    chmod +x "$location"
}

setup_multi_machine_with_networkmanager() {
    # Restart NM to load ovs plugin
    systemctl restart NetworkManager
    # Delete any previous connections
    nmcli con | grep -oP 'ovs-(interface|port|bridge|slave)-[\w-]+' | xargs -r nmcli con del || true
    # Create bridge, port and interface connection
    nmcli con add type ovs-bridge con.int "$bridge"
    nmcli con add type ovs-port con.int "$bridge" con.master "$bridge"
    nmcli con add type ovs-interface con.int "$bridge" con.master "$bridge" ipv4.method manual ipv4.address 10.0.2.2/15 ethernet.mtu "$mtu" con.zone "$zone"
    # Create tap interfaces
    for i in 0 $(
        seq 1 "$instances"
        seq 64 $((64 + instances))
        seq 128 $((128 + instances))
    ); do
        nmcli con add type ovs-port con.int "tap$i" con.master "$bridge"
        nmcli con add type tun mode tap owner "$(id -u _openqa-worker)" group "$(getent group nogroup | cut -f3 -d:)" con.int "tap$i" master "tap$i"
    done
    create_gre_preup_script /etc/NetworkManager/dispatcher.d/gre_tunnel_preup.sh
}

setup_multi_machine_with_wicked() {
    ovs-vsctl list-br | grep -q "$bridge" && ovs-vsctl del-br "$bridge"
    ovs-vsctl add-br "$bridge"
    ovs-vsctl set int "$bridge" mtu_request="$mtu"
    cat > "/etc/sysconfig/network/ifcfg-$bridge" << EOF
BOOTPROTO='static'
IPADDR='10.0.2.2/15'
STARTMODE='auto'
ZONE="$zone"
OVS_BRIDGE='yes'
PRE_UP_SCRIPT="wicked:gre_tunnel_preup.sh"
OVS_BRIDGE_PORT_DEVICE_0='tap0'
EOF
    cat > /etc/sysconfig/network/ifcfg-tap0 << EOF
BOOTPROTO='none'
IPADDR=''
NETMASK=''
PREFIXLEN=''
STARTMODE='auto'
TUNNEL='tap'
TUNNEL_SET_GROUP='nogroup'
TUNNEL_SET_OWNER='_openqa-worker'
EOF
    for i in $(
        seq 1 "$instances"
        seq 64 $((64 + instances))
        seq 128 $((128 + instances))
    ); do ln -sf ifcfg-tap0 "/etc/sysconfig/network/ifcfg-tap$i" && echo "OVS_BRIDGE_PORT_DEVICE_$i='tap$i'" >> "/etc/sysconfig/network/ifcfg-$bridge"; done
    create_gre_preup_script /etc/wicked/scripts/gre_tunnel_preup.sh

    # ensure the zone of the uplink/ethernet device is set in accordance as well
    sed -i -e "s/ZONE=.*/ZONE=$zone/g" "/etc/sysconfig/network/ifcfg-$ethernet"
}

configure_openvswitch() {
    echo "OS_AUTOINST_USE_BRIDGE=$bridge" > /etc/sysconfig/os-autoinst-openvswitch
    systemctl enable os-autoinst-openvswitch
    systemctl restart openvswitch os-autoinst-openvswitch
}

main() {
    network=$(basename -s.service "$(readlink /etc/systemd/system/network.service)")
    test "$network" = wicked -o "$network" = NetworkManager || {
        echo "This script only works with wicked network daemon or NetworkManager"
        exit 1
    }
    ensure_ip_forwarding
    install_packages
    configure_firewall
    systemctl enable --now openvswitch
    case "$network" in
        wicked)
            setup_multi_machine_with_wicked
            ;;
        NetworkManager)
            setup_multi_machine_with_networkmanager
            ;;
    esac
    configure_openvswitch
}

caller 0 > /dev/null || main "$@"
