#!/bin/bash

# Copyright 2017 Red Hat, Inc.
#
# NAME
#     rht-movebr0 - Red Hat Training Move br0 connection tool
#
# SYNOPSIS
#     rht-movebr0 --status
#     rht-movebr0 --interface IFACE
#
# DESCRIPTION
#     Changes br0's backing interface to an alternate interface.
#       --status - display current status of backing and interfaces
#       --interface IFACE - sets the br0 backing to be IFACE
#
# CHANGELOG
#   * Fri Aug 25 2017 Robert Locke <rlocke@redhat.com>
#   - change in behavior of -- versus blank
#   * Fri Jul  7 2017 Robert Locke <rlocke@redhat.com>
#   - initial code

networkdir=/etc/sysconfig/network-scripts

# Source library of functions
source /usr/local/lib/rhttool.shlib

function print_usage() {
  local retcode=$1
  cat <<-EOF
$0 is a script to set up forwarding and NAT services.

Usage: $0 --status
  will display current status of br0 backing and interfaces
Usage: $0 --interface IFACE
  will enable IFACE as the new backing of br0 (dropping existing)
Usage: $0 [--help] (default)
  will display this usage information.

EOF
  exit ${retcode}
}

function parse_opts() {
  ARGS=$(getopt -o hsi: -l "help,status,interface:" -n "rht-movebr0" -- "$@")
  if [[ $? -ne 0 ]]; then
    print_usage 1
  fi
  eval set -- "${ARGS}"
  while true ; do
    case $1 in
      -h|--help)
        print_usage 0 ;;
      -s|--status)
        status=yes ; shift ;;
      -i|--interface)
        interface=config ; shift
        if [[ -n "$1" ]]; then
          new_interface=$1 ; shift
        fi
        ;;
      --)
        shift ; break ;;
      *)
        echo "Error: bad option, $1" ; print_usage 1 ;;
    esac
  done
  [[ -z ${status} && -z ${interface} ]] && print_usage 0
}

function ext_status() {
  if [[ -n ${br0_slaves} ]]; then
    echo "The following interface is currently linked to br0: ${br0_slaves}"
    speed_check=false
    for slave in ${br0_slaves}; do
      if ip addr show ${slave} | grep -q 'state DOWN'; then
        echo "ERROR: br0 slave (${slave}) is down"
      fi
      speed_output=$(ethtool ${slave} 2>&1 | grep 'Speed:' | cut -d' ' -f2)
      if [[ "${speed_output}" = "1000Mb/s" ]]; then
        echo "   br0 slave (${slave}) speed detected is ${speed_output}"
        speed_check=true
      else
        echo "WARNING: br0 slave (${slave}) speed detected is ${speed_output}"
      fi
    done
    if [[ "${speed_check}" = false ]]; then
      echo "WARNING: None of br0 slaves are GigE"
    fi
  else
    echo "No interfaces are currently linked to br0."
  fi
  if [[ -n ${extifaces} ]]; then
    echo "The following interfaces are currently externally configured: ${extifaces}"
  else
    echo "No interfaces are currently configured for external access."
  fi
  if [[ -n ${netdevs} ]]; then
    echo "Here are a list of potential devices:"
    for iface in ${netdevs} ; do
      nmcli -t -f DEVICE,CONNECTION dev status | grep ${iface} | sed 's/^/   /'
    done
  else
    echo "There do not appear to be any viable potential devices."
  fi
}

function ext_interface() {

  # Confirm validity of new_interface
  if [[ ${interface} == "config" ]]; then
    iface=${new_interface}
  else
    print_usage 1
  fi
  # Is IFACE valid?
  if [[ -z ${netdevs} ]]; then
    echo "ERROR - Invalid interface, ${iface}"
    print_usage 1
  fi
  ifacevalid=
  for i in ${netdevs} ; do
    [[ ${i} == ${iface} ]] && ifacevalid=true
  done
  if [[ -z ${ifacevalid} ]]; then
    echo "ERROR - Invalid interface, ${iface}"
    print_usage 1
  fi
  # Punt if ${iface} is the external interface
  for extdev in ${extifaces}; do
    if [[ "${extdev}" == "${iface}" ]]; then
      echo "ERROR - Interface ${extdev} configured for external use"
      print_usage 1
    fi
  done

  # Disable existing bridge slave
  for slave in ${br0_slaves}; do
    slaveconn=$(nmcli -t -f DEVICE,CON-UUID dev status | grep ${slave} | cut -d: -f2)
    nmcli con mod ${slaveconn} connection.master "" connection.slave-type "" connection.autoconnect no
    nmcli dev disconnect ${slave}
  done

  # Determine connection name for iface
  ifaceconn=$(nmcli -t -f DEVICE,CONNECTION dev status | grep ${iface} | cut -d: -f2)
  if [[ -z ${ifaceconn} || "${ifaceconn}" == "--" ]]; then
    echo "No active connection for ${iface}"
    # Test for existing ifcfg file
    ifcfgfile=$(ls -1 ${networkdir}/ifcfg-${iface} | head -n1) 
    if [[ -n ${ifcfgfile} ]]; then 
      # Check for DEVICE= in the file and add if missing 
      if ! grep -l "^DEVICE=${iface}" ${ifcfgfile} &>/dev/null ; then 
        echo "DEVICE=${iface}" >> ${ifcfgfile} 
        nmcli con reload 
      fi 
    fi 
    ifcfgfile=$(grep -l "^DEVICE=${iface}" ${networkdir}/ifcfg-* | head -n1)
    if [[ -z "${ifcfgfile}" ]]; then
      echo "Creating connection named ${iface}"
      ifaceconn=${iface}
      nmcli con add type ethernet con-name ${ifaceconn} ifname ${iface} master br0 slave-type bridge autoconnect yes
    else
      echo "Existing config file $(basename ${ifcfgfile})"
      ifaceconn="$(grep '^UUID=' ${ifcfgfile} | cut -d= -f2)"
      nmcli con mod ${ifaceconn} master br0 slave-type bridge autoconnect yes
    fi
  else
    echo "Active connection for ${iface}:${ifaceconn}"
    nmcli con mod ${ifaceconn} master br0 slave-type bridge autoconnect yes
  fi
  sleep 5
  nmcli con up ${ifaceconn}

}

# If not root, then sudo ourself and run as root
if [[ "${EUID}" -gt "0" ]]; then
  ${sudo:-/usr/bin/sudo} $0 "$@"
  exit
fi

# Parse arguments
parse_opts "$@"

# Gather some basic data from machine
# Generic mask of interfaces that cannot function as slave of br0
ifacesbad="lo|br.*|virbr.*|privbr.*|vnet.*"
# Interface connected to the classroom/student machines
br0_slaves="$(brctl show br0 | sed '1d; s/.*\t//' | grep -v 'vnet.*')"
# Current external interface (there should be zero or one)
extifaces=$(firewall-cmd --get-active-zones | grep -A1 external | sed '1d; s/.*: //')
# Add classroom network as not br0 possible (it already is)
[[ -n "${extifaces}" ]] && ifacesbad="${ifacesbad}|${extifaces}"
# Full list of network devices
#netdevall=$(tail -n +3 /proc/net/dev | cut -d: -f1)
netdevall=$(nmcli -t -f DEVICE dev status)
# List of viable network devices
netdevs=""
for iface in ${netdevall} ; do
  if [[ ! "$iface" =~ ^(${ifacesbad})$ ]]; then
    if ! grep -E 'DEVTYPE=(bridge|wlan|wwan)' /sys/class/net/${iface}/uevent &>/dev/null ; then
      netdevs="${netdevs} ${iface}"
    fi
  fi
done

# There are some mutually exclusive options that may have been specified.

# Perform actions
if [[ -n ${status} ]]; then
  if [[ -n ${interface} ]]; then
    echo "ERROR - The --status option cannot be combined with any other options"
    print_usage 1
  fi
  ext_status
fi
[[ -n "${interface}" ]] && ext_interface

