#!/bin/bash
set -e

lock_file=/var/lock/openshift-sdn.lock

action=$1
net_container=$2
tenant_id=$3

lockwrap() {
    (
    flock 200
    "$@"
    ) 200>${lock_file}
}

# Retrieve the name of the host-local member of the veth pair that
# connects the container (identified by pid) to the docker bridge.
get_veth_host() {
    local pid=$1

    local veth_ifindex=$(nsenter -n -t $pid -- ethtool -S eth0 | sed -n -e 's/.*peer_ifindex: //p')
    # Strip a suffix starting with '@' from the interface name.
    # The suffixed interface name won't be recognized by brctl or ovs-*
    ip link show | sed -ne "s/^$veth_ifindex: \([^:@]*\).*/\1/p"
}

get_ipaddr_pid_veth() {
    network_mode=$(docker inspect --format "{{.HostConfig.NetworkMode}}" ${net_container})
    if [ "${network_mode}" == "host" ]; then
      # quit, nothing for the SDN here
      exit 0
    elif [[ "${network_mode}" =~ container:.* ]]; then
      # Get pod infra container
      net_container=$(echo ${network_mode} | cut -d ":" -f 2)
    fi
    ipaddr=$(docker inspect --format "{{.NetworkSettings.IPAddress}}" ${net_container})
    pid=$(docker inspect --format "{{.State.Pid}}" ${net_container})
    veth_host=$(get_veth_host $pid)
}

add_ovs_port() {
    brctl delif lbr0 $veth_host
    ovs-vsctl add-port br0 ${veth_host}
}

del_ovs_port() {
    ovs-vsctl --if-exists del-port $veth_host
}

add_ovs_flows() {
    ovs_port=$(ovs-ofctl -O OpenFlow13 dump-ports-desc br0  | grep ${veth_host} | cut -d "(" -f 1 | tr -d ' ')

    case $tenant_id in
	-1) # single-tenant plugin
	    ovs-ofctl -O OpenFlow13 add-flow br0 "table=4,priority=100,in_port=${ovs_port},ip,nw_src=${ipaddr},actions=goto_table:6"
	    ;;

	0)  # multi-tenant plugin, admin namespace
	    ovs-ofctl -O OpenFlow13 add-flow br0 "table=4,priority=100,in_port=${ovs_port},ip,nw_src=${ipaddr},actions=load:${tenant_id}->NXM_NX_REG0[],goto_table:5"
	    ovs-ofctl -O OpenFlow13 add-flow br0 "table=7,priority=150,ip,nw_dst=${ipaddr},actions=output:${ovs_port}"
	    ;;

	*)  # multi-tenant plugin, normal namespace
	    ovs-ofctl -O OpenFlow13 add-flow br0 "table=4,priority=100,in_port=${ovs_port},ip,nw_src=${ipaddr},actions=load:${tenant_id}->NXM_NX_REG0[],goto_table:5"
	    ovs-ofctl -O OpenFlow13 add-flow br0 "table=7,priority=100,ip,nw_dst=${ipaddr},reg0=${tenant_id},actions=output:${ovs_port}"
	    ;;
    esac
}

del_ovs_flows() {
    ovs-ofctl -O OpenFlow13 del-flows br0 "ip,nw_dst=${ipaddr}"
    ovs-ofctl -O OpenFlow13 del-flows br0 "ip,nw_src=${ipaddr}"
    ovs-ofctl -O OpenFlow13 del-flows br0 "arp,nw_dst=${ipaddr}"
}

add_subnet_route() {
    source /run/openshift-sdn/config.env
    local subnet_route="ip route add ${OPENSHIFT_CLUSTER_SUBNET} dev eth0 proto kernel scope link src $ipaddr"
    nsenter -n -t $pid -- $subnet_route
}

Init() {
    true
}

Setup() {
    get_ipaddr_pid_veth
    add_ovs_port
    add_ovs_flows
    add_subnet_route
}

Update() {
    get_ipaddr_pid_veth
    del_ovs_flows
    add_ovs_flows
}

Teardown() {
    get_ipaddr_pid_veth
    del_ovs_port
    del_ovs_flows
}

Status() {
    # do nothing, empty output will default to address as picked by docker
    true
}

case "$action" in
    init)
	lockwrap Init
	;;
    setup)
	set -x
	lockwrap Setup
	;;
    update)
	set -x
	lockwrap Update
	;;
    teardown)
	set -x
	lockwrap Teardown
	;;
    status)
	lockwrap Status
	;;
    *)
        echo "Bad input: $@"
        exit 1
esac
