#!/bin/bash # -*-shell-mode-*- ### expected to be run as root COMMAND=$0 #################### global vars RUN_DIR=@RUNDIR@ DB_CONF_FILE=@DBDIR@/conf.db DB_SCHEMA=@pkgdatadir@/vswitch.ovsschema DB_LOG=@LOGDIR@/ovs-db.log ## DB_SOCKET=$RUN_DIR/db.sock ## SWITCH_LOG=@LOGDIR@/ovs-switch.log #################### helper functions function kill_pltap_ovs () { killall pltap-ovs 2>/dev/null || : } function error { echo "$@" >&2 exit 1 } function get_params { params=$1; shift err_msg="$COMMAND $SUBCOMMAND $(echo $params | perl -pe 's/\S+/<$&>/g')" for p in $params; do [[ -z "$@" ]] && error "$err_msg" pname=$(echo -n $p|perl -pe 's/\W/_/g') eval $pname="$1"; shift done [[ -n "$@" ]] && error "$err_msg" } function is_switch_running { ovs-appctl --target=ovs-vswitchd version >& /dev/null } function is_db_running { ovs-appctl --target=ovsdb-server version >& /dev/null } function tapname () { IP=$1; shift echo $(ip addr show to "$IP/32" | perl -ne '/^\s*\d+:\s*([\w-]+):/ && print $1') } function wait_server () { pid_file=$1; shift server_name=$1; shift timeout=$1; shift expire=$(($(date +%s) + $timeout)) ## wait for it to be up - xxx todo - could use a timeout of some kind while [ ! -f "$pid_file" ]; do echo "Waiting for $server_name to start... $(($expire - $(date +%s)))s left" >&2 sleep 1; [ $(date +%s) -ge $expire ] && return 1 done cat "$pid_file" } function wait_device () { local tapname=$1; shift local timeout=$1; shift local expire=$(($(date +%s) + $timeout)) while ! ip link show up | egrep -q "^[0-9]+: +$tapname:"; do echo "Waiting for $tapname to come UP...$(($expire - $(date +%s)))s left" >&2 sleep 1 [ $(date +%s) -ge $expire ] && return 1 done return 0 } ######################################## startup function start_db () { get_params "" "$@" ## init conf conf_dir=$(dirname $DB_CONF_FILE) [ -d $conf_dir ] || mkdir -p $conf_dir [ -f $DB_CONF_FILE ] || ovsdb-tool create $DB_CONF_FILE $DB_SCHEMA ## init run [ -d $RUN_DIR ] || mkdir -p $RUN_DIR ## check [ -f $DB_CONF_FILE ] || { echo "Could not initialize $DB_CONF_FILE - exiting" ; exit 1 ; } [ -d $RUN_DIR ] || { echo "Could not initialize $RUN_DIR - exiting" ; exit 1 ; } ## run the stuff ovsdb-server $DB_CONF_FILE \ --remote=punix:$DB_SOCKET \ --remote=db:Open_vSwitch,Open_vSwitch,manager_options \ --private-key=db:Open_vSwitch,SSL,private_key \ --certificate=db:Open_vSwitch,SSL,certificate \ --bootstrap-ca-cert=db:Open_vSwitch,SSL,ca_cert \ --pidfile \ --log-file=$DB_LOG \ --detach >& /dev/null wait_server $RUN_DIR/ovsdb-server.pid ovsdb-server 30 } function start_switch () { get_params "" "$@" # ensure ovsdb-server is running is_db_running || { echo "ovsdb-server not running" >&2 ; exit 1 ; } ovs-vswitchd \ --pidfile \ --log-file=$SWITCH_LOG \ --detach \ unix:$DB_SOCKET >& /dev/null wait_server $RUN_DIR/ovs-vswitchd.pid ovs-vswitchd 30 } function stop_db () { ovs-appctl --target=ovsdb-server exit || : } function stop_switch () { ovs-appctl --target=ovs-vswitchd exit || : } function status () { pids=$(pgrep '^ovs') [ -n "$pids" ] && ps $pids } function start () { start_db start_switch } function stop () { stop_switch stop_db } #################### create functions function create_bridge () { get_params "bridge IP/PREFIX" "$1" "$2" shift; shift; IP=${IP_PREFIX%/*} PREFIX=${IP_PREFIX#*/} W= if ! is_switch_running; then # we can create the bridge even if ovs-vswitchd is not running, # but we need a running ovsdb-server is_db_running || { echo "ovsdb-server not running" >&2; exit 1; } W="--no-wait" fi set -e ovs-vsctl --db=unix:$DB_SOCKET $W -- --may-exist add-br "$bridge" \ -- set bridge "$bridge" datapath_type=planetlab \ -- set interface "$bridge" options:local_ip="$IP" option:local_netmask="$PREFIX" \ -- "$@" # check that the bridge has actually been created if [ -z "$W" ]; then local tap=$(ovs-appctl netdev-pltap/get-tapname "$bridge") wait_device $tap 10 fi } function create_port () { get_params "bridge port" "$@" W= if ! is_switch_running; then # we can create the port even if ovs-vswitchd is not running, # but we need a running ovsdb-server is_db_running || { echo "ovsdb-server not running" >&2; exit 1; } W="--no-wait" fi set -e ovs-vsctl --db=unix:$DB_SOCKET $W -- --may-exist add-port "$bridge" "$port" \ -- set interface "$port" type=tunnel } function get_local_endpoint () { get_params "local_port" "$@" is_switch_running || { echo "ovs-vswitchd not running" >&2; exit 1; } set -e ovs-appctl --target=ovs-vswitchd netdev-tunnel/get-port "$local_port" } function set_remote_endpoint () { get_params "local_port remote_ip remote_UDP_port" "$@" W= if ! is_switch_running; then # we can store the info even if ovs-vswitchd is not running, # but we need a running ovsdb-server is_db_running || { echo "ovsdb-server not running" >&2; exit 1; } W="--no-wait" fi set -e ovs-vsctl --db=unix:$DB_SOCKET $W set interface $local_port \ options:remote_ip=$remote_ip \ options:remote_port=$remote_UDP_port } function set_controller () { get_params "bridge_name controller" "$@" # ensure ovs-vswitchd is running is_switch_running || { echo "ovs-vswitchd not running" >&2 ; exit 1 ; } set -e ovs-vsctl --db=unix:$DB_SOCKET set-controller "$bridge_name" "$controller" } function del_controller () { get_params "bridge_name" "$@" # ensure ovs-vswitchd is running is_switch_running || { echo "ovs-vswitchd not running" >&2 ; exit 1 ; } set -e ovs-vsctl --db=unix:$DB_SOCKET del-controller "$bridge_name" } #################### del functions function del_bridge () { get_params "bridge_name" "$@" W= if ! is_switch_running; then # we can delete the bridge even if ovs-vswitchd is not running, # but we need a running ovsdb-server is_db_running || { echo "ovsdb-server not running" >&2; exit 1; } W="--no-wait" fi ovs-vsctl --db=unix:$DB_SOCKET $W -- --if-exists del-br $bridge_name } function del_port () { get_params "port" "$@" W= if ! is_switch_running; then # we can delete the port even if ovs-vswitchd is not running, # but we need a running ovsdb-server is_db_running || { echo "ovsdb-server not running" >&2; exit 1; } W="--no-wait" fi ovs-vsctl --db=unix:$DB_SOCKET $W -- --if-exists del-port "$port" } function show () { get_params "" "$@" is_db_running || { echo "ovsdb-server not running" >&2; exit 1; } ovs-vsctl --db=unix:$DB_SOCKET show } function get_local_ip () { get_params "bridge" "$@" set -e ovs-vsctl --db=unix:$DB_SOCKET br-exists "$bridge" || return local ip=$(ovs-vsctl get interface "$bridge" options:local_ip) local netmask=$(ovs-vsctl get interface "$bridge" options:local_netmask) eval echo $ip/$netmask } function get_local_links () { get_params "bridge" "$@" set -e ovs-vsctl --db=unix:$DB_SOCKET br-exists "$bridge" || return ovs-vsctl --db=unix:$DB_SOCKET list-ifaces "$bridge" } function get_mac () { get_params "bridge" "$@" set -e local tap=$(ovs-appctl netdev-pltap/get-tapname "$bridge") ifconfig "$tap" | awk '/HWaddr/ { print $5 }' } ### for demos - connect to an ndnmap deployment to visualize links bandwidth # this expects 3 arguments # an interface name, L- based on your ids in conf.mk # the hostname for a ndnmap deployment # a linkid, this is the id that this link has in your ndnmap scenario (hard-coded in some json file) # this one-shot function writes the current statistics onto the ndnmap site # it needs to be called regularly so that ndnmap can do the bw computations # would make sense for the caller to redirect stderr onto some relevant location function gmap_probe_once () { iface=$1; shift hostname=$1; shift linkid=$1; shift rx_bytes=$(ovs-appctl netdev-tunnel/get-rx-bytes $iface) tx_bytes=$(ovs-appctl netdev-tunnel/get-tx-bytes $iface) rx_bits=$(($rx_bytes*8)) tx_bits=$(($tx_bytes*8)) now=$(date +%s).$(date +%N) trigger=http://${hostname}/bw/${linkid}/${now}/${rx_bits}/${tx_bits} # curl -s -L $trigger | grep -q "Got it" || echo Missed event with $trigger # echo $trigger curl -s -L $trigger >& /dev/null } ### the front end, manages pid and so on function gmap_probe () { iface=$1; shift hostname=$1; shift linkid=$1; shift looptime=$1; shift [ -z "$looptime" ] && looptime=1 pid_file=/var/run/openvswitch/gmap-$iface.pid if [ -f $pid_file ] ; then pid=$(cat $pid_file) [ -n "$pid" ] && kill $pid >& /dev/null rm $pid_file fi # close std fds so that ssh invokations can return exec <&- exec >&- while true; do gmap_probe_once $iface $hostname $linkid sleep $looptime done & # this is the pid for the background process echo $! > $pid_file } #################### SUPPORTED_SUBCOMMANDS="start stop status start_db stop_db start_switch stop_switch create_bridge create_port del_bridge del_port show get_local_endpoint set_remote_endpoint set_controller del_controller gmap_probe get_local_ip get_local_links get_mac" function main () { message="Usage: $COMMAND ... Supported subcommands are (dash or underscore is the same): $SUPPORTED_SUBCOMMANDS" [[ -z "$@" ]] && error "$message" SUBCOMMAND=$1; shift # support dashes instead of underscores SUBCOMMAND=$(echo $SUBCOMMAND | sed -e s,-,_,g) found="" for supported in $SUPPORTED_SUBCOMMANDS; do [ "$SUBCOMMAND" = "$supported" ] && found=yes; done [ -z "$found" ] && error $message $SUBCOMMAND "$@" } main "$@"