#!/bin/bash


# cleanup_opendaylight() - Remove residual data files, anything left over
# from previous runs that a clean run would need to clean up
function cleanup_opendaylight {
    # Wipe out the data, journal and snapshots directories ... grumble grumble grumble
    rm -rf $ODL_DIR/$ODL_NAME/{data,journal,snapshots}

    # Remove existing logfiles
    if [[ -n "$LOGDIR" ]]; then
        rm -f "$LOGDIR/$ODL_KARAF_LOG_BASE*"
    fi
    if [[ -n "$SCREEN_LOGDIR" ]]; then
        rm -f "$SCREEN_LOGDIR/$ODL_KARAF_LOG_BASE*"
    fi
    rm -f "$DEST/logs/$ODL_KARAF_LOG_BASE*"

    move_interface_addresses "outof_bridge"

    unbind_opendaylight_controller
}


# configure_opendaylight() - Set config files, create data dirs, etc
function configure_opendaylight {
    echo "Configuring OpenDaylight"

    # The logging config file in ODL
    local ODL_LOGGING_CONFIG=${ODL_DIR}/${ODL_NAME}/etc/org.ops4j.pax.logging.cfg

    # Add netvirt feature in Karaf, if it's not already there
    local ODLFEATUREMATCH=$(cat $ODL_DIR/$ODL_NAME/etc/org.apache.karaf.features.cfg | \
                            grep featuresBoot= | grep $ODL_NETVIRT_KARAF_FEATURE)
    if [ "$ODLFEATUREMATCH" == "" ]; then
        sed -i "/^featuresBoot=/ s/$/,$ODL_NETVIRT_KARAF_FEATURE/" \
               $ODL_DIR/$ODL_NAME/etc/org.apache.karaf.features.cfg
    fi

    if [[ "$ODL_RELEASE" =~ "helium" ]]; then
        # Move Tomcat to $ODL_PORT
        local _ODLPORT=$(cat $ODL_DIR/$ODL_NAME/configuration/tomcat-server.xml | \
                         grep $ODL_PORT)
        if [ "$_ODLPORT" == "" ]; then
            sed -i "/\<Connector port/ s/808./$ODL_PORT/" \
                   $ODL_DIR/$ODL_NAME/configuration/tomcat-server.xml
        fi
    else
        # Move Jetty to $ODL_PORT
        local _ODLPORT=$(cat $ODL_DIR/$ODL_NAME/etc/jetty.xml | grep $ODL_PORT)
        if [ "$_ODLPORT" == "" ]; then
            sed -i "/\<Property name\=\"jetty\.port/ s/808./$ODL_PORT/" \
                   $ODL_DIR/$ODL_NAME/etc/jetty.xml
        fi
    fi

    # Configure conntrack for legacy netvirt
    if [[ "$ODL_LEGACY_NETVIRT_CONNTRACK" == "True" ]]; then
        NETVIRT_INIT_CONFIG_XML=$NETWORKING_ODL_DIR/devstack/odl-etc/opendaylight/datastore/initial/config/netvirt-impl-config_netvirt-impl-config.xml
        ODL_DATASTORE_INITIAL_CONFIG_DIR=${ODL_DIR}/${ODL_NAME}/etc/opendaylight/datastore/initial/config
        mkdir -p $ODL_DATASTORE_INITIAL_CONFIG_DIR
        cp --backup --force $NETVIRT_INIT_CONFIG_XML $ODL_DATASTORE_INITIAL_CONFIG_DIR/
    fi

    # Configure L3 if the user wants it for NETVIRT_OVSDB
    # L3 is always enabled in NETVIRT_VPNSERVICE
    if [[ ",$ODL_NETVIRT_KARAF_FEATURE," =~ ",$ODL_NETVIRT_KARAF_FEATURE_OVSDB," ]] && [ "${ODL_L3}" == "True" ]; then
        # Configure L3 FWD if it's not there
        local L3FWD=$(cat $ODL_DIR/$ODL_NAME/etc/custom.properties | \
                      grep ^ovsdb.l3.fwd.enabled)
        if [ "$L3FWD" == "" ]; then
            echo "ovsdb.l3.fwd.enabled=yes" >> $ODL_DIR/$ODL_NAME/etc/custom.properties
        fi

        # Configure L3 GW MAC if it's not there
        local L3GW_MAC=$(cat $ODL_DIR/$ODL_NAME/etc/custom.properties | \
                         grep ^ovsdb.l3gateway.mac)
        if [[ -z "$L3GW_MAC" && -n "$ODL_L3GW_MAC" ]]; then
            echo "ovsdb.l3gateway.mac=$ODL_L3GW_MAC" >> $ODL_DIR/$ODL_NAME/etc/custom.properties
        fi
    fi

    # Remove existing logfiles
    local ODL_LOGDIR=$DEST/logs
    if [[ -n "$LOGDIR" ]]; then
        ODL_LOGDIR=$LOGDIR
    fi

    rm -f "$ODL_LOGDIR/$ODL_KARAF_LOG_BASE*"
    # Log karaf output to a file
    _LF=$ODL_LOGDIR/$ODL_KARAF_LOG_NAME
    LF=$(echo $_LF | sed 's/\//\\\//g')
    # Soft link for easy consumption
    sudo mkdir -p "$ODL_LOGDIR"
    ln -sf $_LF "$ODL_LOGDIR/screen-karaf.log"
    if [[ -n $SCREEN_LOGDIR ]]; then
        ln -sf $_LF "$SCREEN_LOGDIR/screen-karaf.log"
    fi

    # Change the karaf logfile
    # disable log rotation by setting max fiel size large enough
    sed -i -e "/^log4j\.appender\.out\.file/ s/.*/log4j\.appender\.out\.file\=$LF/" \
           -e "/^log4j\.appender\.out\.maxFileSize/ s/.*/log4j\.appender\.out\.maxFileSize\=1024GB/" \
    $ODL_DIR/$ODL_NAME/etc/org.ops4j.pax.logging.cfg

    # Configure DEBUG logs for network virtualization in odl, if the user wants it
    if [ "${ODL_NETVIRT_DEBUG_LOGS}" == "True" ]; then
        local OVSDB_DEBUG_LOGS=$(cat $ODL_LOGGING_CONFIG | grep ^log4j.logger.org.opendaylight.ovsdb)
        if [ "${OVSDB_DEBUG_LOGS}" == "" ]; then
            echo 'log4j.logger.org.opendaylight.ovsdb = TRACE, out' >> $ODL_LOGGING_CONFIG
            echo 'log4j.logger.org.opendaylight.ovsdb.lib = INFO, out' >> $ODL_LOGGING_CONFIG
            echo 'log4j.logger.org.opendaylight.ovsdb.openstack.netvirt.impl.NeutronL3Adapter = DEBUG, out' >> $ODL_LOGGING_CONFIG
            echo 'log4j.logger.org.opendaylight.ovsdb.openstack.netvirt.impl.TenantNetworkManagerImpl = DEBUG, out' >> $ODL_LOGGING_CONFIG
            echo 'log4j.logger.org.opendaylight.ovsdb.openstack.netvirt.providers.openflow13.services.arp.GatewayMacResolverService = DEBUG, out' >> $ODL_LOGGING_CONFIG
            echo 'log4j.logger.org.opendaylight.ovsdb.plugin.md.OvsdbInventoryManager = INFO, out' >> $ODL_LOGGING_CONFIG
        fi
        if [[ "$ODL_RELEASE" =~ "helium" ]]; then
            local ODL_NEUTRON_DEBUG_LOGS=$(cat $ODL_LOGGING_CONFIG | \
                        grep ^log4j.logger.org.opendaylight.controller.networkconfig.neutron)
            if [ "${ODL_NEUTRON_DEBUG_LOGS}" == "" ]; then
                echo 'log4j.logger.org.opendaylight.controller.networkconfig.neutron = TRACE, out' >> $ODL_LOGGING_CONFIG
            fi
        else
            local ODL_NEUTRON_DEBUG_LOGS=$(cat $ODL_LOGGING_CONFIG | \
                        grep ^log4j.logger.org.opendaylight.neutron)
            if [ "${ODL_NEUTRON_DEBUG_LOGS}" == "" ]; then
                echo 'log4j.logger.org.opendaylight.neutron = TRACE, out' >> $ODL_LOGGING_CONFIG
            fi
        fi
    fi
}

# configure_neutron_opendaylight() - Set Neutron config files according to ODL settings
function configure_neutron_odl {
    echo "Configuring ML2 for OpenDaylight"

    # https://bugs.launchpad.net/neutron/+bug/1614766
    # Allow ovsdb_interface native by avoiding port conflict.
    if [[ -n "$ODL_OVSDB_ALTPORT" ]]; then
        iniset $NEUTRON_CONF OVS ovsdb_connection tcp:127.0.0.1:$ODL_OVSDB_ALTPORT
        iniset $NEUTRON_DHCP_CONF OVS ovsdb_connection tcp:127.0.0.1:$ODL_OVSDB_ALTPORT
    fi

    populate_ml2_config /$Q_PLUGIN_CONF_FILE ml2_odl url=$ODL_ENDPOINT
    populate_ml2_config /$Q_PLUGIN_CONF_FILE ml2_odl username=$ODL_USERNAME
    populate_ml2_config /$Q_PLUGIN_CONF_FILE ml2_odl password=$ODL_PASSWORD
    populate_ml2_config /$Q_PLUGIN_CONF_FILE ml2_odl port_binding_controller=$ODL_PORT_BINDING_CONTROLLER
    if [[ -n "$ODL_TIMEOUT" ]]; then
        populate_ml2_config /$Q_PLUGIN_CONF_FILE ml2_odl timeout=$ODL_TIMEOUT
    fi
    # When it's not set, the default value is set by networking-odl
    if [[ -n "$ODL_HOSTCONF_URI" ]]; then
        populate_ml2_config /$Q_PLUGIN_CONF_FILE ml2_odl odl_hostconf_uri=$ODL_HOSTCONF_URI
    fi

    # NOTE(mgkwill): ODL layer-3 and DHCP services currently lack support
    # for metadata. Enabling both native services also requires enabling
    # config drive to provide instances with metadata. If conventional DHCP agent
    # is used instead, configure it to provide instances with metadata.
    if is_service_enabled q-dhcp; then
        # Conventional DHCP agent must provide all metadata when ODL
        # layer-3 is enabled. The conventional DHCP agent will be forced
        # to provide metadata for all networks.
        iniset $Q_DHCP_CONF_FILE DEFAULT force_metadata True
    fi
    if [[ "$ODL_L3" == "True" ]]; then
        if is_service_enabled n-cpu; then
            iniset $NOVA_CONF DEFAULT force_config_drive True
        fi
    fi
}

function configure_neutron_odl_lightweight_testing {
    echo "Configuring lightweight testing for OpenDaylight"
    populate_ml2_config /$Q_PLUGIN_CONF_FILE ml2_odl enable_lightweight_testing=True
}

# init_opendaylight() - Initialize databases, etc.
function init_opendaylight {
    # clean up from previous (possibly aborted) runs
    # create required data files
    :
}


# install_opendaylight() - Collect source and prepare
function install_opendaylight {
    echo "Installing OpenDaylight and dependent packages"
    if [[ "$ODL_USING_EXISTING_JAVA" != "True" ]]
    then
        if ! setup_java "${ODL_REQUIRED_JAVA_VERSION:-7}"; then
            exit 1
        fi
    fi

    # Download OpenDaylight
    cd $ODL_DIR

    if [[ "$OFFLINE" != "True" ]]; then
        wget -N $ODL_URL/$ODL_PKG
    fi
    unzip -u -o $ODL_PKG
}


# install_networking_odl() - Install the ML2 driver and other plugins/drivers
function install_networking_odl {
    echo "Installing the Networking-ODL driver for OpenDaylight"
    setup_develop $NETWORKING_ODL_DIR
}


# install_opendaylight_compute() - Make sure OVS is installed
function install_opendaylight_compute {
    if [[ "$SKIP_OVS_INSTALL" = "True" ]]; then
        echo "Skipping OVS installation."
    else
        # packages are the same as for Neutron OVS agent
        _neutron_ovs_base_install_agent_packages
    fi
}


# start_opendaylight() - Start running processes, including screen
function start_opendaylight {
    echo "Starting OpenDaylight"

    # Wipe out the data and journal directories ... grumble grumble grumble
    rm -rf $ODL_DIR/$ODL_NAME/{data,journal}

    # The following variables are needed by the running karaf process.
    # See the "bin/setenv" file in the OpenDaylight distribution for
    # their individual meaning.
    setup_java_env
    export JAVA_MIN_MEM=$ODL_JAVA_MIN_MEM
    export JAVA_MAX_MEM=$ODL_JAVA_MAX_MEM
    export JAVA_MAX_PERM_MEM=$ODL_JAVA_MAX_PERM_MEM

    # this is a forking process, just start it in the background
    $ODL_DIR/$ODL_NAME/bin/start

    if [ -n "$ODL_BOOT_WAIT_URL" ]; then
        echo "Waiting for Opendaylight to start via $ODL_BOOT_WAIT_URL ..."
        # Probe ODL restconf for netvirt until it is operational
        local testcmd="curl -o /dev/null --fail --silent --head -u \
              ${ODL_USERNAME}:${ODL_PASSWORD} http://${ODL_MGR_IP}:${ODL_PORT}/${ODL_BOOT_WAIT_URL}"
        test_with_retry "$testcmd" "Opendaylight did not start after $ODL_BOOT_WAIT" \
              $ODL_BOOT_WAIT $ODL_RETRY_SLEEP_INTERVAL
    else
        echo "Waiting for Opendaylight to start ..."
        # Sleep a bit to let OpenDaylight finish starting up
        sleep $ODL_BOOT_WAIT
    fi
}


# stop_opendaylight() - Stop running processes (non-screen)
function stop_opendaylight {
    # Stop the karaf container
    $ODL_DIR/$ODL_NAME/bin/stop
}


# cleanup_opendaylight_compute() - Remove all OVS ports, bridges and disconnects
# controller from switch
function cleanup_opendaylight_compute {
    # Remove the patch ports
    for port in $(sudo ovs-vsctl show | grep Port | awk '{print $2}'  | cut -d '"' -f 2 | grep patch); do
        sudo ovs-vsctl del-port ${port}
    done

    # remove all OVS ports that look like Neutron created ports
    for port in $(sudo ovs-vsctl list port | grep -o -e tap[0-9a-f\-]* -e q[rg]-[0-9a-f\-]*); do
        sudo ovs-vsctl del-port ${port}
    done

    # Remove all the vxlan ports
    for port in $(sudo ovs-vsctl list port | grep name | grep vxlan | awk '{print $3}'  | cut -d '"' -f 2); do
        sudo ovs-vsctl del-port ${port}
    done

    # Disconnect controller from switch
    unbind_opendaylight_controller

    # remove all OVS bridges created by ODL
    for bridge in $(sudo ovs-vsctl list-br | grep -o -e ${OVS_BR} -e ${PUBLIC_BRIDGE}); do
        sudo ovs-vsctl del-br ${bridge}
    done
}

# bind_opendaylight_controller() - set control manager to OVS
function bind_opendaylight_controller {
    echo_summary "Initializing OpenDaylight"
    ODL_LOCAL_IP=${ODL_LOCAL_IP:-$HOST_IP}
    ODL_MGR_PORT=${ODL_MGR_PORT:-6640}
    ODL_OVS_MANAGERS=${ODL_OVS_MANAGERS:-$ODL_MGR_IP}
    read ovstbl <<< $(sudo ovs-vsctl get Open_vSwitch . _uuid)
    local ODL_MANAGERS_PARAM=()
    for manager in $(echo $ODL_OVS_MANAGERS | tr "," "\n"); do
        # ovsdbd doesn't understand localhost. replace it to numerical ip address
        ODL_MANAGERS_PARAM=( "${ODL_MANAGERS_PARAM[@]}" "tcp:${manager/localhost/127.0.0.1}:$ODL_MGR_PORT" )
    done
    # don't overwrite the already existing managers
    local ODL_MANAGERS_OLD=$(sudo ovs-vsctl get-manager)
    local ODL_MANAGERS=$(echo $ODL_MANAGERS_OLD ${ODL_MANAGERS_PARAM[@]} | tr ' ' '\n' | sort | uniq | tr '\n' ' ')
    sudo ovs-vsctl set-manager ${ODL_MANAGERS}
    if [[ -n "$ODL_PROVIDER_MAPPINGS" ]]; then
        sudo ovs-vsctl set Open_vSwitch $ovstbl \
            other_config:provider_mappings=$ODL_PROVIDER_MAPPINGS
    fi
    sudo ovs-vsctl set Open_vSwitch $ovstbl other_config:local_ip=$ODL_LOCAL_IP
    # for pseudo agent port binding
    if [ "$ODL_PORT_BINDING_CONTROLLER" == "pseudo-agentdb-binding" ]; then
        sudo neutron-odl-ovs-hostconfig --config-file=$NEUTRON_CONF $ODL_OVS_HOSTCONFIGS_OPTIONS
    fi
}

# unbind_opendaylight_controller() - disconnect controller from switch and clear bridges
function unbind_opendaylight_controller {
    sudo ovs-vsctl del-manager
    BRIDGES=$(sudo ovs-vsctl list-br)
    for bridge in $BRIDGES ; do
        sudo ovs-vsctl del-controller $bridge
     done
}


function _configure_veth {
    ip link show $Q_PUBLIC_VETH_INT > /dev/null 2>&1 ||
        sudo ip link add $Q_PUBLIC_VETH_INT type veth \
             peer name $Q_PUBLIC_VETH_EX
    sudo ip link set $Q_PUBLIC_VETH_INT up
    sudo ip link set $Q_PUBLIC_VETH_EX up
    sudo ip addr flush dev $Q_PUBLIC_VETH_EX
    if [[ ",$ODL_NETVIRT_KARAF_FEATURE," =~ ",$ODL_NETVIRT_KARAF_FEATURE_OVSDB," ]]; then
        local OVSBR_EX=$(echo $ODL_PROVIDER_MAPPINGS | cut -d ':' -f1)
        sudo ovs-vsctl --may-exist add-port $OVSBR_EX $Q_PUBLIC_VETH_INT
    else
        sudo ovs-vsctl --may-exist add-port $OVS_BR $Q_PUBLIC_VETH_INT
    fi

    local cidr_len=${FLOATING_RANGE#*/}
    sudo ip addr replace ${PUBLIC_NETWORK_GATEWAY}/$cidr_len dev $Q_PUBLIC_VETH_EX
    if [[ -n "$IPV6_PUBLIC_RANGE" ]] && [[ -n "$IPV6_PUBLIC_NETWORK_GATEWAY" ]] && [[ -n "$FIXED_RANGE_V6" ]] && [[ -n "$IPV6_ROUTER_GW_IP" ]]; then
        local ipv6_cidr_len=${IPV6_PUBLIC_RANGE#*/}
        sudo ip -6 addr replace ${IPV6_PUBLIC_NETWORK_GATEWAY}/$ipv6_cidr_len dev ${Q_PUBLIC_VETH_EX}
    fi
}

function _configure_opendaylight_l3_legacy_netvirt {
    wait_for_active_bridge $PUBLIC_BRIDGE $ODL_RETRY_SLEEP_INTERVAL $ODL_BOOT_WAIT

    # Note: as of Lithium-SR3 and Beryllium releases, ODL will add the OVS port(s)
    #       to the external bridge via the ODL_PROVIDER_MAPPINGS method. Being so,
    #       the usage of PUBLIC_INTERFACE is no longer necessary in ODL devstack.

    if [[ "$Q_USE_PUBLIC_VETH" == "True" ]]; then
        _configure_veth
    else
        # DEPRECATED: Add public interface to public bridge, if needed
        if [[ -n "$PUBLIC_INTERFACE" && "$PUBLIC_INTERFACE" != "br100" ]]; then
            deprecated "Adding $PUBLIC_INTERFACE to $PUBLIC_BRIDGE."
            deprecated "Use ODL_PROVIDER_MAPPINGS instead of PUBLIC_INTERFACE."
            sudo ovs-vsctl --no-wait -- --may-exist add-port $PUBLIC_BRIDGE $PUBLIC_INTERFACE
            sudo ip link set $PUBLIC_INTERFACE up
        fi

        move_interface_addresses "into_bridge"
    fi
}

function _configure_opendaylight_l3_new_netvirt {
    if [[ "$Q_USE_PUBLIC_VETH" == "True" ]]; then
        _configure_veth
    fi
}


# configure_opendaylight_l3() - configure bridges for OpenDaylight L3 forwarding
function configure_opendaylight_l3 {
    if [[ ",$ODL_NETVIRT_KARAF_FEATURE," =~ ",$ODL_NETVIRT_KARAF_FEATURE_OVSDB," ]]; then
        _configure_opendaylight_l3_legacy_netvirt
    else
        _configure_opendaylight_l3_new_netvirt
    fi
}
