#!/bin/sh

#
# Analyze equipment configuration files and chain different phases
# to build the network graph.
#
# Syntax:
#	$0 [-v] [-1] [-t] [eq ... eq]
#
# History :
#   2004/06/08 : pda/jean   : design
#   2004/09/29 : pda/jean   : remove generated files before new generation
#   2006/06/01 : pda/jean   : test snmp community
#   2006/06/19 : pda/boggia : send sensors
#   2007/07/17 : pda        : rancid does not detect cisco access point model
#   2008/10/01 : pda        : add -1 mode ("at least once")
#   2008/11/12 : pda/jean   : add special equipment _vlan
#   2010/11/17 : pda        : adapt list-vlans
#   2010/11/17 : pda/jean   : stop sending sensors
#   2010/11/23 : pda        : remove status file
#   2010/12/18 : pda        : rework installation
#   2012/01/18 : pda        : rancidconfdir + ranciddb -> ranciddir
#

TMP=/tmp/topo.$$
TMPERR=/tmp/topoerr.$$

eval `/usr/local/bin/netmagis-config \
		ranciddir \
		eqvirtdir \
		eqgendir \
		filterdir \
		topograph \
		topobindir \
		checkrouterif \
		ssidsensors \
		topocpgraph \
		`
vlanfile=$eqgendir/_vlan.eq

VERBOSE=0

##############################################################################
# Utility functions
##############################################################################

verbose ()
{
    if [ $VERBOSE != 0 ]
    then echo "$*" >&2
    fi
}

usage ()
{
    echo "usage: anaconf [-v][-1][-t] [eq ...eq]" >&2
    exit 1
}

##############################################################################
# Phases to build the graph
##############################################################################

#
# Get equipment list in the rancid configuration file, and extract
# equipment model from the equipment configuration file.
#

list_rancid ()
{
    verbose "calling 'list-rancid'"
    $topobindir/list-rancid $ranciddir/router.db $ranciddir/configs > $TMP
    return $?
}

#
# Clean-up the generation directory. This handles the case where
# old equipments have been removed from rancid.
#

cleanup_eqgen ()
{
    if [ $# = 0 ]
    then rm -f $eqgendir/*
    fi
    return 0
}

#
# Get vlans list.
#

list_vlans ()
{
    verbose "calling 'list-vlans'"
    $topobindir/list-vlans > $vlanfile
    return $?
}

#
# Extract informations from equipment configuration files
#

analyze_conf ()
{
    if [ $# = 0 ]
    then
	analyze_eq < $TMP
    else
	for eq
	do
	    if [ "$eq" = "_vlan" ]
	    then :
	    elif [ -f "$eqvirtdir/$eq.eq" ]
	    then :
	    elif grep "^$eq " $TMP | analyze_eq
	    then :
	    else echo "$eq not found in list-rancid result" >&2
	    fi
	done
    fi
    return 0
}

#
# Filter to rewrite equipement configuration file.
# Original configuration is simply "piped" to a program
#

filter ()
{
  eqtype=$1
  infile=$2

  # The filter program is called only if it exists in "filterdir" directory ;
  # "filterdir" is a variable defined in the configuration file

  if [ -n "$filterdir" -a -f "$filterdir/$eqtype" ] ; then 

    # The name of the program called is be the same as the equipment type 
    # Example: /usr/lib/netmagis/filters/cisco

    $filterdir/$eqtype < $infile
  else
    cat $infile
  fi
}

analyze_eq ()
{
    r=1				# 1 if no equipment found
    while read name type model
    do
	# remove domain name
	n=`echo $name | sed 's/\..*//'`

	verbose "analyze $name"
	# for an unknown reason, rancid does not detect model for
	# some Cisco access points (not for all)
	if [ x"$model" = x ]
	then model="UNKNOWN"
	fi
	eqconf="/tmp/$name.$$"
	filter $type $ranciddir/configs/$name > $eqconf
	$topobindir/analyze $topobindir $type "$model" $eqconf $n \
	    > $eqgendir/$n.eq
	rm $eqconf
	r=0			# at least one equipment found
    done
    return $r
}

#
# Equipments not managed by rancid, just copy files (virtual files)
#

copy_eqvirt ()
{
    for nameeq in `ls $eqvirtdir | grep '\.eq$'`
    do
	cp $eqvirtdir/$nameeq $eqgendir/$nameeq
    done
    return 0
}

#
# Local policy to get sensors for each ssid
#

ssidsensors ()
{
    if [ "$ssidsensors" = yes ]
    then $topobindir/ssidsensors
    else cat
    fi
}

#
# Graph generation
#

build_graph ()
{
    verbose "graph generation"
    cat $eqgendir/* \
	| ssidsensors \
	| $topobindir/buildgraph > $topograph.tmp \
	    && mv $topograph.tmp $topograph
    return $?
}

#
# Check that SNMP community is specified on all equipments
#

check_snmp ()
{
    verbose "SNMP community check"
    r=0
    WITHOUTSNMP=`$topobindir/dumpgraph < $topograph \
	| sed -n '/^eq .* snmp -$/s/eq \([^ ]*\) .*/\1/p'`
    if [ "$WITHOUTSNMP" != "" ]
    then
	(
	    echo "Warning : equipements without any SNMP community string"
	    echo "$WITHOUTSNMP" | sed 's/^/	/'
	) >&2
	r=1
    fi
    return $r
}

#
# Check that router interfaces are declared in the DNS
# (not activated here)
#

check_dns ()
{
    verbose "checking DNS declaration of router interfaces"
    if [ "checkrouterif" = "yes" ]
    then 
	$topobindir/getnetif < $topograph | $topobindir/checkdns
    fi
    return 0
}

#
# Copy graph to another host if configured
#

copy_graph ()
{
    if [ -n "$topocpgraph" -a -n "$topograph" ]
    then
    	userhost=`echo "$topocpgraph" | cut -f1 -d:`
    	destfile=`echo "$topocpgraph" | cut -f2 -d:`
    	scp -q $topograph $userhost:$destfile.new && \
	    ssh -q $userhost \
	    	"mv $destfile $destfile.old;mv $destfile.new $destfile"
    fi
    return 0
}

##############################################################################
# Phase chaining
##############################################################################

chain ()
{
    list_rancid		\
	&& cleanup_eqgen $*	\
	&& list_vlans		\
	&& analyze_conf $*	\
	&& copy_eqvirt		\
	&& build_graph		\
	&& copy_graph		\
	&& check_snmp		\
	&& check_dns
    return $?
}

execute ()
{
    if [ "$ONCE" = true -o "$TESTONLY" = true ]
    then
	chain $*
	error=$?
    else
	rm -f $TMPERR
	chain $* 2> $TMPERR

	#
	# Distinguish true errors (return code != 0) from inconsistencies
	# detected by various tools (buildgraph for example): the last
	# do not prevent graph building.
	#

	error=$?
	if [ $error != 0 ]
	then
	    if [ -s $TMPERR ]
	    then NEWERR="`cat $TMPERR`"
	    else NEWERR="Unknown error (no error message)"
	    fi
	else
	    if [ -s $TMPERR ]
	    then NEWERR="`cat $TMPERR`"
	    fi
	fi

	#
	# Display warning or error messages
	#

	if [ ! -z "$NEWERR" ]
	then echo "$NEWERR"
	fi
    fi

    return $error
}

##############################################################################
# Main program
##############################################################################

#
# Syntax checking
#

args=`getopt v1 $*`

if [ $? != 0 ]
then usage
fi

set -- $args

TESTONLY=false
for i in $*
do
    case "$i" in
	-v)	VERBOSE=1
		shift
		;;
	-1)	ONCE=true
		shift
		;;
	--)	shift
		break
		;;
    esac
done

if [ $? != 0 ]
then usage
fi

#
# Go!
#

execute $*
error=$?

#
# Exit clean-up
#

rm -f $TMP $TMPERR
exit $error
