#!/usr/bin/bash

# Copyright (c) 2011 Alexey I. Froloff.
#
# 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.

. /etc/init.d/functions

cd /etc/sysconfig/network-scripts
. ./network-functions

[ -f ../network ] && . ../network

CONFIG=${1}
TIMEOUT=10

need_config ${CONFIG}

source_config

OVS_CONFIGURED_MTU=${MTU}

# Default MTU of internal ports is 1500 if not set in the interface configuration file
MTU=${MTU:-1500}

OTHERSCRIPT="/etc/sysconfig/network-scripts/ifup-${REAL_DEVICETYPE}"

if [ ! -x ${OTHERSCRIPT} ]; then
        OTHERSCRIPT="/etc/sysconfig/network-scripts/ifup-eth"
fi

check_recursion ()
{
        [ -n "${UPPEDSTACK}" ] && for _r in ${UPPEDSTACK}; do
                [ "$_r" = "$1" ] && return 1
        done

        return 0
}

ifup_ovs_bridge ()
{
        if ovs-vsctl br-exists "${OVS_BRIDGE}"; then :; else
                /sbin/ifup "${OVS_BRIDGE}"
        fi
}

if [ -z "${UPPEDSTACK}" ]; then
        UPPEDSTACK="${DEVICE}"
fi

[ -n "${OVSREQUIRES}" ] && for _i in ${OVSREQUIRES}; do
        if ( check_recursion "$_i" ); then
                UPPEDSTACK="${UPPEDSTACK} $_i" /sbin/ifup "$_i"
        fi
done

SERVICE_UNIT=/usr/lib/systemd/system/openvswitch.service
if [ -f $SERVICE_UNIT ] && [ -x /usr/bin/systemctl ]; then
        if ! systemctl --quiet is-active openvswitch.service; then
                systemctl start openvswitch.service
        fi
else
        if [ ! -f /var/lock/subsys/openvswitch ]; then
                /sbin/service openvswitch start
        fi
fi

case "$TYPE" in
        OVSBridge|OVSUserBridge)
                # If bridge already exists and is up, it has been configured through
                # other cases like OVSPort, OVSIntPort and OVSBond. If it is down or
                # it does not exist, create it. It is possible for a bridge to exist
                # because it remained in the OVSDB for some reason, but it won't be up.
                if [ "${TYPE}" = "OVSUserBridge" ]; then
                        DATAPATH="netdev"
                fi
                if check_device_down "${DEVICE}"; then
                        if [ -n "${OVS_CONFIGURED_MTU}" ]; then
                                ovs-vsctl -t ${TIMEOUT} -- --may-exist add-br "$DEVICE" $OVS_OPTIONS \
                                ${OVS_EXTRA+-- $OVS_EXTRA} \
                                -- set Interface "$DEVICE" mtu_request="$OVS_CONFIGURED_MTU" \
                                ${STP+-- set bridge "$DEVICE" stp_enable="${STP}"} \
                                ${DATAPATH+-- set bridge "$DEVICE" datapath_type="$DATAPATH"}
                        else
                                ovs-vsctl -t ${TIMEOUT} -- --may-exist add-br "$DEVICE" $OVS_OPTIONS \
                                ${OVS_EXTRA+-- $OVS_EXTRA} \
                                ${STP+-- set bridge "$DEVICE" stp_enable="${STP}"} \
                                ${DATAPATH+-- set bridge "$DEVICE" datapath_type="$DATAPATH"}
                        fi
                else
                        OVSBRIDGECONFIGURED="yes"
                        if [ -n "${OVS_CONFIGURED_MTU}" ]; then
                                ovs-vsctl -t ${TIMEOUT} -- set Interface "$DEVICE" \
                                        mtu_request="$OVS_CONFIGURED_MTU"
                        else
                                # logically, we should set the MTU to 0 to clear the MTU request.
                                # but we don't want to reset the MTU of the bridge, because
                                # we set the MTU of the bridge equal to the MTU of its OVSPort
                                # ovs-vsctl -t ${TIMEOUT} -- set Interface "$DEVICE" \
                                #        mtu_request=[]
                                :
                        fi
                fi

                # If MACADDR is provided in the interface configuration file,
                # we need to set it using ovs-vsctl; setting it with the "ip"
                # command in ifup-eth does not make the change persistent.
                if [ -n "$MACADDR" ]; then
                        ovs-vsctl -t ${TIMEOUT} -- set bridge "$DEVICE" \
                                other-config:hwaddr="$MACADDR"
                fi

                # When dhcp is enabled, the assumption is that there will be a port to
                # attach (otherwise, we can't reach out for dhcp). So, we do not
                # configure the bridge through rhel's ifup infrastructure unless
                # it is being configured after the port has been configured.
                # The "OVSINTF" is set only after the port is configured.
                if [ "${OVSBOOTPROTO}" = "dhcp" ] && [ -n "${OVSINTF}" ]; then
                        case " ${OVSDHCPINTERFACES} " in
                                *" ${OVSINTF} "*)
                                        BOOTPROTO=dhcp ${OTHERSCRIPT} ${CONFIG}
                                ;;
                        esac
                fi

                # When dhcp is not enabled, it is possible that someone may want
                # a standalone bridge (i.e it may not have any ports). Configure it.
                if [ "${OVSBOOTPROTO}" != "dhcp" ] && [ -z "${OVSINTF}" ] && \
                        [ "${OVSBRIDGECONFIGURED}" != "yes" ]; then
                        ${OTHERSCRIPT} ${CONFIG}
                fi
                exit 0
                ;;
        OVSPort)
                ifup_ovs_bridge
                ${OTHERSCRIPT} ${CONFIG} ${2}
                # If OVS_CONFIGURED_MTU is set, use it for the port. Otherwise, use current MTU of the port.
                OVS_PORT_MTU=${OVS_CONFIGURED_MTU}
                if [ -z "${OVS_PORT_MTU}" ] && [ -r "/sys/class/net/${DEVICE}/mtu" ]; then
                        OVS_PORT_MTU=$(<"/sys/class/net/${DEVICE}/mtu")
                fi
                if [ -n "${OVS_PORT_MTU}" ]; then
                        # If the bridge's MTU request is not set, set it to the port's MTU.
                        # This emulates the behavior of the bridge's MTU request being set
                        # to the port's MTU when the bridge is created.
                        OVS_BRIDGE_MTU_REQUEST=$(ovs-vsctl -t ${TIMEOUT} \
                                get Interface "$OVS_BRIDGE" mtu_request 2>/dev/null)
                        if [ "${OVS_BRIDGE_MTU_REQUEST}" = "[]" ]; then
                                ovs-vsctl -t ${TIMEOUT} -- set Interface "$OVS_BRIDGE" \
                                        mtu_request="$OVS_PORT_MTU"
                        fi
                fi
                # The port might be already in the database but not yet
                # in the datapath.  So, remove the stale interface first.
                if [ -n "${OVS_PORT_MTU}" ]; then
                        ovs-vsctl -t ${TIMEOUT} \
                                -- --if-exists del-port "$OVS_BRIDGE" "$DEVICE" \
                                -- add-port "$OVS_BRIDGE" "$DEVICE" $OVS_OPTIONS \
                                -- set Interface "$DEVICE" mtu_request="$OVS_PORT_MTU" \
                                ${OVS_EXTRA+-- $OVS_EXTRA}
                else
                        ovs-vsctl -t ${TIMEOUT} \
                                -- --if-exists del-port "$OVS_BRIDGE" "$DEVICE" \
                                -- add-port "$OVS_BRIDGE" "$DEVICE" $OVS_OPTIONS \
                                ${OVS_EXTRA+-- $OVS_EXTRA}
                fi
                OVSINTF=${DEVICE} /sbin/ifup "$OVS_BRIDGE"
                ;;
        OVSIntPort)
                ifup_ovs_bridge
                ovs-vsctl -t ${TIMEOUT} \
                        -- --if-exists del-port "$OVS_BRIDGE" "$DEVICE" \
                        -- add-port "$OVS_BRIDGE" "$DEVICE" $OVS_OPTIONS \
                        -- set Interface "$DEVICE" type=internal mtu_request="$MTU" \
                        ${OVS_EXTRA+-- $OVS_EXTRA}
                if [ -n "${OVSDHCPINTERFACES}" ]; then
                        for _iface in ${OVSDHCPINTERFACES}; do
                                /sbin/ifup ${_iface}
                        done
                fi
                BOOTPROTO="${OVSBOOTPROTO}" ${OTHERSCRIPT} ${CONFIG} ${2}
                ;;
        OVSBond)
                ifup_ovs_bridge
                for _iface in $BOND_IFACES; do
                        /sbin/ifup ${_iface}
                done
                ovs-vsctl -t ${TIMEOUT} \
                        -- --if-exists del-port "$OVS_BRIDGE" "$DEVICE" \
                        -- add-bond "$OVS_BRIDGE" "$DEVICE" ${BOND_IFACES} $OVS_OPTIONS \
                        ${OVS_EXTRA+-- $OVS_EXTRA}
                for _iface in $BOND_IFACES; do
                        ovs-vsctl -t ${TIMEOUT} -- set Interface "${_iface}" \
                                mtu_request="$MTU"
                done
                OVSINTF=${DEVICE} /sbin/ifup "$OVS_BRIDGE"
                ;;
        OVSTunnel)
                ifup_ovs_bridge
                ovs-vsctl -t ${TIMEOUT} \
                        -- --if-exists del-port "$OVS_BRIDGE" "$DEVICE" \
                        -- add-port "$OVS_BRIDGE" "$DEVICE" $OVS_OPTIONS \
                        -- set Interface "$DEVICE" type=$OVS_TUNNEL_TYPE \
                        mtu_request="$MTU" $OVS_TUNNEL_OPTIONS \
                        ${OVS_EXTRA+-- $OVS_EXTRA}
                ;;
        OVSPatchPort)
                ifup_ovs_bridge
                ovs-vsctl -t ${TIMEOUT} \
                        -- --if-exists del-port "$OVS_BRIDGE" "$DEVICE" \
                        -- add-port "$OVS_BRIDGE" "$DEVICE" $OVS_OPTIONS \
                        -- set Interface "$DEVICE" type=patch mtu_request="$MTU" \
                        options:peer="${OVS_PATCH_PEER}" ${OVS_EXTRA+-- $OVS_EXTRA}
                ;;
        OVSDPDKPort)
                ifup_ovs_bridge
                BRIDGE_MAC_ORIG=$(get_hwaddr $OVS_BRIDGE)
                ovs-vsctl -t ${TIMEOUT} \
                        -- --if-exists del-port "$OVS_BRIDGE" "$DEVICE" \
                        -- add-port "$OVS_BRIDGE" "$DEVICE" $OVS_OPTIONS \
                        -- set Interface "$DEVICE" type=dpdk mtu_request="$MTU" \
                        ${OVS_EXTRA+-- $OVS_EXTRA}
                BRIDGE_MAC=$(get_hwaddr $OVS_BRIDGE)
                # The bridge may change its MAC to be the lower one among all its
                # ports. If that happens, bridge configuration (e.g. routes) will
                # be lost. Restore the post-up bridge configuration again.
                if [ "$BRIDGE_MAC_ORIG" != "$BRIDGE_MAC" ]; then
                        ${OTHERSCRIPT} "$OVS_BRIDGE"
                fi
                ;;
        OVSDPDKVhostUserPort)
                ifup_ovs_bridge
                PORT_TYPE="dpdkvhostuser"
                PORT_PATH=""
                if [ "$OVS_PORT_MODE" == "client" ]; then
                        PORT_TYPE="dpdkvhostuserclient"
                        PORT_PATH="options:vhost-server-path=${OVS_PORT_PATH}"
                fi
                ovs-vsctl -t ${TIMEOUT} \
                        -- --if-exists del-port "$OVS_BRIDGE" "$DEVICE" \
                        -- add-port "$OVS_BRIDGE" "$DEVICE" $OVS_OPTIONS \
                        -- set Interface "$DEVICE" type=$PORT_TYPE \
                        mtu_request="$MTU" \
                        $PORT_PATH \
                        ${OVS_EXTRA+-- $OVS_EXTRA}
                ;;
        OVSDPDKBond)
                ifup_ovs_bridge
                BRIDGE_MAC_ORIG=$(get_hwaddr $OVS_BRIDGE)
                for _iface in $BOND_IFACES; do
                        IFACE_TYPES="${IFACE_TYPES} -- set interface ${_iface} type=dpdk mtu_request=${MTU}"
                done
                ovs-vsctl -t ${TIMEOUT} \
                        -- --if-exists del-port "$OVS_BRIDGE" "$DEVICE" \
                        -- add-bond "$OVS_BRIDGE" "$DEVICE" ${BOND_IFACES} $OVS_OPTIONS ${IFACE_TYPES} ${OVS_EXTRA+-- $OVS_EXTRA}
                BRIDGE_MAC=$(get_hwaddr $OVS_BRIDGE)
                # The bridge may change its MAC to be the lower one among all its
                # ports. If that happens, bridge configuration (e.g. routes) will
                # be lost. Restore the post-up bridge configuration again.
                if [ "$BRIDGE_MAC_ORIG" != "$BRIDGE_MAC" ]; then
                        ${OTHERSCRIPT} "$OVS_BRIDGE"
                fi
                ;;
        *)
                echo $"Invalid OVS interface type $TYPE"
                exit 1
                ;;
esac
