Index: tools/autorun.sh
===================================================================
--- tools/autorun.sh	(revision 8615)
+++ tools/autorun.sh	(revision 8615)
@@ -0,0 +1,15 @@
+#!/bin/sh
+BASEDIR=`dirname $0`
+PATH=$PATH:/usr/local/bin
+
+if [ `id -u` -ne 0 ]; then
+  echo "ROOT Only!" 
+  exit 1
+fi
+
+cd $BASEDIR
+svn up .
+./genesis-to-yaml.pl */wleiden.conf
+./get-network-status.py force
+
+scp -i $BASEDIR/id_rsa /tmp/nodemap_status.yaml rick@dellas.wirelessleiden.nl:/tmp/nodemap_status_`hostname -s`.yaml
Index: tools/before-commit.sh
===================================================================
--- tools/before-commit.sh	(revision 8615)
+++ tools/before-commit.sh	(revision 8615)
@@ -0,0 +1,26 @@
+#!/bin/sh
+# Check all wleiden.conf files on valid perl syntax for people not familiar
+# with perl to generate right configs
+
+DIRNAME=`dirname $0`
+
+RETVAL=0
+for FILE in ${DIRNAME}/*/wleiden.conf; do 
+  perl -c $FILE
+  if [ $? -ne 0 ]; then
+    RETVAL=1
+  break
+  fi
+done
+
+if [ $RETVAL -eq 0 ]; then
+    sh ${DIRNAME}/dubbelipcheck.sh
+    if [ $? -ne 0 ]; then
+        RETVAL=1
+    fi
+fi
+
+if [ "${RETVAL}" -eq 1 ]; then
+  echo "Please fix errors before commit!"
+  exit 1
+fi
Index: tools/cardgen.sh
===================================================================
--- tools/cardgen.sh	(revision 8615)
+++ tools/cardgen.sh	(revision 8615)
@@ -0,0 +1,19 @@
+#!/bin/sh
+# Parse merged status and make graph
+#
+# Rick van der Zwet <info@rickvanderzwet.nl>
+PATH=$PATH:/usr/local/bin
+
+if [ "`id -un`" != "www" ]; then
+  echo "FOO"
+    exit 1
+fi
+
+DIRNAME=`dirname $0`
+cd $DIRNAME
+svn up .
+rm /tmp/nodemap_status.yaml
+./genesis-to-yaml.pl */wleiden.conf
+./get-network-status.py merge /tmp/nodemap_status_*.yaml
+./make-map.py
+
Index: tools/config-node.sh
===================================================================
--- tools/config-node.sh	(revision 8613)
+++ 	(revision )
@@ -1,491 +1,0 @@
-#!/bin/sh
-#
-# (c) Copyright 2002, 2003, 2005 Stichting Wireless Leiden, all
-#     rights reserved. More information can be found on
-#     http://wwww.wirelessleiden.nl and the license is at:
-#     http://wleiden.webweaving.org:8080/svn/node-config/LICENSE
-#
-# 1.00 
-# ??    Marten Vijn 24-03-03
-# ??    new version 14-11-2003 
-# 1.03  proxy cleanup, detect faulty files, generalize
-#       file list, check node name to be valid, '-n' mode.
-#	make moving of final files a bit safer. (dirkx)
-# 1.04  Add auto read-only detection. (dirkx)
-# 1.05  Cope with WHOST containing a port number. (dirkx).
-# 1.06  Better diff (dirkx)
-# 1.07  Check versions of OS and Script
-#
-# If there is a global system configuration file, suck it in.
-#
-
-PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin
-TMPDIR=${TMPDIR:-/tmp}
-TMPPREFIX=${TMPDIR}/wl-tmp-$$
-WHOST=${WHOST:-rambo.wleiden.net}
-HTTP_PROXY_DEFAULT=${HTTP_PROXY:-http://proxy.wleiden.net:3128}
-HTTP_USER_AGENT=${HTTP_USER_AGENT:-curl.faked.fetch/0.0}
-VERSION=1.07
-QUIET=${QUIET:-}
-PRETEND=no
-FORCE=no
-CMD=do_move
-DLV=no
-
-# Genesis master location.
-link=${GENESIS:-http://${WHOST}/cgi-bin/g_list.pl/}
-list=filelist
-
-# Location for private config
-lcd=${LCDIR:-/lcd}
-dir=${lcd}
-
-FETCH=${FETCH:-/usr/bin/fetch}
-test -e ${FETCH} || FETCH="curl"
-
-if echo ${FETCH} | grep -q curl; then
-	FETCH="${FETCH} --silent"
-else
-	FETCH="${FETCH} -q"
-fi
-
-usage() {
-	echo Usage $0 [-q] [-p proxy] [-d] [-D] [-n] [hostname]
-	echo "-p <proxy>	Set a proxy"
-	echo "-P		Use ${HTTP_PROXY_DEFAULT} as a proxy"
-	echo "-D		Use DHCP to get an address"
-	echo "-n		Show what would happen - but do not do it"
-	echo "-d		Diff met hudiige config (maar doe niets)"
-	echo "-q		Suppress all output and user interaction"
-	echo "-F		Force disk to write"
-	echo "-i		Ignore all safety checks checks"
-	exit 1
-}
-
-for i in $*
-do
-	case "$i"
-	in
-		-D)	connset || exit 1
-			;;
-		-q)
-			QUIET=yes
-			;;
-		-i)
-			DLV=
-			;;
-		-d)	
-			CMD=do_diff
-  			dir=${TMPDIR}
-			;;
-		-p)
-			shift;
-			HTTP_PROXY=$1
-			export HTTP_PROXY
-			;;
-		-p)
-			HTTP_PROXY=HTTP_PROXY_DEFAULT
-			export HTTP_PROXY
-			;;
-		-n)
-			PRETEND=yes
-			;;
-		-F)
-			FORCE=yes
-			;;
-		*)
-			test $# -eq 1 || usage
-			nodename=$i
-			break;
-			;;
-	esac
-	shift
-done
-
-HOST=`echo ${WHOST} | sed -e 's/:.*//'`
-set `echo $VERSION | sed -e 's/\./ /'`
-VERSION_MAJOR=$1
-VERSION_MINOR=$2
-VERSION_OTHER=$3
-
-# connection test function
-connset()
-{
-	if [ `ps ax | grep -c dhclient` != "1" ] ; then
-	        killall dhclient
-	fi
-  
-	echo "Enter an IP address of a nearby Nameserver or use:"
-	echo "  1 to use COPE \(on Wleiden\)"
-	echo "  2 to use XS4All  \(on the internet\)"
-	echo "  3 to use the LCP server \(on the internal LCP networ\)"
-
-	echo -n "IP address or 1/2/3: "
-	read dns_list
-
-	case $dns_list in 
-	1) 
-		resolver="172.17.8.1"
-    		;;
-	2)   
-		resolver="194.109.9.99"
-		;;
-	3)
-		resolver="10.0.0.1"
-		;;
-	*)
-  		resolver=$dns_list 
-  	;;
-	esac
-
-	cp /etc/resolv.conf /etc/resolv.bak || exit 1
-	echo "nameserver ${resolver}" > /etc/resolv.conf
-
-
-	for nic in `ifconfig -l`
-	do
-	case ${nic} in
-	lo0 | wi*) 
-		;;
-  	*) 
-		if ping -qnoc ${HOST}; then
-			echo Connection on interface ${nic} to ${HOST} ok.
-  		else
-			killall dhclient
-			echo Trying to get a DHCP lease on ${nic}
-			dhclient -1 ${nic}
-  		fi
-  		;; 
-  	esac
-	done
-}
-
-log()
-{
-	if [ -z ${QUIET} ]; then  
-  		echo "$*"
-  	fi
-}
-lognlr()
-{
-  	if [ -z ${QUIET} ]; then  
-  		echo -n "$*"
-  	fi
-}
-
-cleanse()
-{
-	rm -f ${TMPPREFIX}.?
-}
-
-# Normal exit; but make sure
-# we also clean up any tmp files
-#
-cleanexit()
-{
-	E=1
-	if [ $# -gt 0 ]; then
-		E=$1
-	fi
-	cleanse
-	log Exit
-	exit $E
-	# Trap any weird exit codes.
-	exit 1
-}
-
-safefetch() 
-{
-	url=$1
-	file=$2
-	${FETCH} -o - ${url} > ${TMPPREFIX}.x \
-		    || cleanexit 1
-
-	# Genesis can provide us with corrupted/empty files
-	# with a 200 OK - so insist that they are at least
-	# a few lines long.
-	#
-	set `wc -l ${TMPPREFIX}.x`
-	if [ $1 -lt 2 ]; then
-		echo File ${link}${nodename} is less than 2 lines long.
-		echo Assuming a problem with Genesis.
-		cleanexit 2
-	fi
-
-	cp ${TMPPREFIX}.x ${file} \
-	    || cleanexit 1
-	rm -f ${TMPPREFIX}.x
-
-	return 0
-}
-
-getvalidnodenames()
-{
-	log Fetching list of nodes from ${link}
-	safefetch ${link} ${nlist} || cleanexit 1
-}
-
-getvalidnodename()
-{
-while ! grep -q "^${nodename}\$" ${nlist}
-do
-  echo Nodes:
-  if [ -x /usr/bin/column ]; then
-	column  ${nlist}
-  else
-  	cat ${nlist}
-  fi
-  echo
-  echo -n enter nodename \[default: ${default}\]:
-  if [ -z ${QUIET} ]; then  
-    read nodename
-  else
-    nodename=${default}
-  fi
-
-  if [ "x${nodename}" = "x" ]; then
-    nodename=${default}
-  fi
-done
-
-echo Node Selected: ${nodename}
-}
-
-
-do_diff() {
-	if test -e $lcd/$1; then
-		diff -uwbB $lcd/$1 $dir/$1.new
-	else
-		echo Warning: $lcd/$1 does not yet exist - no DIFF
-	fi
-}
-
-do_move() {
-	if [ -e $dir/$1 ]; then
-		mv $dir/$1 $dir/$1.bak  || cleanexit 1
-	fi
-	cp $dir/$1.new $dir/$1 || cleanexit 1
-	rm $dir/$1.new || cleanexit 1
-}
-
-linkin() {
-  symdir=$1
-  file=$2
-
-  if [ ${PRETEND} = 'yes' ]; then
-	echo "** $CMD $*"
-   else
-  	$CMD $file || exit 1
-   fi
-
-   test -e $symdir/$file || (
-		echo WARNING: Symlink $symdir/$file not in place.
-		echo use:   ln -s $dir/$file $symdir/$file  
-		echo to fix if appropriate.
-	)
-}
-
-log Config Node -- Version: $VERSION '$Rev$'
-
-# Make sure we clean up our mess when needed.
-trap "rm -f ${TMPPREFIX}.?; echo Failed; exit 1;" 2 3
-
-if [ ${PRETEND} != 'yes' -a ${CMD} != 'do_diff' ]; then
-	if mount | grep "on / " | grep -q read-only; then
-		if [ ${FORCE} = "yes" ]; then
-			echo Forcing read-only disk into rw.
-			fsck / || exit 2
-			mount -o noatime -u -w / || exit 2 || exit 1
-			trap "mount -u -r /; rm -f ${TMPPREFIX}.?; echo Failed; exit 1;" 2 3
-			FORCE=rw
-		else
-			echo ERROR - disk / is mounted read only. Aborting.
-			exit 1
-		fi
-	fi
-fi
-
-export HTTP_USER_AGENT
-export TMPDIR
-
-
-#check config dir
-#
-if  [ ! -d ${dir}  ]; then 
-  mkdir -p ${dir} || cleanexit 1
-fi
-
-if [ -z ${HTTP_PROXY} ]; then
-  	(
-		log Checking DNS for ${HOST}
-		host ${HOST} > /dev/null || exit 1
-		log Checking if ${HOST} can be reached
-		ping -qnoc 1 ${HOST} > /dev/null || exit 1
-		log Connection: Ok
-		exit 0
-	) || connset
-else
-	log Connection not checked because there is an http proxy configured: ${HTTP_PROXY}.
-fi
-
-if [ -r ${lcd}/myname ]; then
-    default=`cat ${lcd}/myname`
-else
-    default=`hostname -s`
-    test -z $default && default=none
-fi
-
-if [ ! -z ${QUIET} ]; then
-	if [ -z ${nodename} ]; then
-		nodename=${default}
-	fi
-fi
-
-
-nlist=${TMPPREFIX}.l
-
-test -z $default && default=$nodename
-
-getvalidnodenames || exit 1
-
-test -z $nodename && getvalidnodename
-
-while test -z $nodename || ! grep -q ${nodename} ${nlist}
-do
-	echo
-	echo Error: Node named \"$nodename\" not known.
-  	if [ -z ${QUIET} ]; then  
-		exit 1
-	fi
-	echo Please select one from the list.
-	echo
-	getvalidnodename
-done
-
-lognlr "Checking release and OS versions: "
-
-safefetch ${link}${nodename}/info $dir/info.last \
-	|| cleanexit 1
-
-OS=`uname -s`
-REL=`uname -r`
-set -- `head -1 $dir/info.last`
-
-# FreeBSD 5.0-RELEASE 1 YES
-if [ $# != 4 ]; then
-	echo Info verification failed.
-	cleanexit 1
-fi
-if [ x$4 != 'xYES' ]; then
-	echo Genesis marked as disabled for this machine.
-	test -z ${DLV} || cleanexit 1
-fi
-if [ 0$3 -ne $VERSION_MAJOR ]; then
-	echo This script is version $VERSION, genesis info is for version $3.xx
-	test -z ${DLV} || cleanexit 1
-fi
-if [ x$1 != x$OS ]; then
-	echo Operating system mismatch; this machine: $OS, but config is for $1
-	test -z ${DLV} || cleanexit 1
-fi
-if [ x$2 != x$REL ]; then
-	echo This machine runs $REL, but the configuration is for $2
-	test -z ${DLV} || cleanexit 2
-fi
-
-log Ok
-
-log Fetching file list from $link for $nodename
-
-safefetch ${link}${nodename} ${dir}/${list}  
-
-lognlr "Fetching:"
-for i in `cat ${dir}/${list}`
-do
-  lognlr " ${i}"
-#  $FETCH -o - ${link}${nodename}/${i} > ${dir}/${i}.new || cleanexit 1
-   safefetch ${link}${nodename}/${i} ${dir}/${i}.new 
-done
-log .
-
-for i in  `cat ${dir}/${list}`
-do
-	case ${i} in
-	linux.sh | config | txtconfig ) 
-		# log obsolete file: ${i} - skipped
-		;; 
-	resolv.conf | rc.node.local | rc.local)
-		linkin /etc ${i}
-		;;
-	snmpd.local.conf)
-		linkin /usr/local/share/snmp ${i}
-		;;
-	named.conf)
-		linkin /etc/namedb ${i}
-		;;
-	dhcpd.conf)
-	 	linkin /usr/local/etc ${i}
-		;;
-	zebra.conf | ospfd.conf)
-        	linkin /usr/local/etc/zebra ${i}
-		;;
-	authorized_keys)
-		linkin /root/.ssh ${i}
-		;;
-	ssh_known_hosts)
-		linkin /etc/ssh ${i}
-		;;
-	daemons.sh)
-		linkin /wl ${i}
-		;;
-	*)
-		echo Script cannot cope with ${i} - ignoring..
-		;;
-	esac
-done
-
-if [ -e /etc/rc.local ]; then
-  # See if we are in rc.local
-  if grep -q /config-node.sh /etc/rc.local; then
-
-    echo As this node now has real configs - do enter a root password
-    echo
-    passwd \
-      || cleanexit 1
-
-    echo Removing /etc/rc.local
-    rm -f /etc/rc.local
-  
-    echo Will drop write perms on the next reboot.
-  fi
-fi
-
-# Record our name.
-echo ${nodename} > ${dir}/myname
-
-# Rebuild reverse lookups
-if test -e /etc/rc.node.local; then
-	H=`cat /etc/rc.node.local | grep hostname | sed -e s/hostname=// | sed -e s/[\"\']//g`
-	hostname $H
-else
-	echo Warning: rc.node.local missing.
-fi
-
-if [ -r  /etc/namedb/make-localhost -a ${CMD} != 'do_diff'  ]; then
-  (
-    cd /etc/namedb || exit 1
-    sh /etc/namedb/make-localhost || exit 1
-  ) || exit 1
-fi
-
-cleanse || exit 1
-
-if [ -e /etc/rc.empty.conf ] ; then
-	rm /etc/rc.empty.conf || exit 1
-	echo removed /etc/rc.empty.conf - and rebooting in 30 seconds \(or press ctrl-C to abort\)
-	read -t 30 DUMMY
-	reboot
-fi
-
-test ${FORCE} = 'rw' && mount -u -r /
-exit 0
Index: tools/configcleaner.pl
===================================================================
--- tools/configcleaner.pl	(revision 8613)
+++ 	(revision )
@@ -1,127 +1,0 @@
-#!/usr/bin/perl -w
-#
-#Tooltje om de wleiden files er generiek uit te laten zien
-#
-#Rick van der Zwet (rick@wirelessleiden.nl)
-use IP;
-
-my $debug = 0;
-
-#maak een mooi systeem met hash aanroepen om snel gegevens te verwerken
-sub parse_config {
-  foreach my $id (sort keys %config) {
-    my $rawData = $config{$id};
-    $rawData =~ s/#.*\n/\n/g;                  #wegslopen comments
-    $rawData =~ s/\s*[\r,\n]+\s*/:::/g;        #spaties+enter+spaties vervangen door :::
-    $rawData =~ s/^::://;                      #::: aan het begin weghalen
-    $rawData =~ s/:::$//;                      #::: aan het eind weghalen
-    my @configArray = split( /:::/, $rawData); #array er van maken
-    foreach $line (@configArray) {
-      (my $name, my $value) = split( /\=/, $line);
-      $$name{$id} = $value;
-    }
-  }
-}
-
-
-sub genericConfig {
-  my $configfile = $_[0];
-  open( CONF, "> $configfile");
-  print CONF <<EOP;
-#
-# Config cleaned up bij configcleaner
-# config for $nodetype$nodename
-#
-\$master_ip='$master_ip';
-\$nodetype='$nodetype';
-\$nodename='$nodename';
-\$status='$status';
-
-\$location="$location";
-\$X='$X';
-\$Y='$Y';
-\$labelpos='$labelpos';
-
-\$OS='$OS';
-
-\$tproxy='$tproxy';
-EOP
-  
-  if(! $static_dhcp[0] eq "" ) {
-    for( my $ctr=0; $ctr < scalar(@static_dhcp); $ctr++ ) {
-      print CONF "\$dhcp_static[$ctr]='$static_dhcp[$ctr]';" . "\n";
-    }
-  }
-
-  foreach my $key (sort keys %config) {
-    my $keyTerminator = $key;
-    $keyTerminator =~ tr/a-z/A-Z/;
-    $keyTerminator =~ s/://;
-    print CONF <<EOP;
-
-\$config{'$key'}=sprintf <<$keyTerminator;
-  IP=$IP{$key}
-  TYPE=$TYPE{$key}
-EOP
-
-    if( lc($TYPE{$key}) eq "wireless" ) {
-      print CONF <<EOP;
-  MODE=$MODE{$key}
-  ESSID=$ESSID{$key}
-  CHANNEL=$CHANNEL{$key}
-EOP
-    }
-
-    print CONF <<EOP;
-  SPEED=$SPEED{$key}
-
-  DESC=$DESC{$key}
-  SDESC=$SDESC{$key}
-
-  POINT_TO_POINT=$POINT_TO_POINT{$key}
-  DHCP=$DHCP{$key}
-EOP
-
-    if( lc($TYPE{$key}) eq "wireless" ) {
-      print CONF <<EOP;
-
-  POLAR=$POLAR{$key}
-  ANTENNA=$ANTENNA{$key}
-  GAIN=$GAIN{$key}
-  DIRECTION=$DIRECTION{$key}
-  CABLE=$CABLE{$key}
-  HEIGHT=$HEIGTH{$key}
-EOP
-    }
-    print CONF "$keyTerminator" . "\n";
-  }
-  print CONF "\n" . "# End of Config File" . "\n";
-  close ( CONF );
-}
-
-
-foreach my $file (@ARGV) {
-  if($debug) {
-    $file_output = "test.conf";
-  }
-  else
-    {$file_output = $file;
-  }
-  print "Working on $file...\n";
-  @$_ = [] foreach qw(static_dhcp);
-  $static_dhcp[0]="";
-  $$_ = "" foreach qw(location master_ip gw_open nodetype nodename
-                      OS status X Y N E OS DISK AGGREGATE);
-  %$_ = () foreach qw(config TYPE IP DESC SDESC SPEED DHCP DHCP_STATIC
-                            OSPF_BROADCAST OSPF_NEIGHBORS MODE ESSID CHANNEL
-                            POLAR ANTENNA GAIN DIRECTION BEAMWIDTH CABLE
-                            HEIGHT ROUTE);
-  do($file) || die("Cann't open file");
-  print "\t Parsing config...";
-  parse_config;
-  print "DONE\n";
-  print "\t Applying generic rules ...";
-  genericConfig($file_output);
-  print "DONE\n";
-}
-
Index: tools/dubbelipcheck.sh
===================================================================
--- tools/dubbelipcheck.sh	(revision 8615)
+++ tools/dubbelipcheck.sh	(revision 8615)
@@ -0,0 +1,84 @@
+#!/bin/sh
+#
+# dubbelipcheck.sh
+# Scriptje om in de wleiden.conf files van Wireless Leiden
+# te controleren of er geen dubbele ip addressen bestaan
+#
+# Rick van der Zwet (rick@wirelessleiden.nl)
+genesis_nodes_dir="`dirname $0`"
+allowed_dubbel_ip="172.31.255.1 172.31.255.2 192.168.1.[0-9]\{1,3\} 0.0.0.0"
+TMPDIR=`mktemp -d -t $(basename $0 .sh).XXXXX`
+
+ERRNO=0
+
+#zoek alle ip address in de files op
+init()
+{
+	files=`find $genesis_nodes_dir/*/wleiden.conf | grep -e "[A-Z]Node\|proxy"`
+	
+	master_ips=`cat $files | grep '^$master_ip' |\
+						sed -e 's/[^0-9]*\([0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\).*$/\1/'`
+	link_ips=`cat $files | grep ^IP | grep -v '$master_ip' |\
+					sed -e 's/IP=//' -e 's/\/[0-9]*//' -e 's/[[:space:]]*$//'`
+        echo "$master_ips $link_ips" | sort 
+	echo "$master_ips $link_ips" | sort > $TMPDIR/all_ips
+	echo "$master_ips $link_ips" | sort -u > $TMPDIR/all_ips_no_double
+}
+
+
+#check up dubbele ip addressen
+dubbelIpCheck() {
+	dubbel_ips=`comm -23 $TMPDIR/all_ips $TMPDIR/all_ips_no_double | sort -u`
+}
+
+
+#verwijder toegestane dubbele ip's
+verwijderToegestaneIps() {
+	for allowed in $allowed_dubbel_ip; do
+		dubbel_ips=`echo $dubbel_ips | sed s/$allowed//`
+	done
+}
+
+
+#print resultaten
+results() {
+	if [ "$dubbel_ips" ]; then
+		for dubbel_ip in $dubbel_ips ; do
+			ERRNO=1
+			echo ""
+			echo "Dubbel ip gevonden: $dubbel_ip"
+			echo "Wordt gebruikt in:"
+			for file in $files ; do
+				grep -v '#' $file | grep "$dubbel_ip/" | grep "^IP\|^$master_ip" > /dev/null && echo "  -$file"
+			done
+		done
+	else
+		echo "Geen dubbele ip's gevonden"
+	fi
+	echo ""
+	echo "Toegestane dubbele ip's:"
+	for ip in $allowed_dubbel_ip ; do
+		echo "   -$ip"
+	done
+}
+
+
+#verwijder tijdelijke files
+cleanup() {
+    rm -R $TMPDIR
+}
+
+
+
+#hoofdmodule
+main() {
+	init
+	dubbelIpCheck
+	verwijderToegestaneIps
+	results
+	cleanup
+}
+
+main
+exit $ERRNO
+
Index: tools/fresh-dns.sh
===================================================================
--- tools/fresh-dns.sh	(revision 8615)
+++ tools/fresh-dns.sh	(revision 8615)
@@ -0,0 +1,45 @@
+#!/bin/sh
+#
+# Deploy fresh/changed WL autogenerated DNS zones
+#
+CONFIGROOT='/usr/home/rvdzwet/genesis/nodes'
+DEPLOYROOT='/etc/namedb/master'
+
+TFILE=`mktemp /tmp/$(basename $0).XXXXX`
+trap "rm $TFILE; exit 1" 1 2 3 15
+
+# Generate new DNS and only complain/die on errors
+(
+  svn up $CONFIGROOT $CONFIGROOT/../dns || exit 1
+  $CONFIGROOT/genesis-to-yaml.pl $CONFIGROOT/*/wleiden.conf || exit 1
+  $CONFIGROOT/gformat.py dns || exit 1
+) 2>&1 > $TFILE || { cat $TFILE; exit 1; }
+
+
+UPDATE_ZONES=""
+# Check all zone files for updates
+for NEW in $CONFIGROOT/dns/*; do
+  ZONE=`basename $NEW | cut -c 4-`  
+  OLD=$DEPLOYROOT/`basename $NEW`
+
+  # Contains new data?
+  diff -I 'SOA' $OLD $NEW
+  if [ $? -eq 0 ]; then
+    continue
+  fi
+
+  # Syntax valid?
+  named-checkzone $ZONE $NEW
+  if [ $? -ne 0 ] ; then
+    continue
+  fi
+
+  # Deploy
+  cp $NEW $OLD
+  UPDATE_ZONES="$UPDATE_ZONES $ZONE"
+done
+
+# Make sure to reload if updates are executed
+if [ -n "$UPDATE_ZONES" ]; then
+  rndc reload
+fi
Index: tools/gen2nag.pl
===================================================================
--- tools/gen2nag.pl	(revision 8615)
+++ tools/gen2nag.pl	(revision 8615)
@@ -0,0 +1,192 @@
+#!/usr/bin/perl
+#
+# Parsen van nagios config uit Genesis
+# by maarten@wirelessleiden.nl
+#
+# v0.7 - Notes directive points to the remote log
+# v0.6 - Traffic interface check. checking via SNMP
+# v0.5 - added args support for location output and genesis and added count to parser
+# v0.4 - handeling proxies
+# v0.3 - added hostgroups on postcode
+# v0.2 - append host ext info and xy for statusmap
+# v0.1 - loops trough all nodes and interfaces
+#
+# Inspired by:
+# - configcleaner.pl by Rick van der Zwet (rick@wirelessleiden.nl)
+# - conf2inc.pl
+
+#maak een mooi systeem met hash aanroepen om snel gegevens te verwerken
+sub parse_config {
+  foreach my $id (sort keys %config) {
+    my $rawData = $config{$id};
+    $rawData =~ s/#.*\n/\n/g;                  #wegslopen comments
+    $rawData =~ s/\s*[\r,\n]+\s*/:::/g;        #spaties+enter+spaties vervangen door :::
+    $rawData =~ s/^::://;                      #::: aan het begin weghalen
+    $rawData =~ s/:::$//;                      #::: aan het eind weghalen
+    my @configArray = split( /:::/, $rawData); #array er van maken
+    foreach $line (@configArray) {
+      (my $name, my $value) = split( /\=/, $line);
+      $$name{$id} = $value;
+    }
+  }
+}
+
+sub generateConfig {
+  my $configfile = $_[0];
+  open( CONF, "> $configfile");
+
+### Pre render variables
+  # make genesis configname needed for naming convention proxy
+  my $configname = $nodetype.$nodename;
+  if ($nodetype eq Proxy) { $configname = lc($nodename)};
+  # get current date
+  $DATE = localtime(time());
+
+  # get all WL IP's for host alive check and exclude aliases
+  my $IPS = $master_ip;
+  foreach my $key (sort keys %config) {
+      if (($key !~ m/(:\d)$/) && ($IP{$key} =~/^172\../)) {
+        $IPS = join(' ',$IPS, substr($IP{$key}, 0, -3));
+      }
+  }
+
+  # sanitize edugis coords for statusmap
+   # X=0 uiterst links zwaluwak edugis x,y 90739,463249 
+   # Y=0 uiterst boven LMkempers1 edugis x,y 107030,471206
+   # X=800 uiterst rechts LMkempers1 edugis x,y 107030,471206
+   # Y=600 uiterst onder PPFortis edugis x,y 104920,458730
+  $X = int(($X-90739)/((107030-90739)/800));
+  $Y = int((471206-$Y)/((471206-458730)/600));
+
+  # Create hostgroups on location postcode
+  my @Alphen = (2445, 2400..2409, 2470, 2471, 2770, 2771, 2420, 2421, 2440, 2441, 2430..2432, 2460, 2461, 1428, 3651..3653, 2435);
+  my @Rijnwoude = (2730, 2731, 2390, 2391, 2394, 2396);
+  my @Oegstgeest = (2223,2340..2343);
+  my @Voorschoten = (2250..2254);
+  my @Jacobswoude = (2452, 2355, 2450, 2451, 2465, 2480, 2481, 1430..1432);
+  my @Leiden = (2159, 2376, 2374, 2377, 2375, 2370, 2371, 2300..2334, 2350..2353, 2200..2204, 2190, 2191, 2210, 2211, 2240..2245, 2735, 2380..2382);
+  $group = "Rest";
+  if ($location =~ m/(\d\d\d\d)/) {
+    for (@Alphen) {if ($_ eq $1) {$group = "Alphen"}}
+    for (@Rijnwoude) {if ($_ eq $1) {$group = "Rijnwoude"}}
+    for (@Oegstgeest) {if ($_ eq $1) {$group = "Oegstgeest"}}
+    for (@Voorschoten) {if ($_ eq $1) {$group = "Voorschoten"}}
+    for (@Jacobswoude) {if ($_ eq $1) {$group = "Jacobswoude"}}
+    for (@Leiden) {if ($_ eq $1) {$group = "Leiden"}}
+    if ($group eq "Rest") {$group = "Onbekend"}
+  }
+
+  # Create hostgroup for proxies
+  if ($nodetype eq "Proxy") { $group = "Proxy"}
+
+### begin config file
+
+  print CONF <<EOP;
+# Config for $nodetype $nodename
+# Generated $DATE from Genesis (by gen2nag.pl)
+#
+# host definitions can be done in hosts.cfg
+# service definitions can be done in services.cfg
+
+define host {
+	host_name	$nodename
+	alias		$nodetype$nodename.wleiden.net
+	use		nodes
+	address		$master_ip
+	check_command	check_alive!$IPS
+	hostgroups	$group
+}
+
+# Host extra info
+define hostextinfo{
+	host_name	$nodename
+	notes 		This node is generated $DATE from genesis
+	notes_url	http://watch.wirelessleiden.nl/logs/$configname
+	icon_image	$nodetype.png
+	icon_image_alt	$nodetype $nodename
+	statusmap_image	$nodetype.gd2
+	2d_coords	$X,$Y
+}
+
+#Service definitions for interfaces
+EOP
+ # loop trough all interfaces for service checks 
+    foreach my $key (sort keys %config) {
+
+	  ### Prerender 
+	  #Strip subnet
+	  $IP{$key} = substr($IP{$key}, 0, -3);
+
+	  # Service checks for all WL interfaces and exclude aliases
+	  if (($key !~ m/(:\d)$/) && ($IP{$key} =~/^172\../)) {
+	    print CONF <<EOP;
+define service {
+	host_name		$nodename
+	use			check-if
+	service_description	Ping $key
+	check_command		check_if_ip!$IP{$key}
+}
+
+define service {
+	host_name		$nodename
+	use			check-if
+	service_description	Traffic $key
+	check_command		check_if_snmp!$key
+}
+
+EOP
+	    # Service definition for wireless interfaces only 
+	    if ( lc($TYPE{$key}) eq "wireless" && 1==0 ) {  # disabled with && 1==0 , not used yet
+      		    print CONF <<EOP;
+# Service definition for wireless interface $key
+EOP
+	    }
+	  }
+    }
+  # close the config file
+  close ( CONF );
+}
+
+# check if any args
+if ($#ARGV < 0 ) {
+  print "Usage: gen2nag.pl outputdir/nagios/config [genesis/localtion(default=.)]\n";
+  exit;
+}
+
+# Set the output dir
+my $OUTPUTDIR=$ARGV[0]; # location for nagios object files
+
+# Set the config dir / The location of genesis
+my $CONFDIR='.';
+if ($#ARGV == 1) {
+  $CONFDIR=$ARGV[1];
+}
+
+opendir(DIR,$CONFDIR) or die $!;
+
+my $count=0;
+foreach(readdir(DIR)) {
+  next unless m/([CH]Node[\w+i]|proxy\d+)/;
+  my $n = $_;
+  foreach ($n){
+    my $file = $CONFDIR.'/'.$n.'/wleiden.conf';
+    next unless -r $file;
+    my $file_output = $OUTPUTDIR.'/'.$n.'.cfg';
+    print "Working on $n...\n";
+    $$_ = "" foreach qw(location master_ip gw_open nodetype nodename
+                        OS status X Y N E OS DISK AGGREGATE);
+    %$_ = () foreach qw(config TYPE IP DESC SDESC SPEED DHCP DHCP_STATIC
+                              OSPF_BROADCAST OSPF_NEIGHBORS MODE ESSID CHANNEL
+                              POLAR ANTENNA GAIN DIRECTION BEAMWIDTH CABLE
+                              HEIGHT ROUTE);
+    do($file) || die $!;#("Can't open file $file");
+    print "\t Parsing config...";
+    parse_config;
+    print "DONE\n";
+    print "\t Generating nagios objectfile...";
+    generateConfig($file_output);
+    print "DONE\n";
+    $count+=1;
+  }
+}
+print "Created nagios configs for $count nodes in directory $OUTPUTDIR\n";
Index: tools/generate-config.py
===================================================================
--- tools/generate-config.py	(revision 8615)
+++ tools/generate-config.py	(revision 8615)
@@ -0,0 +1,82 @@
+#!/usr/bin/env python
+#
+# Bouw een template Wireless Leiden wleiden.conf voor een nieuwe node
+# Rick van der Zwet <info@rickvanderzwet.nl>
+import getrange
+import sys
+
+__version__ = '$Id$'
+
+if len(sys.argv) <> 2:
+    print "Gebruik %s <nodeNaam>" % sys.argv[0]
+    print "Configurator versie: %s" % __version__
+    exit(1)
+
+nodename = sys.argv[1]
+# ALIX/IRIS Boord standaard interfaces
+interfaces = "vr0 vr1 vr2 ue0 ue1".split(' ')
+
+# Initieele IP interface, aannemend dat alles master is
+master_ip = getrange.get_ranges(False, 24, 1)[0] + 1
+interlinks = getrange.get_ranges(True, 29, len(interfaces))
+
+# Maak de boel een beetje standaard
+variables = {
+        'nodename' : nodename[0].upper() + nodename[1:],
+        'nodename_lower' : nodename.lower(),
+        'master_ip' : getrange.showaddr(master_ip),
+        'version' : __version__ }
+
+# Header
+print """
+#
+# Configuratie voor CNode%(nodename)s
+# Configuratie gegenereerd met configurator versie %(version)s
+#
+location : "Locatie XXX, Straat XXX, XXXX XX, Leiden"
+master_ip: '%(master_ip)s'  
+nodetype: 'CNode'
+nodename: '%(nodename)s'
+status: 'up'
+release: "8.0-RELEASE"
+rndap_x: 'XXXXX'
+rNdap_y: 'XXXXXX'
+lon: '4.XXX'
+lat: '52.XXX'
+""" % variables
+
+# Accesspoint
+print """
+iface_ath0:
+  type: wireless
+  ip: %(master_ip)s/26
+  desc: ap
+  sdesc: ap
+  dhcp: 10-60
+  mode: master
+  essid: ap-WirelessLeiden-%(nodename)s
+  channel: 8
+""" % variables
+
+# Alle interfaces
+for index, ifname in enumerate(interfaces):
+    print """
+iface_%(ifname)s:
+  comment: Link naar XXX
+  type: ethernet
+  extra_type: eth2wifibridge
+  ip: %(ip)s/29
+  desc: Link naar XXX
+  sdesc: 2xxx-%(ifname)s
+  mode: XXX
+  essid: XX-XX.%(nodename_lower)s.wleiden.net
+  channel: XX
+  nsmac: 
+  ns_ip: %(ns_ip)s/29
+  dhcp: no
+""" % { 'nodename_lower' : nodename.lower(),
+        'ifname' : ifname,
+        'ifname_caps' : ifname.upper(),
+        'ip' : getrange.showaddr(interlinks[index] + 1),
+        'ns_ip' : getrange.showaddr(interlinks[index] + 2) }
+
Index: tools/genesis-to-py.pl
===================================================================
--- tools/genesis-to-py.pl	(revision 8615)
+++ tools/genesis-to-py.pl	(revision 8615)
@@ -0,0 +1,133 @@
+#!/usr/bin/env perl
+
+eval {
+  require Geo::Coordinates::RDNAP;
+};
+if (! $@) {
+  print "Foo";
+  Geo::Coordinates::RDNAP->import(qw/from_rd to_rd dms/);
+} else {
+  print STDERR "[WARN] Geo::Coordinates::RDNAP not installed\n";
+  sub from_rd {
+    return (0,0,0);
+  }
+}
+
+# RD X,Y -> RD Noorderbreedte, Oosterbreedte
+# http://web.inter.nl.net/users/F.Kissels/gps/conversie.html
+
+# Little perl to convert genesis files into python config files,
+# Which gonna be used for more logic stuff ;-)
+
+$argc = $#ARGV + 1;
+if ($argc == 0) {
+  print STDERR  "Usage $_ <wleiden.conf> ...\n";
+  exit 1;
+# Hack to easy support multiple files without remembering any variable in between
+} elsif ($argc > 1) {
+  foreach $file (@ARGV) {
+     print `perl $ENV{_} $file`;
+  }
+  exit 0;
+}
+
+
+do($ARGV[0]) || die;
+
+my %status_labels = ( up => 'up', down => 'dw', planned => 'pl' );
+
+$interfaces = join(',',keys %config);
+if ( ($X =~ /\d+/) and ($Y =~ /\d+/) and ($X > 10) and ($Y > 10)) {
+    ($lat, $lon, $h) = from_rd( $X, $Y, 0);
+} else {
+    $lat = $lon = $h = 0;
+}
+
+$status = $status_labels{$status} || 'up'; # ensure reporting a correct status
+
+print <<EOF;
+[$nodename]
+location = $location
+status = $status
+latitude = $lat
+longitude = $lon
+interfaces = $interfaces
+x = $X
+y = $Y
+masterip = $master_ip
+nodetype = $nodetype
+name = $nodename
+configtype = node
+
+EOF
+
+
+foreach $if (keys %config) {
+    print <<EOF;
+[$nodename/$if]
+EOF
+    %tmp = (
+        'configtype' => 'interface',
+        'dhcp' => 'no',
+        'compass-direction' => '',
+	'nodename' => $nodename,
+	'interface' => $if,
+        );
+    foreach $line  (split('\n',$config{$if})) {
+        ($key, $value) = split(/=/,$line);
+        if ($key and (lc($key) =~ /^[a-z].*/)) {
+            #print "$key : $value\n";
+            $value = lc($value);
+            if ($key =~ /^type$/i) {
+                if ($value eq 'ethernet') {
+                    $value = 'eth';
+                } else {
+                    $value = '11b';
+                }
+            } elsif ($key =~ /^mode$/i) {
+                if ($value eq 'master') {
+                    $value = 'ms';
+                } else { # managed
+                    $value = 'mn';
+                }
+            } elsif ($key =~ /^polar$/i) {
+                if ($value eq 'hor') {
+                    $value = 'hr';
+                } else {
+                    $value = 'vr';
+                }
+            } elsif ($key =~ /^essid$/i) {
+                $key = 'ssid';
+
+                # Dirty hack to fetch compass direction of essid
+                $value =~ /[a-z]+-([a-z]+)\..*/;
+                $direction = $1;
+                # Translate into English
+                if ($direction ne "omni") {
+                    $direction =~ tr/oz/es/;
+                }
+                # node AMwtrt1 and 'test' directions
+                if ($direction eq "wtrtr" or $direction eq "test") {
+                    $direction = '';
+                }
+                $tmp{'compass-direction'} = $direction;
+            } elsif ($key =~ /^gain$/i) {
+                $value =~ s/dbi//;
+            }
+            $tmp{lc($key)} = $value;
+        }
+    }
+# ethernet2wifi bridges
+    if ( $tmp{extra_type} eq 'eth2wifibridge' ) {
+        $tmp{type} = '11b';
+    }
+# 11a
+    if ( $tmp{type} eq '11b' && $tmp{channel} > 13 ) {
+        $tmp{type} = '11a';
+    }
+
+    foreach $key (keys %tmp) {
+        print "$key=$tmp{$key}\n";
+    }
+    print "\n";
+}
Index: tools/genesis-to-yaml.pl
===================================================================
--- tools/genesis-to-yaml.pl	(revision 8615)
+++ tools/genesis-to-yaml.pl	(revision 8615)
@@ -0,0 +1,150 @@
+#!/usr/bin/env perl
+
+eval {
+  require Geo::Coordinates::RDNAP;
+};
+if (! $@) {
+  Geo::Coordinates::RDNAP->import(qw/from_rd to_rd dms/);
+} else {
+  sub from_rd {
+    return (0,0,0);
+  }
+}
+
+# RD X,Y -> RD Noorderbreedte, Oosterbreedte
+# http://web.inter.nl.net/users/F.Kissels/gps/conversie.html
+
+# Little perl to convert genesis files into python config files,
+# Which gonna be used for more logic stuff ;-)
+
+$argc = $#ARGV + 1;
+if ($argc == 0) {
+  print STDERR  "Usage $_ <wleiden.conf> ...\n";
+  exit 1;
+# Hack to easy support multiple files without remembering any variable in between
+} elsif ($argc > 1) {
+  foreach $file (@ARGV) {
+     $output = $file;
+     $output =~ s/conf$/yaml/;
+     print "# Processing $file\n";
+     `./genesis-to-yaml.pl $file > $output`;
+  }
+  exit 0;
+}
+
+
+do($ARGV[0]) || die;
+
+my %status_labels = ( up => 'up', down => 'dw', planned => 'pl' );
+
+$interfaces = [];
+foreach $if (keys %config) {
+  push(@interfaces, $if) if ($if =~ m/^[a-z0-9]+$/g);
+}
+$interfaces = join(",",@interfaces);
+
+if ( ($X =~ /\d+/) and ($Y =~ /\d+/) and ($X > 10) and ($Y > 10)) {
+    ($lat, $lon, $h) = from_rd( $X, $Y, 0);
+} else {
+    $lat = $lon = $h = 0;
+}
+
+$status = $status_labels{$status} || 'up'; # ensure reporting a correct status
+$comment = $comment || 'None';
+
+print <<EOF;
+# Genesis config yaml style
+# vim:ts=2:et:sw=2:ai
+#
+comment   : $comment
+interfaces: $interfaces
+latitude  : $lat
+location  : $location
+longitude : $lon
+masterip  : $master_ip
+nodename  : $nodename
+nodetype  : $nodetype
+rdnap_x   : $X
+rdnap_y   : $Y
+status    : $status
+EOF
+
+
+foreach $if (keys %config) {
+    ($iflabel, $alias) = split(':',$if);
+    $iflabel .= "_alias${alias}" if ($alias =~ m/[0-9]/);
+    
+    print <<EOF;
+iface_$iflabel:
+EOF
+    %tmp = (
+        'dhcp' => 'False',
+        'compass' => 'None',
+        'interface' => $if,
+        );
+    foreach $line  (split('\n',$config{$if})) {
+        ($key, $value) = split(/=/,$line);
+        # Remove trailing and leading white spaces
+        $value =~ s/^\s+//;
+        $value =~ s/\s+$//;
+        if ($key and (lc($key) =~ /^[a-z].*/)) {
+            #print "$key : $value\n";
+            $value = lc($value) if not lc($key) =~ m/(desc|comment)/;
+            if ($key =~ /^type$/i) {
+                if ($value eq 'ethernet') {
+                    $value = 'eth';
+                } else {
+                    $value = '11b';
+                }
+            } elsif ($key =~ /^mode$/i) {
+                if ($value eq 'master') {
+                    $value = 'ap-wds';
+                } else { # managed
+                    $value = 'station-wds';
+                }
+            } elsif ($key =~ /^polar$/i) {
+                if ($value eq 'hor') {
+                    $value = 'hr';
+                } else {
+                    $value = 'vr';
+                }
+            } elsif ($key =~ /^essid$/i) {
+                $key = 'ssid';
+
+                # Dirty hack to fetch compass direction of essid
+                $value =~ /[a-z]+-([a-z]+)\..*/;
+                $direction = $1;
+                # Translate into English
+                if ($direction ne "omni") {
+                    $direction =~ tr/oz/es/;
+                }
+                # node AMwtrt1 and 'test' directions
+                if ($direction eq "wtrtr" or $direction eq "test") {
+                    $direction = 'None';
+                }
+                if ($direction) {
+                  $tmp{'compass'} = $direction;
+                } else {
+                  $tmp{'compass'} = 'None';
+                }
+            } elsif ($key =~ /^gain$/i) {
+                $value =~ s/dbi//;
+            }
+            $tmp{lc($key)} = $value;
+        }
+    }
+# ethernet2wifi bridges
+    if ( $tmp{extra_type} eq 'eth2wifibridge' ) {
+        $tmp{type} = 'eth';
+    }
+# 11a
+    if ( $tmp{type} eq '11b' && $tmp{channel} > 13 ) {
+        $tmp{type} = '11a';
+    }
+
+    foreach $key (sort keys %tmp) {
+        printf "  %-11s: '$tmp{$key}'\n", $key;
+    }
+    print "\n";
+}
+
Index: tools/get-a-ils.pl
===================================================================
--- tools/get-a-ils.pl	(revision 8615)
+++ tools/get-a-ils.pl	(revision 8615)
@@ -0,0 +1,210 @@
+#!/usr/bin/env perl
+
+# Set some vars
+
+# Filter on netmask
+$MASK = $ARGV[0];
+# Otherwise return all.
+if ( ! $MASK ) {
+  $MASK = ".*";
+}
+
+# Print the header
+print "Node 1 name, Node 1 ip, Node 2 ip, $node 2 name\n";
+
+use File::Basename;
+
+sub parse_config {
+  # Set some vars;
+  my $workingfile = $_[0];
+  my $id = $_[1];
+
+  # Undifine config, to avoid polution
+  undef %config;
+
+  # Load config file
+  do($workingfile);
+
+  # Run through all the interfaces
+  foreach my $if (keys %config) {
+
+    # Config file has interface with semicolumn in it, not possible to use these a vars.
+    $ifwithsemicolumn=$if;
+    $if=~s/://g;
+
+    # Get interface info
+    my $cfg=$config{$ifwithsemicolumn};
+
+    # Run through all the parameters of the interface
+    while ($cfg) {
+
+      # Remove newlines
+      $cfg=~s/^([^\n\r]+)[\r\n]*//m;
+
+      # Get first word from the sentence
+      my $line=$1;
+
+      # Remove unwanted characters
+      $line=~s/\s*#.*//;
+
+      # Split key and values
+      if (((my $name, my $value)=split(/=/,$line)) eq 2) {
+
+        # Create a variable of the splited info
+        my $doit = "\$$if\_$id\{\"$name\"\}=\"$value\";";
+        eval($doit);
+      };
+
+      # Remove newlines
+      $cfg=~s/[\r\n]*$//m;
+    };
+  };
+};
+
+
+sub getneighboril {
+  ## I got an IP, just want to know to which node it belongs
+
+  # Get ip from parent
+  $search=$_[0];
+
+  # Open the config file dir
+  opendir ($il_hdir, $dir);
+
+  # Run through all the Nodes in the specified directory.
+  while (my $il_node = readdir($il_hdir)) {
+
+    # Only Allow Directories which represent Nodes
+    if ($il_node =~ /^CNode/) {
+
+      # Read config file
+      $file="$il_node/$configfile";
+      parse_config("$file", "il");
+ 
+      # Run through all the interfaces  in the config file
+      foreach my $il_if (sort keys %config) {
+
+        # Told you before, can't have semicolumns in variable names
+        $il_if=~s/://g;
+
+        # Get the ipinfo of the interface
+        $il_ip = getvar($il_if, "IP", "il");
+
+        # Split the ip and the subnetmask
+        (my $il_ip, my $il_mask) = split(/\//, $il_ip);
+
+        # Match the current ip of node/if with the ip specified in the search
+        # Match the netmask specified
+        if (($il_ip eq $search) && ($il_mask =~ /$MASK/)) {
+          return $il_node;
+        }
+      }
+    }
+  }
+}
+
+
+
+
+sub getvar {
+  ## Make an var from different variabls
+
+  # Get var from parent
+  $if=$_[0];
+  $var=$_[1];
+  $id=$_[2];
+
+  # Make the var, just forgor why this works :-) (yes I made it myself). Didn't specify the value of the var
+  $retval = eval("\$$if\_$id\{\"$var\"\}");
+  return $retval;
+}
+
+sub getneighborip {
+
+  ## The the ip of the neighboring interface
+
+  # Get var (ip) from parent
+  $ip=$_[0];
+
+  # Split ip into oclets
+  (my $oclet1, my $oclet2, my $oclet3, my $oclet4) = split(/\./, $ip);
+
+  # Mod the 4th oclet
+  $neighbor = $oclet4 % 4;
+
+  # 0 = network address, 1 is what we want, 2 makes double values, 3 is broadcast address
+  if ($neighbor eq 1) {
+
+    # Upper the 4th oclet by one (which makes the 2 if the previous comment 
+    $oclet4++;
+  }
+
+  # Make the neightboring ip address
+  $neighbor="$oclet1.$oclet2.$oclet3.$oclet4";
+
+  # If the neighbor and this ip match, we return zero the block further actions
+  if($neighbor eq $ip) {
+    return NULL;
+  }  
+
+  # Return neighboring ip
+  return $neighbor;
+}
+
+# Get relative path of the genesis config dir
+$dir=dirname(".");
+
+# wleiden.con fconfig file
+$configfile="wleiden.conf";
+
+# Open genesis config directory
+opendir ($main_hdir, $dir);
+
+# Run through all items in this directory
+while (my $main_node = readdir($main_hdir)) {
+
+  # Only open config file which have CNode in the name
+  if ($main_node =~ /^CNode/) {
+    $main_file="$main_node/$configfile";
+    parse_config("$main_file", "main");
+
+    # Run through all the interface in the config file
+    foreach my $main_if (sort keys %config) {
+
+      # Told you before, can't have semicolumns in variable names
+      $main_if=~s/://g;
+
+      # Get the ipinfo of the interface
+      $main_ip = getvar($main_if, "IP", "main");
+
+      # Get the interface type
+      $main_extra_type = getvar($main_if, "EXTRA_TYPE", "main");
+
+      # Seperate ip and subnet mask
+      (my $main_ip, my $main_mask) = split(/\//, $main_ip);
+
+      # We only want /30 netmasks and eth2wifibridges 
+      if(($main_mask eq 30) && ($main_extra_type =~ /eth2wifibridge/i)) {
+
+        # Get the neighboring ip
+        $main_neighbor = getneighborip($main_ip);
+
+        # Disregard if empty.
+        if($main_neighbor) {
+
+          # Search the name of the neighboring Node
+          $main_nodename = getneighboril($main_neighbor);
+
+          # Can be empty for valid reasons, but needs the disregarded
+          if ($main_nodename) {
+             
+             # Print the information we have collected
+             print "$main_node,$main_ip,$main_neighbor,$main_nodename\n";
+          }
+        }
+      }
+    }
+  }
+}
+
+exit 0
Index: tools/get-network-status.py
===================================================================
--- tools/get-network-status.py	(revision 8615)
+++ tools/get-network-status.py	(revision 8615)
@@ -0,0 +1,455 @@
+#!/usr/bin/env python
+# vim:ts=2:et:sw=2:ai
+#
+# Scan Wireless Leiden Network and report status of links and nodes
+#
+# Rick van der Zwet <info@rickvanderzwet.nl>
+
+from pprint import pprint
+from xml.dom.minidom import parse, parseString
+import gformat
+import os.path
+import re
+import subprocess
+import sys
+import time
+import yaml
+from datetime import datetime
+
+# When force is used as argument, use this range
+DEFAULT_SCAN_RANGE= ['172.16.0.0/12']
+
+# Default node status output
+nodemap_status_file = '/tmp/nodemap_status.yaml'
+
+#
+# BEGIN nmap XML parser
+# XXX: Should properly go to seperate class/module
+def get_attribute(node,attr):
+  return node.attributes[attr].value
+
+def attribute_from_node(parent,node,attr):
+  return parent.getElementsByTagName(node)[0].attributes[attr].value
+
+def parse_port(node):
+  item = dict()
+  item['protocol'] = get_attribute(node,'protocol')
+  item['portid'] = get_attribute(node,'portid')
+  item['state'] = attribute_from_node(node,'state','state')
+  item['reason'] = attribute_from_node(node,'state','reason')
+  return item
+
+def parse_ports(node):
+  item = dict()
+  for port in node.getElementsByTagName('port'):
+    port_item = parse_port(port)
+    item[port_item['portid']] = port_item
+  return item
+
+def parse_host(node):
+  # Host status
+  item = dict()
+  item['state'] = attribute_from_node(node,'status','state')
+  item['reason'] = attribute_from_node(node,'status','reason')
+  item['addr'] = attribute_from_node(node,'address','addr') 
+  item['addrtype'] = attribute_from_node(node,'address','addrtype')
+
+  # Service status
+  ports = node.getElementsByTagName('ports')
+  if ports:
+    item['port'] = parse_ports(ports[0])
+  return item
+
+def parse_nmap(root):
+  status = dict()
+  for node in root.childNodes[2].getElementsByTagName('host'):
+    scan = parse_host(node)
+    if not status.has_key(scan['addr']):
+      status[scan['addr']] = scan
+  return status
+#
+# END nmap parser
+#
+
+
+
+def _do_nmap_scan(command, iphosts):
+  """ Run/Read nmap XML with various choices"""
+  command = "nmap -n -iL - -oX - %s" %(command)
+  print "# New run '%s', can take a while to complete" % (command)
+  p = subprocess.Popen(command.split(),
+        stdout=subprocess.PIPE,
+        stderr=subprocess.PIPE,
+        stdin=subprocess.PIPE, bufsize=-1)
+
+  (stdoutdata, stderrdata) = p.communicate("\n".join(iphosts))
+  if p.returncode != 0:
+    print "# [ERROR] nmap failed to complete '%s'" % stderrdata
+    sys.exit(1) 
+
+  dom = parseString(stdoutdata)
+  return (parse_nmap(dom),stdoutdata)
+
+
+
+def do_nmap_scan(command, iphosts, result_file=None, forced_scan=False):
+  """ Wrapper around _run_nmap to get listing of all hosts, the default nmap
+      does not return results for failed hosts"""
+  # Get all hosts to be processed
+  (init_status, stdoutdata) = _do_nmap_scan(" -sL",iphosts)
+
+  # Return stored file if exists
+  if not forced_scan and result_file and os.path.exists(result_file)  \
+    and os.path.getsize(result_file) > 0:
+    print "# Reading stored NMAP results from '%s'" % (result_file)
+    status = parse_nmap(parse(result_file))
+  else:
+    # New scan
+    (status, stdoutdata) = _do_nmap_scan(command, iphosts)
+
+    # Store result if requested
+    if result_file:
+      print "# Saving results in %s" % (result_file)
+      f = file(result_file,'w')
+      f.write(stdoutdata)
+      f.close()
+
+  init_status.update(status)
+  return init_status
+
+
+
+def do_snmpwalk(host, oid):
+   """ Do snmpwalk, returns (p, stdout, stderr)"""
+   # GLobal SNMP walk options
+   snmpwalk = ('snmpwalk -r 0 -t 1 -OX -c public -v 2c %s' % host).split()
+   p = subprocess.Popen(snmpwalk + [oid],
+       stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+   (stdoutdata, stderrdata) = p.communicate()
+   stdoutdata = stdoutdata.split('\n')[:-1]
+   stderrdata = stderrdata.split('\n')[:-1]
+   return (p, stdoutdata, stderrdata)
+
+
+
+
+def do_snmp_scan(iphosts, status, stored_status=dict(), forced_scan=False):
+  """ SNMP scanning, based on results fould in NMAP scan"""
+  mac_to_host = dict()
+  host_processed = dict()
+
+  #
+  # Gather SNMP data from hosts
+  for host in iphosts:
+    # Status might be containing old hosts as well and visa-versa
+    if not status.has_key(host):
+       print "## [ERROR] No nmap result found"
+       continue
+
+    scan = status[host]
+    if scan['state'] != "up":
+      continue
+  
+    print '# Processing host %s' % host
+    # IP -> Mac addresses found in host ARP table, with key IP
+    status[host]['arpmac'] = dict()
+    # MAC -> iface addresses, with key MAC
+    status[host]['mac'] = dict()
+    # Mirrored: iface -> MAC addresses, with key interface name
+    status[host]['iface'] = dict()
+    try:
+      if not forced_scan and stored_status[host]['snmp_retval'] != 0:
+        print "## SNMP Connect failed last time, ignoring"
+        continue
+    except:
+      pass
+  
+    stored_status[host] = dict()
+    if not "open" in scan['port']['161']['state']:
+      print "## [ERROR] SNMP port not opened"
+      continue
+  
+    (p, output, stderrdata) = do_snmpwalk(host, 'SNMPv2-MIB::sysDescr')
+    stored_status[host]['snmp_retval'] = p.returncode
+    # Assume host remain reachable troughout all the SNMP queries
+    if p.returncode != 0:
+       print "## [ERROR] SNMP failed '%s'" % ",".join(stderrdata)
+       continue
+  
+    # Get some host details
+    # SNMPv2-MIB::sysDescr.0 = STRING: FreeBSD CNodeSOM1.wLeiden.NET
+    # 8.0-RELEASE-p2 FreeBSD 8.0-RELEASE-p2 #2: Fri Feb 19 18:24:23 CET 2010
+    # root@80fab2:/usr/obj/nanobsd.wleiden/usr/src/sys/kernel.wleiden i386
+    status[host]['sys_desc'] = output[0]
+    hostname = output[0].split(' ')[4]
+    release = output[0].split(' ')[5]
+    stored_status[host]['hostname'] = status[host]['hostname'] = hostname
+    stored_status[host]['release'] = status[host]['release'] = release
+    print "## %(hostname)s - %(release)s" % stored_status[host]
+  
+    # Check if the host is already done processing
+    # Note: the host is marked done processing at the end
+    if host_processed.has_key(hostname):
+      print "## Host already processed this run"
+      continue  
+    
+    # Interface list with key the index number
+    iface_descr = dict()
+    # IF-MIB::ifDescr.1 = STRING: ath0
+    r = re.compile('^IF-MIB::ifDescr\[([0-9]+)\] = STRING: ([a-z0-9]+)$')
+    (p, output, stderrdata) = do_snmpwalk(host, 'IF-MIB::ifDescr')
+    for line in output:
+      m = r.match(line)
+      iface_descr[m.group(1)] = m.group(2)
+  
+    # IF-MIB::ifPhysAddress[1] = STRING: 0:80:48:54:bb:52
+    r = re.compile('^IF-MIB::ifPhysAddress\[([0-9]+)\] = STRING: ([0-9a-f:]*)$')
+    (p, output, stderrdata) = do_snmpwalk(host, 'IF-MIB::ifPhysAddress')
+    for line in output:
+      m = r.match(line)
+      # Ignore lines which has no MAC address
+      if not m.group(2): continue
+      index = m.group(1)
+      # Convert to proper MAC
+      mac = ":".join(["%02X" % int(x,16) for x in m.group(2).split(':')])
+      if not iface_descr.has_key(index):
+        print "## Index cannot be mapped to a key, available:"
+        for index, value  in iface_descr.iteritems():
+          print "## - %s [%s]" % (value, index)
+      else:
+        print "## Local MAC %s [index:%s] -> %s" % (iface_descr[index], index, mac)
+        status[host]['mac'][mac] = iface_descr[index]
+        status[host]['iface'][iface_descr[index]] = mac
+      mac_to_host[mac] = hostname
+  
+    # Process host SNMP status
+    (p, output, stderrdata) = do_snmpwalk(host, 'RFC1213-MIB::atPhysAddress')
+    # RFC1213-MIB::atPhysAddress[8][1.172.21.160.34] = Hex-STRING: 00 17 C4 CC 5B F2 
+    r = re.compile('^RFC1213-MIB::atPhysAddress\[[0-9]+\]\[1\.([0-9\.]+)\] = Hex-STRING: ([0-9A-F\ ]+)$')
+    for line in output:
+      m = r.match(line)
+      if not m:
+        print "## ERROR Unable to parse '%s'" % line
+        continue
+      ip = m.group(1)
+      # Replace spaces in MAC with :
+      mac = ":".join(m.group(2).split(' ')[:-1])
+      status[host]['arpmac'][ip] = mac
+  
+      local = '[remote]'
+      if mac in status[host]['mac'].keys():
+        local = '[local]'
+      print "## Arp table MAC %s -> %s %s" % (ip, mac, local)
+  
+    # Make sure we keep a record of the processed host which ip entry to check
+    host_processed[hostname] = host
+
+  stored_status['host_processed'] = host_processed
+  stored_status['mac_to_host'] = mac_to_host
+  stored_status['nmap_status'] = status
+  return stored_status
+  
+
+
+
+def generate_status(configs, stored_status):
+  """ Generate result file from stored_status """
+  host_processed = stored_status['host_processed']
+  mac_to_host = stored_status['mac_to_host']
+  status = stored_status['nmap_status']
+
+  # Data store format used for nodemap generation
+  nodemap = { 'node' : {}, 'link' : {}}
+
+  # XXX: Pushed back till we actually store the MAC in the config files automatically
+  #configmac_to_host = dict()
+  #for host,config in configs.iteritems():
+  #  for iface_key in gformat.get_interface_keys(config):
+  #    configmac_to_host[config[iface_key]['mac']] = host
+
+  # List of hosts which has some kind of problem
+  for host in configs.keys():
+     fqdn = host + ".wLeiden.NET"
+     if fqdn in host_processed.keys():
+       continue
+     config = configs[host]
+     print "# Problems in host '%s'" % host
+     host_down = True
+     for ip in gformat.get_used_ips([config]):
+       if not gformat.valid_addr(ip):
+         continue
+       if status[ip]['state'] == "up":
+         host_down = False
+       print "## -  ", ip, status[ip]['state']
+     if host_down:
+       print "## HOST is DOWN!"
+       nodemap['node'][fqdn] = gformat.DOWN
+     else:
+       print "## SNMP problems (not reachable, deamon not running, etc)"
+       nodemap['node'][fqdn] = gformat.UNKNOWN
+
+       
+
+  # Correlation mapping
+  for fqdn, ip in host_processed.iteritems():
+    details = status[ip]
+    nodemap['node'][fqdn] = gformat.OK
+    print "# Working on %s" % fqdn
+    for ip, arpmac in details['arpmac'].iteritems():
+      if arpmac in details['mac'].keys():
+        # Local MAC address
+        continue
+      if not mac_to_host.has_key(arpmac):
+        print "## [WARN] No parent host for MAC %s (%s) found" % (arpmac, ip)
+      else:
+        print "## Interlink %s - %s"  % (fqdn, mac_to_host[arpmac])
+        nodemap['link'][(fqdn,mac_to_host[arpmac])] = gformat.OK
+
+  stream = file(nodemap_status_file,'w')
+  yaml.dump(nodemap, stream, default_flow_style=False)
+  print "# Wrote nodemap status to '%s'" % nodemap_status_file
+
+
+def do_merge(files):
+  """ Merge all external statuses in our own nodestatus, using optimistic approch """
+  try:
+    stream = file(nodemap_status_file,'r')
+    status = yaml.load(stream)
+  except IOError, e:
+    # Data store format used for nodemap generation
+    status = { 'node' : {}, 'link' : {}}
+
+  for cfile in files:
+    try:
+      print "# Merging '%s'" % cfile
+      stream = file(cfile,'r')
+      new_status = yaml.load(stream)
+      for item in ['node', 'link']:
+        for key, value in new_status[item].iteritems():
+          if not status[item].has_key(key):
+            # New items always welcome
+            status[item][key] = value
+            print "## [%s][%s] is new (%s)" % (item, key, value) 
+          elif value < status[item][key]:
+            # Better values always welcome
+            status[item][key] = value
+            print "## [%s][%s] is better (%s)" % (item, key, value) 
+    except IOError, e:
+      print "## ERROR '%s'" % e
+
+  # Save results back to file
+  stream = file(nodemap_status_file,'w')
+  yaml.dump(status, stream, default_flow_style=False)
+
+def usage():
+    print "Usage: %s <arguments>"
+    print "Arguments:"
+    print "\tall         = scan all known ips, using cached nmap"
+    print "\tnmap-only   = scan all known ips, using nmap only"
+    print "\tsnmp-only   = scan all known ips, using snmp only"
+    print "\tforce       = scan all known ips, no cache used"
+    print "\tforced-snmp = scan all known ips, no snmp cache"
+    print "\tstored      = generate status file using stored entries"
+    print "\thost <HOST1> [HOST2 ...]  = generate status file using stored entries"
+    print "\tmerge <FILE1> [FILE2 ...] = merge status file with other status files"
+    sys.exit(0)
+
+
+def main():
+  start_time = datetime.now()
+  stored_status_file = '/tmp/stored_status.yaml'
+  nmap_result_file = '/tmp/test.xml'
+
+  stored_status = dict()
+  nmap_status = dict()
+  snmp_status = dict()
+
+  opt_nmap_scan = True
+  opt_store_scan = True
+  opt_snmp_scan = True
+  opt_force_snmp = False
+  opt_force_scan = False
+  opt_force_range = False
+  if len(sys.argv) == 1:
+    usage()
+
+  if sys.argv[1] == "all":
+    pass
+  elif sys.argv[1] == "nmap-only":
+    opt_snmp_scan = False
+  elif sys.argv[1] == "snmp-only":
+    opt_nmap_scan = False
+  elif sys.argv[1] == "force":
+    opt_force_scan = True
+  elif sys.argv[1] == "forced-snmp":
+    opt_nmap_scan = False
+    opt_force_snmp = True
+  elif sys.argv[1] == "host":
+    opt_force_range = True
+    opt_force_scan = True
+  elif sys.argv[1] == "stored":
+    opt_snmp_scan = False
+    opt_nmap_scan = False
+    opt_store_scan = False
+  elif sys.argv[1] == "merge":
+    do_merge(sys.argv[2:])
+    sys.exit(0)
+  else:
+    usage()
+
+  # By default get all IPs defined in config, else own range
+  if not opt_force_range:
+    configs = gformat.get_all_configs()
+    iplist = gformat.get_used_ips(configs.values())
+  else:
+    iplist = sys.argv[1:]
+
+  # Load data hints from previous run if exists
+  if not opt_force_scan and os.path.exists(stored_status_file) and os.path.getsize(stored_status_file) > 0:
+    print "## Loading stored data hints from '%s'" % stored_status_file
+    stream = file(stored_status_file,'r')
+    stored_status = yaml.load(stream)
+  else:
+    print "[ERROR] '%s' does not exists" % stored_status_file
+
+  # Do a NMAP discovery
+  if opt_nmap_scan:
+    if not opt_store_scan:
+      nmap_result_file = None
+    nmap_status = do_nmap_scan(
+                   "-p T:ssh,U:domain,T:80,T:ntp,U:snmp,T:8080 -sU -sT ",
+                   iplist,nmap_result_file, opt_force_scan)
+
+  else:
+    nmap_status = stored_status['nmap_status']
+
+  # Save the MAC -> HOST mappings, by default as it helps indentifing the
+  # 'unknown links'
+  mac_to_host = {}
+  if stored_status:
+     mac_to_host = stored_status['mac_to_host']
+
+  # Do SNMP discovery
+  if opt_snmp_scan:
+    snmp_status = do_snmp_scan(iplist, nmap_status, stored_status, opt_force_snmp)
+  else:
+    snmp_status = stored_status
+
+  # Include our saved MAC -> HOST mappings
+  mac_to_host.update(snmp_status['mac_to_host'])
+  snmp_status['mac_to_host'] = mac_to_host
+ 
+  # Store changed data to disk 
+  if opt_store_scan:
+    stream = file(stored_status_file,'w')
+    yaml.dump(snmp_status, stream, default_flow_style=False)
+    print "## Stored data hints to '%s'" % stored_status_file
+
+  # Finally generated status
+  generate_status(configs, snmp_status)
+  print "# Took %s seconds to complete" % (datetime.now() - start_time).seconds
+
+
+
+if __name__ == "__main__":
+  main()
Index: tools/getrange.py
===================================================================
--- tools/getrange.py	(revision 8615)
+++ tools/getrange.py	(revision 8615)
@@ -0,0 +1,115 @@
+#!/usr/bin/env python
+# zoek de eerste vrije (== niet in een wleiden.conf gedefinieerde) interlink
+# range
+# lodewijk@wirelessleiden.nl
+import re
+import sys
+import glob
+
+def parseaddr(s):
+	f = s.split('.')
+	return (long(f[0]) << 24L) + \
+		(long(f[1]) << 16L) + \
+		(long(f[2]) << 8L) + \
+		long(f[3])
+
+def showaddr(a):
+	return "%d.%d.%d.%d" % ((a >> 24) & 0xff, (a >> 16) & 0xff, (a >> 8) & 0xff, a & 0xff)
+
+def get_ranges(interlink, size, aantal):
+	numaddrs = 1 << (32 - size)
+	
+	substre = re.compile("^(\$[a-zA-Z][^ =]*) ?= ?'([^']*)';")
+	
+	
+	bezet = {}
+	
+	for fname in glob.glob("*/wleiden.conf"):
+		substs = {}
+		lineNr = 0
+		for l in open(fname):
+			lineNr += 1
+			l = l[:-1]
+			match = substre.match(l)
+			if match != None:
+				substs[match.group(1)] = match.group(2)
+			if l.startswith("IP="):
+				right = l[3:]
+				if '/' not in right:
+					addr = right
+					mask = 32
+				else:
+					addr, mask = right.split('/')
+					if addr in substs:
+						addr = substs[addr]
+				try:
+					addr = parseaddr(addr)
+					mask = int(mask)
+					addr = addr & ~((1 << (32 - mask)) - 1)
+					for i in range(0, (1 << (32 - mask))):
+						bezet[addr + i] = 1
+				except ValueError, e:
+					print "[FOUT] in bestand '%s' regel '%i': %s" % (fname, lineNr, l)
+
+	# Fetching smallest in use IP 
+	# XXX: Currently not used for anything
+	i = parseaddr("255.255.255.255")
+	for k in bezet.keys():
+		if k < i:
+			i = k
+	
+	# Interlinks are living in special ranges usually
+	if interlink:
+		i = parseaddr("172.16.3.0")
+	else:
+		i = parseaddr("172.17.0.0")
+	
+	ranges = []
+	for n in xrange(aantal):
+		okay = False
+		while not okay: 
+			while bezet.has_key(i):
+				i = i + numaddrs
+			
+			# Assume Ok, till we find a already used IP
+			okay = True
+			for j in range(numaddrs):
+				if bezet.has_key(i + j):
+					i = i + numaddrs
+					okay = False
+					break
+		ranges.append(i)
+		bezet[i] = 1
+
+	return ranges
+
+def main():
+	if len(sys.argv) < 2:
+		print "Gebruik: %s <interlink|subnet> <grootte> [aantal]" % sys.argv[0]
+		print "Voorbeelden:"
+		print "\tVoor drie /29 interlinks - '%s interlink 29 3'" % sys.argv[0]
+		print "\tVoor een (1) /24 subnet  - '%s subnet 24'" % sys.argv[0]
+		exit(1)
+	
+	# Argument parsing
+	interlink = sys.argv[1] == 'interlink'
+	size = int(sys.argv[2])
+	try:
+		aantal = int(sys.argv[3])
+	except (KeyError,IndexError):
+	    aantal = 1
+	
+	ranges = get_ranges(interlink, size, aantal)
+	numaddrs = 1 << (32 - size)
+
+	for i in ranges:
+		print "%s/%d:" % (showaddr(i), size),
+		if size > 28:
+			print " en ".join([showaddr(i) for i in range(i + 1, i + numaddrs - 1)])
+		else:
+			print " en ".join([showaddr(i) for i in range(i + 1, i + 4)]),
+			print "...",
+			print " en ".join([showaddr(i) for i in range(i + numaddrs - 7, i + numaddrs - 4)])
+
+if __name__ == "__main__":
+    main()
Index: tools/gformat.py
===================================================================
--- tools/gformat.py	(revision 8615)
+++ tools/gformat.py	(revision 8615)
@@ -0,0 +1,746 @@
+#!/usr/bin/env python
+#
+# vim:ts=2:et:sw=2:ai
+# Wireless Leiden configuration generator, based on yaml files'
+# Rick van der Zwet <info@rickvanderzwet.nl>
+import cgi
+import cgitb
+import copy
+import glob
+import os
+import socket
+import string
+import subprocess
+import sys
+import time
+import make_map
+from pprint import pprint
+try:
+  import yaml
+except ImportError, e:
+  print e
+  print "[ERROR] Please install the python-yaml or devel/py-yaml package"
+  exit(1)
+
+try:
+    from yaml import CLoader as Loader
+    from yaml import CDumper as Dumper
+except ImportError:
+    from yaml import Loader, Dumper
+
+  
+
+NODE_DIR = os.path.dirname(os.path.realpath(__file__))
+__version__ = '$Id$'
+
+
+files = [ 
+    'authorized_keys',
+    'dnsmasq.conf',
+    'rc.conf.local',
+    'resolv.conf',
+    'wleiden.yaml'
+    ]
+
+# Global variables uses
+OK = 10
+DOWN = 20
+UNKNOWN = 90
+
+
+def get_proxylist():
+  """Get all available proxies proxyX sorting based on X number"""
+  os.chdir(NODE_DIR)
+  proxylist = sorted(glob.glob("proxy*"),
+                key=lambda name: int(''.join([c for c in name if c in string.digits])),
+                cmp=lambda x,y: x - y)
+  return proxylist
+
+
+
+def valid_addr(addr):
+  """ Show which address is valid in which are not """
+  return str(addr).startswith('172.')
+
+
+def get_nodelist():
+  """ Get all available nodes - sorted """
+  os.chdir(NODE_DIR)
+  nodelist = sorted(glob.glob("CNode*"))
+  return nodelist
+
+def get_hostlist():
+  """ Combined hosts and proxy list"""
+  return get_nodelist() + get_proxylist()
+
+def angle_between_points(lat1,lat2,long1,long2):
+  """ 
+  Return Angle in radians between two GPS coordinates
+  See: http://stackoverflow.com/questions/3809179/angle-between-2-gps-coordinates
+  """
+  dy = lat2 - lat1
+  dx = math.cos(math.pi/180*lat1)*(long2 - long1)
+  angle = math.atan2(dy,dx)
+  return angle
+
+def angle_to_cd(angle):
+  """ Return Dutch Cardinal Direction estimation in 'one digit' of radian angle """
+
+  # For easy conversion get positive degree
+  degrees = math.degrees(angle)
+  if degrees < 0:
+    360 - abs(degrees)
+
+  # Numbers can be confusing calculate from the 4 main directions
+  p = 22.5
+  if degrees < p:
+    return "n"
+  elif degrees < (90 - p): 
+    return "no"
+  elif degrees < (90 + p): 
+    return "o"
+  elif degrees < (180 - p): 
+    return "zo"
+  elif degrees < (180 + p): 
+    return "z"
+  elif degrees < (270 - p): 
+    return "zw"
+  elif degrees < (270 + p): 
+    return "w"
+  elif degrees < (360 - p): 
+    return "nw"
+  else:
+    return "n"
+
+
+def generate_title(nodelist):
+  """ Main overview page """
+  items = {'root' : "." } 
+  output = """
+<html>
+  <head>
+    <title>Wireless leiden Configurator - GFormat</title>
+    <style type="text/css">
+     th        {background-color: #999999}
+     tr:nth-child(odd)    {background-color: #cccccc}
+     tr:nth-child(even)   {background-color: #ffffff}
+     th, td    {padding: 0.1em 1em}
+    </style>
+  </head>
+  <body>
+    <center>
+    <form type="GET" action="%(root)s">
+      <input type="hidden" name="action" value="update">
+      <input type="submit" value="Update Configuration Database (SVN)">
+    </form>
+    <table>
+     <caption><h3>Wireless Leiden Configurator</h3></caption>
+  """ % items
+
+  for node in nodelist:
+    items['node'] = node
+    output += '<tr><td><a href="%(root)s/%(node)s">%(node)s</a></td>' % items
+    for config in files:
+      items['config'] = config
+      output += '<td><a href="%(root)s/%(node)s/%(config)s">%(config)s</a></td>' % items
+    output += "</tr>"
+  output += """
+    </table>
+    <hr />
+    <em>%s</em>
+    </center>
+  </body>
+</html>
+  """ % __version__
+
+  return output
+
+
+
+def generate_node(node):
+  """ Print overview of all files available for node """
+  return "\n".join(files)
+
+
+
+def generate_header(ctag="#"):
+  return """\
+%(ctag)s 
+%(ctag)s DO NOT EDIT - Automatically generated by 'gformat'
+%(ctag)s Generated at %(date)s by %(host)s
+%(ctag)s 
+""" % { 'ctag' : ctag, 'date' : time.ctime(), 'host' : socket.gethostname() }
+
+
+
+def parseaddr(s):
+  """ Process IPv4 CIDR notation addr to a (binary) number """
+  f = s.split('.')
+  return (long(f[0]) << 24L) + \
+    (long(f[1]) << 16L) + \
+    (long(f[2]) << 8L) + \
+    long(f[3])
+
+
+
+def showaddr(a):
+  """ Display IPv4 addr in (dotted) CIDR notation """
+  return "%d.%d.%d.%d" % ((a >> 24) & 0xff, (a >> 16) & 0xff, (a >> 8) & 0xff, a & 0xff)
+
+
+def is_member(ip, mask, canidate):
+  """ Return True if canidate is part of ip/mask block"""
+  ip_addr = gformat.parseaddr(ip)
+  ip_canidate = gformat.parseaddr(canidate)
+  mask = int(mask)
+  ip_addr = ip_addr & ~((1 << (32 - mask)) - 1)
+  ip_canidate = ip_canidate & ~((1 << (32 - mask)) - 1)
+  return ip_addr == ip_canidate
+ 
+
+
+
+def netmask2subnet(netmask):
+  """ Given a 'netmask' return corresponding CIDR """
+  return showaddr(0xffffffff & (0xffffffff << (32 - int(netmask))))
+
+
+
+def generate_dnsmasq_conf(datadump):
+  """ Generate configuration file '/usr/local/etc/dnsmasq.conf' """
+  output = generate_header()
+  output += """\
+# DHCP server options 
+dhcp-authoritative
+dhcp-fqdn
+domain=dhcp.%(nodename_lower)s.%(domain)s
+domain-needed
+expand-hosts
+
+# Low memory footprint
+cache-size=10000
+  \n""" % datadump
+
+  for iface_key in datadump['iface_keys']:
+    if not datadump[iface_key].has_key('comment'):
+      datadump[iface_key]['comment'] = None
+    output += "## %(interface)s - %(desc)s - %(comment)s\n" % datadump[iface_key]
+
+    try:
+      (dhcp_start, dhcp_stop) = datadump[iface_key]['dhcp'].split('-')
+      (ip, netmask) = datadump[iface_key]['ip'].split('/')
+      datadump[iface_key]['subnet'] = netmask2subnet(netmask)
+    except (AttributeError, ValueError):
+      output += "# not autoritive\n\n"
+      continue
+
+    dhcp_part = ".".join(ip.split('.')[0:3])
+    datadump[iface_key]['dhcp_start'] = dhcp_part + "." + dhcp_start
+    datadump[iface_key]['dhcp_stop'] =  dhcp_part + "." + dhcp_stop
+    output += "dhcp-range=%(interface)s,%(dhcp_start)s,%(dhcp_stop)s,%(subnet)s,24h\n\n" % datadump[iface_key]
+  
+  return output
+
+
+
+def generate_rc_conf_local(datadump):
+  """ Generate configuration file '/etc/rc.conf.local' """
+  output = generate_header("#");
+  output += """\
+hostname='%(nodetype)s%(nodename)s.%(domain)s'
+location='%(location)s' 
+""" % datadump
+  
+  # TProxy configuration
+  output += "\n"
+  try:
+    if datadump['tproxy']:
+      output += """\
+tproxy_enable='YES'
+tproxy_range='%(tproxy)s'
+""" % datadump
+  except KeyError:
+    output += "tproxy_enable='NO'\n"
+  
+  output += '\n'
+  # lo0 configuration:
+  # - 172.32.255.1/32 is the proxy.wleiden.net deflector
+  # - masterip is special as it needs to be assigned to at 
+  #   least one interface, so if not used assign to lo0
+  addrs_list = { 'lo0' : ["127.0.0.1/8", "172.31.255.1/32"] }
+  iface_map = {'lo0' : 'lo0'} 
+
+  masterip_used = False
+  for iface_key in datadump['iface_keys']:
+    if datadump[iface_key]['ip'].startswith(datadump['masterip']):
+      masterip_used = True
+      break
+  if not masterip_used: 
+    addrs_list['lo0'].append(datadump['masterip'] + "/32")
+
+  wlan_count = 0
+  for iface_key in datadump['iface_keys']:
+    ifacedump = datadump[iface_key]
+    interface = ifacedump['interface']
+    # By default no special interface mapping
+    iface_map[interface] = interface
+
+    # Add interface IP to list
+    if addrs_list.has_key(interface):
+      addrs_list[interface].append(ifacedump['ip'])
+    else:
+      addrs_list[interface] = [ifacedump['ip']]
+
+    # Alias only needs IP assignment for now, this might change if we
+    # are going to use virtual accesspoints
+    if "alias" in iface_key:
+      continue
+
+    # XXX: Might want to deduct type directly from interface name
+    if ifacedump['type'] in ['11a', '11b', '11g', 'wireless']:
+      # Create wlanX interface
+      ifacedump['wlanif'] ="wlan%i" % wlan_count 
+      iface_map[interface] = ifacedump['wlanif']
+      wlan_count += 1
+
+      # Default to station (client) mode
+      ifacedump['wlanmode'] = "sta"
+      if ifacedump['mode'] in ['master', 'master-wds']:
+        ifacedump['wlanmode'] = "ap"
+      # Default to 802.11b mode
+      ifacedump['mode'] = '11b'
+      if ifacedump['type'] in ['11a', '11b' '11g']:
+        ifacedump['mode'] = ifacedump['type'] 
+
+      if not ifacedump.has_key('channel'):
+        if ifacedump['type'] == '11a':
+          ifacedump['channel'] = 36
+        else:
+          ifacedump['channel'] = 1
+
+      # Allow special hacks at the back like wds and stuff
+      if not ifacedump.has_key('extra'):
+        ifacedump['extra'] = 'regdomain ETSI country NL'
+
+      output += "wlans_%(interface)s='%(wlanif)s'\n" % ifacedump
+      output += ("create_args_%(wlanif)s='wlanmode %(wlanmode)s mode " +\
+        "%(mode)s ssid %(ssid)s %(extra)s channel %(channel)s'\n") % ifacedump
+        
+    elif ifacedump['type'] in ['ethernet', 'eth']:
+      # No special config needed besides IP
+      pass
+    else:
+      assert False, "Unknown type " + ifacedump['type']
+
+  # Print IP address which needs to be assigned over here 
+  output += "\n"
+  for iface,addrs in sorted(addrs_list.iteritems()):
+    output += "ipv4_addrs_%s='%s'\n" % (iface_map[iface], " ".join(addrs))
+
+  return output
+
+
+
+def get_yaml(item):
+  """ Get configuration yaml for 'item'"""
+  gfile = NODE_DIR + '/%s/wleiden.yaml' % item
+
+  f = open(gfile, 'r')
+  datadump = yaml.load(f,Loader=Loader)
+  f.close()
+
+  return datadump
+
+def store_yaml(datadump):
+  """ Store configuration yaml for 'item'"""
+  gfile = NODE_DIR + '/%s/wleiden.yaml' % item
+
+  f = open(gfile, 'w')
+  f.write(generate_wleiden_yaml(datadump))
+  f.close()
+
+
+
+def get_all_configs():
+  """ Get dict with key 'host' with all configs present """
+  configs = dict()
+  for host in get_hostlist():
+    datadump = get_yaml(host)
+    configs[host] = datadump
+  return configs
+
+
+def get_interface_keys(config):
+  """ Quick hack to get all interface keys, later stage convert this to a iterator """
+  return [elem for elem in config.keys() if (elem.startswith('iface_') and not "lo0" in elem)]
+
+
+def get_used_ips(configs):
+    """ Return array of all IPs used in config files"""
+    ip_list = []
+    for config in configs:
+      ip_list.append(config['masterip'])
+      for iface_key in get_interface_keys(config):
+        l = config[iface_key]['ip']
+        addr, mask = l.split('/')
+        # Special case do not process
+        if valid_addr(addr):
+          ip_list.append(addr)
+        else:
+          print "## IP '%s' in '%s' not valid" % (addr, config['nodename'])
+    return sorted(ip_list)
+
+
+
+def write_yaml(item, datadump):
+  """ Write configuration yaml for 'item'"""
+  gfile = NODE_DIR + '/%s/wleiden.yaml' % item
+
+  f = open(gfile, 'w')
+  f.write(format_wleiden_yaml(datadump))
+  f.close()
+
+
+
+def generate_resolv_conf(datadump):
+  """ Generate configuration file '/etc/resolv.conf' """
+  output = generate_header("#");
+  output += """\
+search wleiden.net
+# Try local (cache) first
+nameserver 127.0.0.1
+
+# Proxies are recursive nameservers 
+# needs to be in resolv.conf for dnsmasq as well
+""" % datadump
+  
+  for proxy in get_proxylist():
+    proxy_ip = get_yaml(proxy)['masterip']
+    output += "nameserver %-15s # %s\n" % (proxy_ip, proxy)
+  return output
+
+
+
+def format_yaml_value(value):
+  """ Get yaml value in right syntax for outputting """
+  if isinstance(value,str):
+    output = "'%s'" % value
+  else:
+    output = value
+  return output 
+
+
+
+def format_wleiden_yaml(datadump):
+  """ Special formatting to ensure it is editable"""
+  output = "# Genesis config yaml style\n" 
+  output += "# vim:ts=2:et:sw=2:ai\n"
+  output += "#\n"
+  iface_keys = [elem for elem in datadump.keys() if elem.startswith('iface_')]
+  for key in sorted(set(datadump.keys()) - set(iface_keys)):
+    output += "%-10s: %s\n" % (key, format_yaml_value(datadump[key]))
+  
+  output += "\n\n"
+  
+  key_order = [ 'comment', 'interface', 'ip', 'desc', 'sdesc', 'mode', 'type',
+    'extra_type', 'channel', 'ssid', 'dhcp' ]
+
+  for iface_key in sorted(iface_keys):
+    output += "%s:\n" % iface_key
+    for key in key_order + list(sorted(set(datadump[iface_key].keys()) - set(key_order))):
+      if datadump[iface_key].has_key(key):
+        output += "  %-11s: %s\n" % (key, format_yaml_value(datadump[iface_key][key])) 
+    output += "\n\n"
+
+  return output
+
+
+
+def generate_wleiden_yaml(datadump):
+  """ Generate (petty) version of wleiden.yaml"""
+  output = generate_header("#")
+  output += format_wleiden_yaml(datadump)
+  return output
+
+
+def generate_yaml(datadump):
+  return generate_config(datadump['nodename'], "wleiden.yaml", datadump)
+  
+
+
+def generate_config(node, config, datadump=None):
+  """ Print configuration file 'config' of 'node' """
+  output = ""
+  try:
+    # Load config file
+    if datadump == None:
+      datadump = get_yaml(node)
+    
+    # Preformat certain needed variables for formatting and push those into special object
+    datadump_extra = copy.deepcopy(datadump)
+    if not datadump_extra.has_key('domain'):
+      datadump_extra['domain'] = 'wleiden.net'
+    datadump_extra['nodename_lower'] = datadump_extra['nodename'].lower()
+    datadump_extra['iface_keys'] = sorted([elem for elem in datadump.keys() if elem.startswith('iface_')])
+
+    if config == 'wleiden.yaml':
+      output += generate_wleiden_yaml(datadump)
+    elif config == 'authorized_keys':
+      f = open("global_keys", 'r')
+      output += f.read()
+      f.close()
+    elif config == 'dnsmasq.conf':
+      output += generate_dnsmasq_conf(datadump_extra)
+    elif config == 'rc.conf.local':
+      output += generate_rc_conf_local(datadump_extra)
+    elif config == 'resolv.conf':
+      output += generate_resolv_conf(datadump_extra)
+    else:
+      assert False, "Config not found!"   
+  except IOError, e:
+    output += "[ERROR] Config file not found"
+  return output
+
+
+
+def process_cgi_request():
+  """ When calling from CGI """
+  # Update repository if requested
+  form = cgi.FieldStorage()
+  if form.getvalue("action") == "update":
+    print "Refresh: 5; url=."
+    print "Content-type:text/plain\r\n\r\n",
+    print "[INFO] Updating subverion, please wait..."
+    print subprocess.Popen(['svn', 'up', NODE_DIR], stderr=subprocess.STDOUT, stdout=subprocess.PIPE).communicate()[0],
+    print "[INFO] All done, redirecting in 5 seconds"
+    sys.exit(0)
+  
+  
+  uri = os.environ['PATH_INFO'].strip('/').split('/')
+  output = ""
+  if not uri[0]:
+    output += "Content-type:text/html\r\n\r\n"
+    output += generate_title(get_hostlist())
+  elif len(uri) == 1:
+    output += "Content-type:text/plain\r\n\r\n"
+    output += generate_node(uri[0])
+  elif len(uri) == 2:
+    output += "Content-type:text/plain\r\n\r\n"
+    output += generate_config(uri[0], uri[1])
+  else:
+    assert False, "Invalid option"
+  print output
+
+def get_fqdn(datadump):
+  # Proxy naming convention is special
+  if datadump['nodetype'] == 'Proxy':
+    fqdn = datadump['nodename']
+  else:
+    # By default the full name is listed and also a shortname CNAME for easy use.
+    fqdn = datadump['nodetype'] + datadump['nodename']
+  return(fqdn)
+  
+  
+
+def make_dns():
+  items = dict()
+
+  # hostname is key, IP is value
+  wleiden_zone = dict()
+  wleiden_cname = dict()
+
+  pool = dict()
+  for node in get_hostlist():
+    datadump = get_yaml(node)
+  
+    # Proxy naming convention is special
+    fqdn = get_fqdn(datadump)
+    if datadump['nodetype'] == 'CNode':
+      wleiden_cname[datadump['nodename']] = fqdn
+
+    wleiden_zone[fqdn] = datadump['masterip']
+
+    # Hacking to get proper DHCP IPs and hostnames
+    for iface_key in get_interface_keys(datadump):
+      iface_name = datadump[iface_key]['interface'].replace(':',"-alias-")
+      (ip, netmask) = datadump[iface_key]['ip'].split('/')
+      try:
+        (dhcp_start, dhcp_stop) = datadump[iface_key]['dhcp'].split('-')
+        datadump[iface_key]['subnet'] = netmask2subnet(netmask)
+        dhcp_part = ".".join(ip.split('.')[0:3])
+        if ip != datadump['masterip']:
+          wleiden_zone["dhcp-gateway-%s.%s" % (iface_name, fqdn)] = ip
+        for i in range(int(dhcp_start), int(dhcp_stop) + 1):
+          wleiden_zone["dhcp-%s-%s.%s" % (i, iface_name, fqdn)] = "%s.%s" % (dhcp_part, i)
+      except (AttributeError, ValueError):
+        # First push it into a pool, to indentify the counter-part later on
+        addr = parseaddr(ip)
+        netmask = int(netmask)
+        addr = addr & ~((1 << (32 - netmask)) - 1)
+        if pool.has_key(addr): 
+          pool[addr] += [(iface_name, fqdn, ip)]
+        else: 
+          pool[addr] = [(iface_name, fqdn, ip)]
+        continue
+
+  # Automatic naming convention of interlinks namely 2 + remote.lower()
+  for (key,value) in pool.iteritems():
+    if len(value) == 1:
+      (iface_name, fqdn, ip) = value[0]
+      wleiden_zone["2unused-%s.%s" % (iface_name, fqdn)] = ip
+    elif len(value) == 2:
+      (a_iface_name, a_fqdn, a_ip) = value[0]
+      (b_iface_name, b_fqdn, b_ip) = value[1]
+      wleiden_zone["2%s.%s" % (b_fqdn,a_fqdn)] = a_ip
+      wleiden_zone["2%s.%s" % (a_fqdn,b_fqdn)] = b_ip
+    else:
+      pool_members = [k[1] for k in value]
+      for item in value:
+        (iface_name, fqdn, ip) = item 
+        pool_name = "2pool-" + showaddr(key).replace('.','-') + "-" + "-".join(sorted(list(set(pool_members) - set([fqdn]))))
+        wleiden_zone["%s.%s" % (pool_name, fqdn)] = ip
+
+  # Include static DNS entries
+  # XXX: Should they override the autogenerated results?
+  # XXX: Convert input to yaml more useable.
+  # Format:
+  ##; this is a comment
+  ## roomburgh=CNodeRoomburgh1
+  ## apkerk1.CNodeVosko=172.17.176.8 ;this as well
+  f = open('../dns/staticDNS.conf','r')
+  for l in f.readlines():
+    # Get rid of comments
+    l = l.strip().split(';')[0].strip()
+    if l:
+      k,v = l.split('=')
+      if valid_addr(v):
+        wleiden_zone[k] = v
+      else:
+        wleiden_cname[k] = v
+  f.close()
+      
+  details = dict()
+  # 24 updates a day allowed
+  details['serial'] = time.strftime('%Y%m%d%H')
+
+  dns_header = '''
+$TTL 3h
+%(zone)s. SOA sunny.wleiden.net. beheer.lijst.wirelessleiden.nl. ( %(serial)s 1d 12h 1w 3h )
+	; Serial, Refresh, Retry, Expire, Neg. cache TTL
+
+	NS	sunny.wleiden.net.
+  \n'''
+
+    
+  if not os.path.isdir('dns'):
+    os.makedirs('dns')
+  details['zone'] = 'wleiden.net'
+  f = open("dns/db." + details['zone'], "w")
+  f.write(dns_header % details)
+
+  for host,ip in wleiden_zone.iteritems():
+    if valid_addr(ip):
+      f.write("%s.wleiden.net. IN A %s \n" % (host, ip)) 
+  for source,dest in wleiden_cname.iteritems():
+    f.write("%s.wleiden.net. IN CNAME %s.wleiden.net.\n" % (source, dest))
+  f.close()
+  
+  # Create whole bunch of specific sub arpa zones. To keep it compliant
+  for s in range(16,32):
+    details['zone'] = '%i.172.in-addr.arpa' % s
+    f = open("dns/db." + details['zone'], "w")
+    f.write(dns_header % details)
+
+    #XXX: Not effient, fix to proper data structure and do checks at other
+    # stages
+    for host,ip in wleiden_zone.iteritems():
+      if valid_addr(ip):
+        if int(ip.split('.')[1]) == s:
+          rev_ip = '.'.join(reversed(ip.split('.')))
+          f.write("%s.in-addr.arpa. IN PTR %s.wleiden.net.\n" % (rev_ip, host)) 
+    f.close()
+
+
+def usage():
+  print """Usage: %s <standalone [port] |test [test arguments]|static|dns>
+Examples:
+\tdns                          =  Generate BIND compliant zone files in dns.
+\tstandalone                   =  Run configurator webserver [default port=8000]
+\tstatic                       =  Generate all config files and store on disk
+\t                                with format ./static/%%NODE%%/%%FILE%%
+\ttest CNodeRick dnsmasq.conf  =  Receive output of CGI script 
+\t                                for arguments CNodeRick/dnsmasq.conf
+"""
+  exit(0)
+
+
+
+def main():
+  """Hard working sub"""
+  # Allow easy hacking using the CLI
+  if not os.environ.has_key('PATH_INFO'):
+    if len(sys.argv) < 2:
+      usage()
+  
+    if sys.argv[1] == "standalone":
+      import SocketServer
+      import CGIHTTPServer
+      try:
+        PORT = int(sys.argv[2])
+      except (IndexError,ValueError):
+        PORT = 8000
+  
+      class MyCGIHTTPRequestHandler(CGIHTTPServer.CGIHTTPRequestHandler):
+        """ Serve this CGI from the root of the webserver """
+        def is_cgi(self):
+          if "favicon" in self.path:
+            return False
+  
+          self.cgi_info = (__file__, self.path)
+          self.path = ''
+          return True
+      handler = MyCGIHTTPRequestHandler
+      httpd = SocketServer.TCPServer(("", PORT), handler)
+      httpd.server_name = 'localhost'
+      httpd.server_port = PORT
+      
+      print "serving at port", PORT
+      httpd.serve_forever()
+    elif sys.argv[1] == "test":
+      os.environ['PATH_INFO'] = "/".join(sys.argv[2:])
+      os.environ['SCRIPT_NAME'] = __file__
+      process_cgi_request()
+    elif sys.argv[1] == "static":
+      items = dict()
+      for node in get_hostlist():
+        items['node'] = node
+        items['wdir'] = "./static/%(node)s" % items
+        if not os.path.isdir(items['wdir']):
+          os.makedirs(items['wdir'])
+        datadump = get_yaml(node)
+        for config in files:
+          items['config'] = config
+          print "## Generating %(node)s %(config)s" % items
+          f = open("%(wdir)s/%(config)s" % items, "w")
+          f.write(generate_config(node, config, datadump))
+          f.close()
+    elif sys.argv[1] == "dns":
+      make_dns()
+    elif sys.argv[1] == "cleanup":    
+      # First generate all datadumps
+      datadumps = dict()
+      for host in get_hostlist():
+        datadump = get_yaml(host)
+        datadumps[get_fqdn(datadump)] = datadump
+      
+        datadump['latitude'], datadump['longitude'] = make_map.rd2etrs(datadump['rdnap_x'], datadump['rdnap_y'])
+        write_yaml(host, datadump)
+    else:                             
+      usage()                         
+  else:                               
+    cgitb.enable()                    
+    process_cgi_request()             
+                                      
+                                  
+if __name__ == "__main__":            
+  main()                              
Index: tools/gindent.py
===================================================================
--- tools/gindent.py	(revision 8615)
+++ tools/gindent.py	(revision 8615)
@@ -0,0 +1,33 @@
+#!/usr/bin/env python
+# vim:ts=2:et:sw=2:ai
+#
+# Build topological network graph
+# Rick van der Zwet <info@rickvanderzwet.nl>
+import gformat
+import sys
+
+__version__ = '$Id$'
+
+def gindent(hosts):
+  """ Pretty indent all files automatically """
+  try:
+    for host in hosts:
+      print "## Processing host %-20s: " % host,
+      datadump = gformat.get_yaml(host)
+      gformat.write_yaml(host, datadump)
+      print "OK"
+  except (KeyError, ValueError), e:
+    print "[ERROR] in '%s' interface '%s'" % (host,iface_key) 
+    print e
+    sys.exit(1)
+
+
+if __name__ == "__main__":
+  if len(sys.argv) == 1:
+    print "Usage: %s <all|host [host2 host3 ...]>" % sys.argv[0]
+    sys.exit(1)
+  elif sys.argv[1] == "all":
+    gindent(gformat.get_proxylist() + gformat.get_nodelist())
+  else:
+    gindent(sys.argv[1:])
+
Index: tools/ip2node.py
===================================================================
--- tools/ip2node.py	(revision 8615)
+++ tools/ip2node.py	(revision 8615)
@@ -0,0 +1,22 @@
+#!/usr/bin/env python
+import os
+import re
+
+def xor(a,b):
+    return (not b and a) or (not a and b) or False
+
+ipre = re.compile('^IP')
+masterre = re.compile('master_ip')
+ipnumber = re.compile('([0-9]{1,3}(\.[0-9]{1,3}){3})')
+
+for file in os.listdir('.'):
+    if file[0:5] == "proxy" or file[0:5] == "CNode":
+        conf = open( file + '/wleiden.conf', 'r')
+        for line in conf.readlines():
+            a =  ipre.search(line)
+            b =  masterre.search(line)
+            if (xor(a,b)):
+                result = ipnumber.search(line).groups()[0]
+                print '%s|%s' % ( result,file)
+        conf.close()
+
Index: tools/ip2node.sh
===================================================================
--- tools/ip2node.sh	(revision 8615)
+++ tools/ip2node.sh	(revision 8615)
@@ -0,0 +1,8 @@
+#!/bin/sh
+for file in `echo  CNode* proxy*`
+do
+    cat $file/wleiden.conf  | grep ^IP | grep -v master |\
+    sed "s#IP=\(.*\)/.*#\1|$file#"
+    cat $file/wleiden.conf  | grep '^$master' |\
+    sed "s#\$master_ip='\(.*\)';#\1|$file#"
+done
Index: tools/make-network-graph.py
===================================================================
--- tools/make-network-graph.py	(revision 8615)
+++ tools/make-network-graph.py	(revision 8615)
@@ -0,0 +1,84 @@
+#!/usr/bin/env python
+# vim:ts=2:et:sw=2:ai
+#
+# Build topological network graph
+# Rick van der Zwet <info@rickvanderzwet.nl>
+import re
+import sys
+import glob
+import tempfile
+import subprocess
+import gformat
+
+OUTFILE = 'network.png'
+
+def make_graph():
+  poel = {}
+  link_type = {}
+  try:
+    for host in gformat.get_hostlist():
+      print "## Processing host", host
+      datadump = gformat.get_yaml(host)
+      iface_keys = [elem for elem in datadump.keys() if (elem.startswith('iface_') and not "lo0" in elem)]
+      for iface_key in iface_keys:
+        l = datadump[iface_key]['ip']
+        addr, mask = l.split('/')
+
+        # Not parsing of these folks please
+        if not gformat.valid_addr(addr):
+          continue
+
+        addr = gformat.parseaddr(addr)
+        mask = int(mask)
+        addr = addr & ~((1 << (32 - mask)) - 1)
+        if poel.has_key(addr): 
+          poel[addr] += [host]
+        else: 
+          poel[addr] = [host]
+          # Assume all eth2wifibridge to be 11a for a moment
+          if datadump[iface_key].has_key('eth2wifibridge'):
+            link_type[addr] = '11a'
+          else:
+            link_type[addr] = datadump[iface_key]['type']
+          print "### %s [%s] is of type %s" % (gformat.showaddr(addr), iface_key, link_type[addr])
+  except (KeyError, ValueError), e:
+    print "[FOUT] in '%s' interface '%s'" % (host,iface_key) 
+    print e
+    sys.exit(1)
+
+  f = tempfile.NamedTemporaryFile(bufsize=0)
+  sys.stderr.write("# Building temponary graph file\n")
+  print >> f, "Graph WirelessLeidenNetwork {"
+  print >> f ,"""
+graph [ fontsize = 36,
+        overlap = scalexy,
+        splines = true,
+      ]
+"""
+  for addr,leden in poel.iteritems():
+    if link_type[addr] == '11a':
+      color = 'red'
+      weight = 4
+    elif link_type[addr] == 'eth':
+      color = 'blue'
+      weight = 8
+    else:
+      color = 'black'
+      weight = 1
+    leden = sorted(set(leden))
+    for index,lid in enumerate(leden[:-1]):
+      for buur in leden[index + 1:]:
+        print >> f,'  %s -- %s [label="%s", color="%s", weight="%s"]' % (lid, buur, gformat.showaddr(addr), color, weight)
+  print >> f, "}"
+  sys.stderr.write("# Plotting temponary graph file using graphviz\n")
+  retval = subprocess.call(["neato","-Tpng",f.name, "-o", OUTFILE])
+  if retval != 0:
+    sys.stderr.write("# FAILED [%i]\n" % retval)
+    subprocess.call(["cat",f.name])
+    exit(1)
+  sys.stderr.write("# COMPLETED find your output in %s\n" % OUTFILE)
+    
+
+if __name__ == "__main__":
+  make_graph()
+
Index: tools/make_map.py
===================================================================
--- tools/make_map.py	(revision 8615)
+++ tools/make_map.py	(revision 8615)
@@ -0,0 +1,262 @@
+#!/usr/bin/env python
+# vim:ts=2:et:sw=2:ai
+#
+# Build topological network graph
+# Rick van der Zwet <info@rickvanderzwet.nl>
+
+import cgi
+import gformat
+import re
+import sys
+import urllib
+import yaml
+import math
+
+def get_yaml(gfile):
+  """ Get configuration yaml for 'item'"""
+  f = open(gfile, 'r')
+  datadump = yaml.load(f)
+  return datadump
+
+def write_yaml(gfile, datadump):
+  """ Write configuration yaml for 'item'"""
+  f = open(gfile, 'w')
+  f.write(yaml.dump(datadump, default_flow_style=False))
+  f.close()
+
+CACHE_FILE = '/tmp/rd2etrs.yaml'
+coordinates = None
+
+def etrs2rd(lam, phi):
+  """ Convert etrs to rd """
+  
+  item['lam'] = lam
+  item['phi'] = phi
+  item['func'] = 'etrs2rd'
+  url = 'http://vanderzwet.net/rdnap/%(func)s/%(xrd)s/%(yrd)s/0/' % item
+
+  f = urllib.urlopen(url)
+  raw = f.read()
+  
+  rdnap_x,rdnap_y,rdnap_h = raw.split('/')
+  return (rdnap_x, rdnap_y)
+
+
+
+def rd2etrs(xrd, yrd, hnap=0.0):
+  """ 
+  Convert rd to etrs 
+  JavaScript Version: https://rdinfo.kadaster.nl/rd/transformator.html
+  """
+  # Get cache is exists
+  global coordinates
+  if coordinates == None:
+    try: 
+      coordinates = get_yaml(CACHE_FILE)
+    except (IOError,AttributeError):
+      coordinates = dict()
+      pass
+
+  # Check if item in cache
+  xrd = float(str(xrd))
+  yrd = float(str(yrd))
+  if coordinates.has_key((xrd, yrd)):
+    return coordinates[(xrd, yrd)]
+
+  # Get new coordinate
+  item = dict()
+  item['xrd'] = xrd
+  item['yrd'] = yrd
+  item['hnap'] = hnap
+  item['func'] = 'rd2etrs'
+  url = 'http://vanderzwet.net/rdnap/%(func)s/%(xrd)s/%(yrd)s/%(hnap)s/' % item
+  print "### Not in Cache, Fetching coordinate %s, %s from %s" % (xrd, yrd, url) 
+  f = urllib.urlopen(url)
+  raw = f.read()
+  
+  lam,phi,h = raw.split('/')
+  coordinates[(xrd, yrd)] = (lam, phi)
+  write_yaml(CACHE_FILE, coordinates)
+  return (lam, phi)
+
+def make_graph():
+  status = None
+  try:
+    stream = file('/tmp/nodemap_status.yaml','r')
+    status = yaml.load(stream)
+  except IOError,e:
+    print "# Error loading status '%s'" % e
+
+  f = open('kmlfile.kml', 'w')
+  f.write("""<?xml version="1.0" encoding="UTF-8"?>
+<kml xmlns="http://earth.google.com/kml/2.0">
+  <Document>
+    <name>WirelessLeiden Nodemap</name>
+    <open>1</open>
+    <description>Generated realtime status of all Wireless Leiden AccessPoints</description>
+    <Style id="node_status_up">
+      <IconStyle>
+        <scale>0.5</scale>
+        <Icon><href>http://www.google.com/mapfiles/kml/paddle/grn-stars-lv.png</href></Icon>  
+      </IconStyle>
+    </Style>
+    <Style id="node_status_down">
+      <IconStyle>
+        <scale>0.5</scale>
+        <Icon><href>http://www.google.com/mapfiles/kml/paddle/red-stars-lv.png</href></Icon>  
+      </IconStyle>
+    </Style>
+    <Style id="node_status_planned">
+      <IconStyle>
+        <scale>0.5</scale>
+        <Icon><href>http://www.google.com/mapfiles/kml/paddle/wht-stars-lv.png</href></Icon>  
+      </IconStyle>
+    </Style>
+    <Style id="node_status_unknown">
+      <IconStyle>
+        <scale>0.5</scale>
+        <Icon><href>http://www.google.com/mapfiles/kml/paddle/wht-stars-lv.png</href></Icon>  
+      </IconStyle>
+    </Style>
+    <Folder>
+      <name>Nodes</name>
+      <visibility>0</visibility>
+      <description>All active nodes and links</description>
+    """)
+
+  poel = {}
+  link_type = {}
+  node = {}
+  
+  nodes = []
+  links = []
+  try:
+    for host in gformat.get_hostlist():
+      fqdn = host + ".wLeiden.NET"
+      print "## Processing host", host
+      datadump = gformat.get_yaml(host)
+      iface_keys = [elem for elem in datadump.keys() if (elem.startswith('iface_') and not "lo0" in elem)]
+      for iface_key in iface_keys:
+        l = datadump[iface_key]['ip']
+        addr, mask = l.split('/')
+
+        addr = gformat.parseaddr(addr)
+        mask = int(mask)
+        addr = addr & ~((1 << (32 - mask)) - 1)
+        if poel.has_key(addr): 
+          poel[addr] += [host]
+        else: 
+          poel[addr] = [host]
+          # Assume all eth2wifibridge to be 11a for a moment
+          iface_parent = '_'.join(iface_key.split('_')[0:2])
+          if datadump[iface_parent].has_key('extra_type') and datadump[iface_parent]['extra_type'] == 'eth2wifibridge':
+            link_type[addr] = '11a'
+          else:
+            link_type[addr] = datadump[iface_parent]['type']
+          print "### %s [%s] is of type %s" % (gformat.showaddr(addr), iface_key, link_type[addr])
+      lam, phi = rd2etrs(datadump['rdnap_x'], datadump['rdnap_y'])
+      node[host] = (lam, phi)
+      if not status:
+        # By default assume up
+        node_status = "up"
+      elif status['node'][fqdn] == gformat.OK:
+        node_status = "up"
+      elif status['node'][fqdn] == gformat.DOWN:
+        node_status = "down"
+      elif status['node'][fqdn] == gformat.UNKNOWN:
+        node_status = "unknown"
+      else:
+        assert False, "Status cannot be generated"
+
+      print "### Node status is '%s'" % node_status
+      f.write("""
+      <description>All active nodes</description>
+      <Placemark>
+        <name>%(nodetype)s %(nodename)s</name>
+        <description>%(desc)s</description>
+        <styleUrl>%(style)s</styleUrl>
+        <Point><coordinates>%(lam)s,%(phi)s,0</coordinates></Point>
+      </Placemark>
+   """ % {'nodetype' : datadump['nodetype'], 'nodename' : datadump['nodename'], 'desc' : cgi.escape(datadump['location']), 'style' : '#node_status_' + node_status, 'lam' : lam, 'phi' : phi})
+      nodes += [("POINT(%s, %s)" % (lam, phi))]
+  except (KeyError, ValueError), e:
+    print "[FOUT] in '%s' interface '%s'" % (host,iface_key) 
+    raise
+    sys.exit(1)
+
+  f.write("""
+    </Folder>
+    <Folder>
+      <name>Links</name>
+      <visibility>0</visibility>
+      <description>All links</description>
+  """)
+  for addr,leden in poel.iteritems():
+    if not gformat.valid_addr(gformat.showaddr(addr)):
+      print "## Adres '%s' met leden '%s' ignored'" % (gformat.showaddr(addr), ','.join(leden))
+      continue
+    if link_type[addr] == '11a':
+      weight = 2
+    elif link_type[addr] == 'eth':
+      weight = 4
+    else:
+      weight = 1
+
+    leden = sorted(set(leden))
+    for index,lid in enumerate(leden[:-1]):
+      for buur in leden[index + 1:]:
+        key = (lid + ".wLeiden.NET", buur + ".wLeiden.NET")
+        rev_key = (key[1],key[0])
+        link_status = None
+        if not status:
+          # Default assume OK
+          link_status = gformat.OK
+        elif status['link'].has_key(key):
+          link_status = status['link'][key]
+        elif status['link'].has_key(rev_key):
+          link_status = status['link'][rev_key]
+        else:
+          # If link is not known assume nothing
+          link_status = gformat.UNKNOWN
+
+
+        if link_status == gformat.OK:
+          color = '#ffff0000' # green
+        elif link_status == gformat.DOWN:
+          color = '#ff0000ff' # red
+        elif link_status == gformat.UNKNOWN:
+          color = '#55000000' # black
+        else:
+          assert False, "Link status not mapped properly"
+
+        f.write("""
+      <Placemark>
+        <name>%(name)s</name>
+        <visibility>0</visibility>
+        <description>%(desc)s</description>
+        <LineString>
+          <tessellate>0</tessellate>
+          <coordinates> %(lam1)s, %(phi1)s, 0
+            %(lam2)s , %(phi2)s, 0 </coordinates>
+        </LineString>
+        <Style>%(style)s</Style>
+      </Placemark>
+      """ % { 'lam1' : node[lid][0], 
+              'phi1' : node[lid][1], 
+              'lam2' : node[buur][0], 
+              'phi2' : node[buur][1],
+              'name' : "Interlink: %s --- %s" % (lid, buur), 
+              'desc' : "%s [%s]" % (gformat.showaddr(addr), link_type[addr]),
+              'style' : "<LineStyle><color>%s</color><width>%s</width></LineStyle>" % (color, weight),
+              })
+  f.write("""
+    </Folder>
+  </Document>
+</kml>
+  """)
+  f.close()
+
+
+if __name__ == "__main__":
+  make_graph()
+
Index: tools/rogue.py
===================================================================
--- tools/rogue.py	(revision 8615)
+++ tools/rogue.py	(revision 8615)
@@ -0,0 +1,50 @@
+# gegeven een lvrouted.mytree, kijk of alle adressen daarin wel in genesis
+# staan.
+
+# lodewijk@wirelessleiden.nl
+import popen2
+import re
+import sys
+
+size = 30
+if len(sys.argv) > 1:
+	size = int(sys.argv[1])
+numaddrs = 1 << (32 - size)
+
+ipre = re.compile("^IP=(172\.16\.[0-9]+\.[0-9]+)/([0-9]+) *")
+
+def parseaddr(s):
+	f = s.split('.')
+	return (long(f[0]) << 24L) + \
+		(long(f[1]) << 16L) + \
+		(long(f[2]) << 8L) + \
+		long(f[3])
+
+def showaddr(a):
+	return "%d.%d.%d.%d" % ((a >> 24) & 0xff, (a >> 16) & 0xff, (a >> 8) & 0xff, a & 0xff)
+
+bezet = {}
+
+(cout, cin) = popen2.popen2("cat */wleiden.conf | grep ^IP | grep 172.16\...*\/[28\|29\|30]")
+cin.close()
+for l in cout.readlines():
+	l = l[:-1]
+	match = ipre.match(l)
+	if match == None:
+		print "'" + l + "'"
+	addr = parseaddr(match.group(1))
+	mask = int(match.group(2))
+	addr = addr & ~((1 << (32 - mask)) - 1)
+	upper = addr + (1 << (32 - mask))
+	while addr < upper:
+		bezet[addr] = 1
+		addr = addr + 1
+
+for line in open('lvrouted.mytree').readlines():
+	line = line.strip()
+	addr = parseaddr(line)
+	if addr > 0xac110000:
+		continue
+	if not bezet.has_key(addr):
+		print line
+
Index: tools/syntax-checker.py
===================================================================
--- tools/syntax-checker.py	(revision 8615)
+++ tools/syntax-checker.py	(revision 8615)
@@ -0,0 +1,53 @@
+#!/usr/bin/env python
+# vim:ts=2:et:sw=2:ai
+#
+# Build topological network graph
+# Rick van der Zwet <info@rickvanderzwet.nl>
+import gformat
+import sys
+
+__version__ = '$Id$'
+
+allowed_multi_use = ['0.0.0.0', '192.168.1.100']
+
+def check_double_ip():
+  pool = {}
+  try:
+    for host in gformat.get_proxylist() + gformat.get_nodelist():
+      print "## Processing host %-20s: " % host,
+      datadump = gformat.get_yaml(host)
+      iface_keys = [elem for elem in datadump.keys() if (elem.startswith('iface_') and not "lo0" in elem)]
+      for iface_key in iface_keys:
+        l = datadump[iface_key]['ip']
+        addr, mask = l.split('/')
+
+        label = "%s - %s" % (host, iface_key)
+        if pool.has_key(addr): 
+          pool[addr] += [label]
+        else: 
+          pool[addr] = [label]
+      print "OK"
+  except (KeyError, ValueError), e:
+    print "[ERROR] in '%s' interface '%s'" % (host,iface_key) 
+    print e
+    sys.exit(1)
+
+  error = False
+  for addr,leden in pool.iteritems():
+    if len(leden) > 1:
+      if not addr in allowed_multi_use:
+        print "[ERROR] Multiple usages of IP %s:" % (addr)
+        print "  -", "\n  - ".join(leden)
+        error = True
+
+  if error:
+    print "# Errors found"
+    return 1
+  else:
+    print "# No multiple usages of IPs found"
+    return 0
+
+
+if __name__ == "__main__":
+  sys.exit(check_double_ip())
+
Index: tools/update-wlconfig
===================================================================
--- tools/update-wlconfig	(revision 8615)
+++ tools/update-wlconfig	(revision 8615)
@@ -0,0 +1,6 @@
+#!/bin/sh
+#
+# Very simple wrapper to update wlconfig.wirelessleiden.nl without the need of
+# a webbrowser
+
+curl 'http://wlconfig.wirelessleiden.nl/freebsd/iris-8.0/g_list.pl?submit=Update'
