Index: branches/exodus-roland/CREDITS.txt
===================================================================
--- branches/exodus-roland/CREDITS.txt	(revision 6402)
+++ branches/exodus-roland/CREDITS.txt	(revision 6402)
@@ -0,0 +1,6 @@
+Send flowers/gifts to the persons below if you like exodus (or just donate some
+money to Stichting Wireless Leiden :-)
+
+Roland van Laar   - initial design, coding
+Rick van der Zwet - coding
+Maarten Rootsaert - trac ticketing tool
Index: branches/exodus-roland/HACKING
===================================================================
--- branches/exodus-roland/HACKING	(revision 6402)
+++ branches/exodus-roland/HACKING	(revision 6402)
@@ -0,0 +1,28 @@
+# In order to be more flexible an have a more consistent view on every page
+# (and avoid repeating code) a little 'framework' has been setup, which is
+# implemented in the folowing way:
+
+The basic is the function GenericHandler() in exodus/views.py which uses the
+default template in exodus/templates/genericForm.html is the base
+implementation of the logic, while extending remind to take a look a the
+implemented classes which shows a variaty of options possible, some hightlights
+which uses the model class FOO as example:
+
+* FOOHandler(GenericHandler): will be the default way to call the extention,
+  this is needed due the fact that more includes/classes are depending on this
+  naming scheme
+* adding/edit/deleting is allowed to be overwritten by the custom class,
+  allowing logic which might needs doing after submitting
+* custom django HTML could be used to extended the genericForm using the
+  following scheme exodus/templates/<action>FOO.html
+
+When it comes of creating config files, place them at
+exodus/templates/<version>/<file> and you will be allowed to call them useing
+the url exodus/config/<version>/node/<file>
+
+Any futher questions? Feel free to ask at the techniek mailinglist at
+techniek@lijst.wirelessleiden.nl
+Web-subscribe: http://lijst.wirelessleiden.nl/mailman/listinfo/techniek
+
+Happy hacking!
+/Rick <rick@wirelessleiden.nl>
Index: branches/exodus-roland/LICENSE
===================================================================
--- branches/exodus-roland/LICENSE	(revision 6402)
+++ branches/exodus-roland/LICENSE	(revision 6402)
@@ -0,0 +1,45 @@
+Copyright (c) 2002-2008 Stichting Wireless Leiden,
+	All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in
+   the documentation and/or other materials provided with the
+   distribution.
+
+3. The end-user documentation included with the redistribution,
+   if any, must include the following acknowledgment:
+      "This product includes software developed by the
+       Stichting Wireless Leiden (http://www.wirelessleiden.nl)."
+   Alternately, this acknowledgment may appear in the software itself,
+   if and wherever such third-party acknowledgments normally appear.
+
+4. The name "Wireless Leiden" or "Stiching Wireless Leiden" must
+   not be used to endorse, create or promote products derived from 
+   this software without prior written permission. For written
+   permission, please contact bestuur@wirelessleiden.nl
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED.  IN NO EVENT SHALL THE STICHTING WIRELESS LEIDEN, ITS
+MEMBERS, THE SOFTWARE AUTHORS OR ITS CONTRIBUTORS BE LIABLE FOR 
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
+IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
+POSSIBILITY OF SUCH DAMAGE.
+
+======================================================================
+
+This software consists of voluntary contributions made by many
+individuals on behalf of the Stichting Wireless Leiden.  For more
+information see http://www.wirelessleiden.nl/
Index: branches/exodus-roland/Makefile
===================================================================
--- branches/exodus-roland/Makefile	(revision 6402)
+++ branches/exodus-roland/Makefile	(revision 6402)
@@ -0,0 +1,49 @@
+SQLDB="exodus/exodus.db"
+
+help:
+#Usage
+	@grep -e '[a-z\-]:' -e '^#' Makefile | sed -e 's/:.*/:/g' | tr '\n' '@' |\
+	 sed -e 's/@#/ /g' -e 's/@$$//g' | tr '@' '\n' |\
+	 sed -e 's/^/make /g'
+
+init: ./bin/buildout
+#Setup environment database
+	@./bin/buildout
+	@echo "no" | ./bin/django syncdb
+
+./bin/buildout:
+#Setup environment database - part creation
+	@python bootstrap.py
+
+
+debug: init
+#Run server in extra debugging mode
+	@./bin/django runserver_plus
+
+run: init
+#Run server in normal power mode
+	@./bin/django runserver
+
+
+debug-init: init
+#Put debug.sql in database
+	@sqlite3 $(SQLDB) < debug.sql
+
+clean:
+#Remove all created data, development ground, but keep downloaded files
+	@rm -f $(SQLDB)
+	@rm -Rf develop-eggs eggs parts .installed.cfg bin
+
+dist-clean: clean
+#Remove all created data, development ground and downloaded files
+	@rm -Rf downloads
+
+test: init
+# Testing suite
+	@./bin/test
+
+new: clean debug-init debug
+#Fresh start, with new datebase which include debugging code
+
+batch: clean debug-init run
+#Intended usage for batch runs only e.g. no debug server
Index: branches/exodus-roland/README
===================================================================
--- branches/exodus-roland/README	(revision 6402)
+++ branches/exodus-roland/README	(revision 6402)
@@ -0,0 +1,32 @@
+See LICENSE for the license details; Exodus falls under this
+license.  
+Roland van Laar email: roland@wirelessleiden.nl
+
+= Directory/File layout =
+doc              = documentation files e.g. papers and transcripts
+exodus           = exodus module
+exodus/static    = exodus static files
+exodus/templates = exodus template files
+
+= Hacking =
+Buildout is a tool that automagically builds a development environment
+for you.
+
+Python package python-setuptools is required.
+
+To build the development environment:
+$ cd $SVNTRUNK
+$ python bootstrap.py
+$ bin/buildout
+
+To create the database:
+$ cd $SVNTRUNK
+$ make init
+
+To run the developmentserver do:
+$ cd $SVNTRUNK
+$ make debug
+
+To run unittests do:
+$ cd $SVNTRUNK
+$ bin/test
Index: branches/exodus-roland/base.cfg
===================================================================
--- branches/exodus-roland/base.cfg	(revision 6402)
+++ branches/exodus-roland/base.cfg	(revision 6402)
@@ -0,0 +1,45 @@
+[buildout]
+parts = 
+  extras
+  django
+  testrunner
+versions = versions
+
+[versions]
+djangorecipe=0.12.1
+zc.recipe.egg=1.0.0
+zc.recipe.testrunner=1.0.0
+
+[testrunner]
+recipe = zc.recipe.testrunner
+eggs = 
+  ${django:eggs}
+extra-paths = 
+  ${django:pythonpath}
+  ${buildout:directory}
+  ${django:location}
+working-directory = ${buildout:directory}
+script = test
+defaults = ['--path', 'exodus',
+            '--suite-name', 'suite' ]
+initialization = 
+  os.environ['DJANGO_SETTINGS_MODULE'] = 'exodus.development'
+
+[extras]
+recipe = iw.recipe.subversion
+urls =
+  http://django-command-extensions.googlecode.com/svn/trunk@135 django-command-extensions
+
+[django]
+recipe = djangorecipe
+version = 1.0.2
+project = exodus
+wsgi = true
+eggs =
+  Werkzeug
+pythonpath = 
+  ${extras:location}/django-command-extensions/
+
+[supervisor]
+recipe = zc.recipe.egg
+eggs = supervisor
Index: branches/exodus-roland/bootstrap.py
===================================================================
--- branches/exodus-roland/bootstrap.py	(revision 6402)
+++ branches/exodus-roland/bootstrap.py	(revision 6402)
@@ -0,0 +1,52 @@
+##############################################################################
+#
+# Copyright (c) 2006 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Bootstrap a buildout-based project
+
+Simply run this script in a directory containing a buildout.cfg.
+The script accepts buildout command-line options, so you can
+use the -c option to specify an alternate configuration file.
+
+$Id$
+"""
+
+import os, shutil, sys, tempfile, urllib2
+
+tmpeggs = tempfile.mkdtemp()
+
+ez = {}
+exec urllib2.urlopen('http://peak.telecommunity.com/dist/ez_setup.py'
+                     ).read() in ez
+ez['use_setuptools'](to_dir=tmpeggs, download_delay=0)
+
+import pkg_resources
+
+cmd = 'from setuptools.command.easy_install import main; main()'
+if sys.platform == 'win32':
+    cmd = '"%s"' % cmd # work around spawn lamosity on windows
+
+ws = pkg_resources.working_set
+assert os.spawnle(
+    os.P_WAIT, sys.executable, sys.executable,
+    '-c', cmd, '-mqNxd', tmpeggs, 'zc.buildout',
+    dict(os.environ,
+         PYTHONPATH=
+         ws.find(pkg_resources.Requirement.parse('setuptools')).location
+         ),
+    ) == 0
+
+ws.add_entry(tmpeggs)
+ws.require('zc.buildout')
+import zc.buildout.buildout
+zc.buildout.buildout.main(sys.argv[1:] + ['bootstrap'])
+shutil.rmtree(tmpeggs)
Index: branches/exodus-roland/buildout.cfg
===================================================================
--- branches/exodus-roland/buildout.cfg	(revision 6402)
+++ branches/exodus-roland/buildout.cfg	(revision 6402)
@@ -0,0 +1,5 @@
+[buildout]
+extends = base.cfg
+
+[django]
+settings = development
Index: branches/exodus-roland/debug.sql
===================================================================
--- branches/exodus-roland/debug.sql	(revision 6402)
+++ branches/exodus-roland/debug.sql	(revision 6402)
@@ -0,0 +1,3 @@
+INSERT INTO exodus_location VALUES ( 1, 'Zoeterwoude', 10, 30);
+INSERT INTO exodus_node VALUES ( 1, 'Zwet', 1, 1, '172.16.1.2', 1);
+INSERT INTO exodus_node VALUES ( 2, 'Laar', 1, 1, '172.16.2.2', 1);
Index: branches/exodus-roland/doc/README
===================================================================
--- branches/exodus-roland/doc/README	(revision 6402)
+++ branches/exodus-roland/doc/README	(revision 6402)
@@ -0,0 +1,19 @@
+= Directory/File layout =
+
+django.conf         = apache configuration file for django virtual host
+exodus_models.dot   = graphviz file of the current database structure
+exodus_models.pdf   = pdf from graphviz file
+exodus_mysql        = original database structure 
+examples/           = example configuration files from genesis
+
+= Generate exodusmodels.dot =
+
+exodusmodels.dot is a graphviz file which shows the exodus database.
+
+Using the djangotrunk execute:
+$ ./bin/django graph_models exodus > exodusmodels.dot
+
+= Debug Django =
+
+Using werkzeug:
+$ ./bin/django runserver_plus 
Index: branches/exodus-roland/doc/discussion-08-09-2008.txt
===================================================================
--- branches/exodus-roland/doc/discussion-08-09-2008.txt	(revision 6402)
+++ branches/exodus-roland/doc/discussion-08-09-2008.txt	(revision 6402)
@@ -0,0 +1,78 @@
+Talkthrough September 8th 2008
+Attendees: Roland van Laar, Rick van der Zwet
+Goal: Streamline Exodus development, documenting
+
+* URL - OK
+- urls will represent config which needs to be generated
+- Cool URLs do not change
+- URL represent function/state of exodus
+- config files:
+* exodus/config/freebsd-5.0/FooBar/named.conf
+* exodus/config/<version>/<node>/<file>
+
+exodus/add/node
+exodus/add/link (one link)
+...
+
+(confirmation view)
+exodus/delete/node/<node> (recursive, but location)
+exodus/delete/link/<linkid>
+...
+
+exodus/edit/node/<node>
+exodus/edit/link/<node> (all links)
+...
+
+exodus/nodelist -> exodus/view/nodelist (view url, geo-info, nodelist)
+exodus/<node>/node -> exodus/view/node/<node> (generic overview)
+
+* GRAPHICAL VIEW
+- Box and EDIT buttons
+
+* PYTHON FILES/DIRECTORES - OK
+- merge url logic into view logic.
+- split files to match the underlying logic, like on version and templates a
+  subdir version number
+
+* PLAIN VIEW TEMPLATES TO MANY SPACES/RETURN - OK
+- Config file, should be readable, really (genesis style) pretty printing not
+  required due automatic configuration
+
+
+* DATABASE - OK
+- node -> network -> dnsserver
+- seperate dnsserver
+- owner -> network possible if XS is going to happen
+
+* INTERLINK - OK
+- Nic -*> Link -> LinkPool
+- Nic -*> Link -> PublicAP
+- Choice ssid/channel PublicAP precedence
+- Link needed due desc,ip,etc
+- LinkPool, linkID to Link to indentify master
+
+* ACCESS MANAGMENT - OK
+Users:
+SSHKey
+Name:
+username:
+password:
+group -*> Group
+Node -*> Node
+
+Group:
+name
+Network -*> Network
+Node -*> Node
+
+* SITEBAR MENU -
+- Home
+- Add Node
+- View Nodes
+
+* OTHER
+# What to do with statuses? Nodes only planned, up, down.
+  No need for sql table anymore, models.py only
+# API? JSON/XML (2nd round, version 1.x)
+
+
Index: branches/exodus-roland/doc/django.conf
===================================================================
--- branches/exodus-roland/doc/django.conf	(revision 6402)
+++ branches/exodus-roland/doc/django.conf	(revision 6402)
@@ -0,0 +1,23 @@
+LoadModule python_module /usr/local/libexec/apache22/mod_python.so
+
+<VirtualHost *:80>
+	ServerName rick.wleiden.net
+
+	RewriteEngine On
+	RewriteRule ^/exodus$ /exodus/ [R=301]
+
+	<Location /exodus/>
+	    SetHandler python-program
+	    PythonHandler django.core.handlers.modpython
+	    SetEnv DJANGO_SETTINGS_MODULE exodus.development
+	    PythonOption django.root /exodus
+	    PythonDebug On
+	    PythonPath "['/srv/wleiden/exodus/trunk/parts/django', '/srv/wleiden/exodus/trunk', '/srv/wleiden/exodus/trunk/eggs/Werkzeug-0.3.1-py2.5.egg', '/srv/wleiden/exodus/trunk/parts/extras/django-command-extensions/'] + sys.path"
+	</Location>
+
+	<Directory /srv/wleiden/exodus/trunk/exodus/static>
+		Allow from all
+	</Directory>
+	
+	Alias /exodus/static /srv/wleiden/exodus/trunk/exodus/static
+</VirtualHost>
Index: branches/exodus-roland/doc/examples/README
===================================================================
--- branches/exodus-roland/doc/examples/README	(revision 6402)
+++ branches/exodus-roland/doc/examples/README	(revision 6402)
@@ -0,0 +1,1 @@
+Example files which needs to be generated using exodus and sample Genesis input file
Index: branches/exodus-roland/doc/examples/dhcpd.conf
===================================================================
--- branches/exodus-roland/doc/examples/dhcpd.conf	(revision 6402)
+++ branches/exodus-roland/doc/examples/dhcpd.conf	(revision 6402)
@@ -0,0 +1,46 @@
+ 
+# This file specific to wireless
+# leiden. Please make all changes in Genesis.
+#
+# Generated by dellas.wirelessleiden.nl
+# on Thu Sep  4 08:56:05 2008
+#
+#  feb 2003 jasper@WirelessLeiden.NL | maart 2005 rick@WirelessLeiden.NL
+#
+
+
+option domain-name "wLeiden.NET";
+ 
+default-lease-time 7200;
+max-lease-time 2592000;
+
+ddns-update-style none;
+
+# Hack for the WET11
+#
+always-broadcast on;
+
+option domain-name-servers 172.27.129.1;
+
+# ep0 Link naar pc's
+subnet 172.27.129.80 netmask 255.255.255.240 {
+  range 172.27.129.82 172.27.129.94;
+  option broadcast-address 172.27.129.95;
+  option subnet-mask 255.255.255.240;
+  option routers 172.27.129.81;
+}
+
+# wi0 Omni voor de buurt
+subnet 172.27.129.0 netmask 255.255.255.192 {
+  range 172.27.129.10 172.27.129.60;
+  option broadcast-address 172.27.129.63;
+  option subnet-mask 255.255.255.192;
+  option routers 172.27.129.1;
+}
+
+# xl0 Link naar thuisnetwerk 
+subnet 172.27.129.64 netmask 255.255.255.252 {not authoritative; }
+
+# xl1 Link naar duivenhok
+subnet 172.16.1.164 netmask 255.255.255.252 {not authoritative; }
+
Index: branches/exodus-roland/doc/examples/named.conf
===================================================================
--- branches/exodus-roland/doc/examples/named.conf	(revision 6402)
+++ branches/exodus-roland/doc/examples/named.conf	(revision 6402)
@@ -0,0 +1,215 @@
+ 
+# This file specific to wireless
+# leiden. Please make all changes in Genesis.
+#
+# Generated by dellas.wirelessleiden.nl
+# on Thu Sep  4 08:56:08 2008
+#
+#  feb 2003 jasper@WirelessLeiden.NL | maart 2005 rick@WirelessLeiden.NL
+#
+
+
+options {
+  directory "/etc/namedb";
+  pid-file "/var/run/named/pid";
+  forwarders {
+172.17.8.68;
+172.17.143.4;
+172.20.128.98;
+  };
+};
+
+
+zone "." {
+  type hint;
+  file "/etc/namedb/named.root";
+};
+
+zone "0.0.127.IN-ADDR.ARPA" {
+  type master;
+  file "/etc/namedb/master/localhost.rev";
+};
+
+zone "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.IP6.INT" {
+  type master;
+  file "/etc/namedb/master/localhost-v6.rev";
+};
+
+zone "16.172.in-addr.arpa" {
+  type slave;
+  file "slave/slave-16.172.in-addr.arpa";
+  masters {
+    172.17.143.4;
+    172.17.8.68;
+    172.20.128.98;
+  };
+};
+
+zone "17.172.in-addr.arpa" {
+  type slave;
+  file "slave/slave-17.172.in-addr.arpa";
+  masters {
+    172.17.143.4;
+    172.17.8.68;
+    172.20.128.98;
+  };
+};
+
+zone "18.172.in-addr.arpa" {
+  type slave;
+  file "slave/slave-18.172.in-addr.arpa";
+  masters {
+    172.17.143.4;
+    172.17.8.68;
+    172.20.128.98;
+  };
+};
+
+zone "19.172.in-addr.arpa" {
+  type slave;
+  file "slave/slave-19.172.in-addr.arpa";
+  masters {
+    172.17.143.4;
+    172.17.8.68;
+    172.20.128.98;
+  };
+};
+
+zone "20.172.in-addr.arpa" {
+  type slave;
+  file "slave/slave-20.172.in-addr.arpa";
+  masters {
+    172.17.143.4;
+    172.17.8.68;
+    172.20.128.98;
+  };
+};
+
+zone "21.172.in-addr.arpa" {
+  type slave;
+  file "slave/slave-21.172.in-addr.arpa";
+  masters {
+    172.17.143.4;
+    172.17.8.68;
+    172.20.128.98;
+  };
+};
+
+zone "22.172.in-addr.arpa" {
+  type slave;
+  file "slave/slave-22.172.in-addr.arpa";
+  masters {
+    172.17.143.4;
+    172.17.8.68;
+    172.20.128.98;
+  };
+};
+
+zone "23.172.in-addr.arpa" {
+  type slave;
+  file "slave/slave-23.172.in-addr.arpa";
+  masters {
+    172.17.143.4;
+    172.17.8.68;
+    172.20.128.98;
+  };
+};
+
+zone "24.172.in-addr.arpa" {
+  type slave;
+  file "slave/slave-24.172.in-addr.arpa";
+  masters {
+    172.17.143.4;
+    172.17.8.68;
+    172.20.128.98;
+  };
+};
+
+zone "25.172.in-addr.arpa" {
+  type slave;
+  file "slave/slave-25.172.in-addr.arpa";
+  masters {
+    172.17.143.4;
+    172.17.8.68;
+    172.20.128.98;
+  };
+};
+
+zone "26.172.in-addr.arpa" {
+  type slave;
+  file "slave/slave-26.172.in-addr.arpa";
+  masters {
+    172.17.143.4;
+    172.17.8.68;
+    172.20.128.98;
+  };
+};
+
+zone "27.172.in-addr.arpa" {
+  type slave;
+  file "slave/slave-27.172.in-addr.arpa";
+  masters {
+    172.17.143.4;
+    172.17.8.68;
+    172.20.128.98;
+  };
+};
+
+zone "28.172.in-addr.arpa" {
+  type slave;
+  file "slave/slave-28.172.in-addr.arpa";
+  masters {
+    172.17.143.4;
+    172.17.8.68;
+    172.20.128.98;
+  };
+};
+
+zone "29.172.in-addr.arpa" {
+  type slave;
+  file "slave/slave-29.172.in-addr.arpa";
+  masters {
+    172.17.143.4;
+    172.17.8.68;
+    172.20.128.98;
+  };
+};
+
+zone "30.172.in-addr.arpa" {
+  type slave;
+  file "slave/slave-30.172.in-addr.arpa";
+  masters {
+    172.17.143.4;
+    172.17.8.68;
+    172.20.128.98;
+  };
+};
+
+zone "31.172.in-addr.arpa" {
+  type slave;
+  file "slave/slave-31.172.in-addr.arpa";
+  masters {
+    172.17.143.4;
+    172.17.8.68;
+    172.20.128.98;
+  };
+};
+
+zone "wLeiden.NET" {
+  type slave;
+  file "slave/slave-wLeiden.NET";
+  masters {
+    172.17.143.4;
+    172.17.8.68;
+    172.20.128.98;
+  };
+};
+
+zone "wZoeterwoude.NET" {
+  type slave;
+  file "slave/slave-wZoeterwoude.NET";
+  masters {
+    172.27.129.66;
+  };
+};
+
Index: branches/exodus-roland/doc/examples/rc.node.local
===================================================================
--- branches/exodus-roland/doc/examples/rc.node.local	(revision 6402)
+++ branches/exodus-roland/doc/examples/rc.node.local	(revision 6402)
@@ -0,0 +1,21 @@
+ 
+# This file specific to wireless
+# leiden. Please make all changes in Genesis.
+#
+# Generated by dellas.wirelessleiden.nl
+# on Thu Sep  4 08:56:13 2008
+#
+#  feb 2003 jasper@WirelessLeiden.NL | maart 2005 rick@WirelessLeiden.NL
+#
+
+
+hostname="CNodeZwet.wLeiden.NET"
+location="Buurt sportvelden, Particulier dak,Zoeterwoude "
+
+ifconfig_lo0_alias0="inet 172.31.255.1/32"
+#ifconfig_lo0_alias1="inet 172.27.129.1/32"
+
+ifconfig_ep0="inet 172.27.129.81/28 "
+ifconfig_wi0="inet 172.27.129.1/26  ssid ap-omni.zwet.wleiden.net channel 1 mediaopt hostap"
+ifconfig_xl0="inet 172.27.129.65/30 "
+ifconfig_xl1="inet 172.16.1.166/30 "
Index: branches/exodus-roland/doc/examples/resolv.conf
===================================================================
--- branches/exodus-roland/doc/examples/resolv.conf	(revision 6402)
+++ branches/exodus-roland/doc/examples/resolv.conf	(revision 6402)
@@ -0,0 +1,15 @@
+ 
+# This file specific to wireless
+# leiden. Please make all changes in Genesis.
+#
+# Generated by dellas.wirelessleiden.nl
+# on Thu Sep  4 08:56:17 2008
+#
+#  feb 2003 jasper@WirelessLeiden.NL | maart 2005 rick@WirelessLeiden.NL
+#
+
+
+search wleiden.net.
+nameserver 127.0.0.1
+
+nameserver 172.16.1.165 # Link naar duivenhok
Index: branches/exodus-roland/doc/examples/wleiden.conf
===================================================================
--- branches/exodus-roland/doc/examples/wleiden.conf	(revision 6402)
+++ branches/exodus-roland/doc/examples/wleiden.conf	(revision 6402)
@@ -0,0 +1,92 @@
+#  Read by /usr/local/sbin/wireless-up.pl
+#
+# config CNodeZwet 
+$location="Buurt sportvelden, Particulier dak,Zoeterwoude ";
+$master_ip='172.27.129.1';
+$gw_open='closed';
+$nodetype='CNode';
+$nodename='Zwet';
+
+$status='up';
+
+$release="5.0-RELEASE";
+$node_config_version="1";
+$genesis_control="YES";
+
+$OS='FreeBSD';
+$labelpos='left';
+
+# edugis x,y 94319,459273
+$X='94319';
+$Y='459273';
+
+$config{'ep0'}=sprintf <<ED0;
+TYPE=ethernet
+IP=172.27.129.81/28
+DESC=Link naar pc's
+SDESC=2zwet-pc
+
+DHCP=82-94
+
+
+ED0
+
+$config{'xl0'}=sprintf <<ED1;  # Link naar thuisnetwerk
+TYPE=ethernet
+IP=172.27.129.65/30
+
+DESC=Link naar thuisnetwerk 
+SDESC=2home
+SPEED=10240000000
+
+OSPF_BROADCAST=no
+OSPF_NEIGHBORS=no
+
+DHCP=no
+
+ED1
+
+$config{'xl1'}=sprintf <<ED2;  # Link naar Duivenhok
+TYPE=ethernet
+IP=172.16.1.166/30
+
+DESC=Link naar duivenhok
+SDESC=2duivenhok
+SPEED=10240000000
+
+DHCP=no
+
+POINT_TO_POINT=172.16.1.165
+OSPF_BROADCAST=no
+OSPF_NEIGHBORS=172.16.1.165
+
+ED2
+
+$config{'wi0'}=sprintf <<EW0;  # OMNI
+TYPE=wireless
+IP=$master_ip/26
+
+DESC=Omni voor de buurt
+SDESC=omni
+SPEED=11534336
+
+OSPF_BROADCAST=no
+OSPF_NEIGHBORS=no
+
+MODE=master
+ESSID=ap-omni.zwet.wleiden.net
+CHANNEL=1
+
+DHCP=10-60
+
+POLAR=Ver
+ANTENNA=omni
+GAIN=5dBi
+DIRECTION=omni
+BEAMWIDTH=360
+CABLE=5
+HEIGTH=14
+
+EW0
+
+
Index: branches/exodus-roland/doc/exodus_mysql
===================================================================
--- branches/exodus-roland/doc/exodus_mysql	(revision 6402)
+++ branches/exodus-roland/doc/exodus_mysql	(revision 6402)
@@ -0,0 +1,129 @@
+XXX: Initial model dependency design, needs documenting of choices and porting
+to direct code of ../exodus/models.py 
+
+XXX: Mis koppeling interlink en node 
+
+group
+    id
+    name
+    contact -> contact.id
+    network
+    netmask
+
+contact
+    id
+    name
+    adres
+    postcode
+    woonplaats
+    telefoonnr vast
+    telefoonnr mobiel
+    email
+    messengernr
+    function
+    group -> group.id
+    comment
+
+location
+    id
+    owner -> contact.id
+    operator -> contact.id
+    adres
+    postcode
+    woonplaats
+    GPS
+    telefoonnr vast
+    comment
+
+hardware
+    id
+    name
+    cpu
+    memory
+    mainboard
+    aantal pci sloten
+    aantal agp sloten
+    aantal mini pci sloten
+    aantal isa sloten
+    onboard video
+    onboard network
+    disksize
+    comment
+
+connector
+    id
+    name
+
+nictype
+    id
+    name
+    transmissionpower
+    connector
+    slottype
+    comment
+
+nic (zowel ethernet als wifi)
+    id
+    node -> node.id
+    mac address
+    type -> nictype.id
+    mode
+    ssid
+    channel
+    flashversion
+    connector -> connector.id
+    comment
+
+interlink
+    id
+    name
+    comment
+
+nicconfig (kan meer dan 1 per card ivm virtual interfaces)
+    id
+    nic -> nic.id
+    ip
+    hostname
+    network -> ippool.id
+    interlink -> interlink.id
+    comment
+
+antennehw
+    id
+    name
+    gain
+    range
+    height
+    directions
+    connector -> connector.id
+    comment
+
+antenne
+    id
+    nic -> nic.id
+    antennehw -> antennehw.id
+
+NF
+    id
+    versionnr
+    OS
+    OSversionnr
+
+ippool
+    id
+    network
+    netmask
+    group -> group.id
+
+system
+    id
+    name
+    builder -> contact.id
+    administrator -> contact.id
+    backup administrator -> contact.id
+    location -> location.id
+    hardware -> hardware.id
+    status (produktie/pre-produktie)
+    type (node/server)
+    NF ->  NF.id
+    comment
Index: branches/exodus-roland/doc/exodusmodels.dot
===================================================================
--- branches/exodus-roland/doc/exodusmodels.dot	(revision 6402)
+++ branches/exodus-roland/doc/exodusmodels.dot	(revision 6402)
@@ -0,0 +1,716 @@
+
+digraph name {
+  fontname = "Helvetica"
+  fontsize = 8
+
+  node [
+    fontname = "Helvetica"
+    fontsize = 8
+    shape = "plaintext"
+  ]
+  edge [
+    fontname = "Helvetica"
+    fontsize = 8
+  ]
+
+
+
+
+
+  
+    exodus_models_NICType [label=<
+    <TABLE BGCOLOR="palegoldenrod" BORDER="0" CELLBORDER="0" CELLSPACING="0">
+     <TR><TD COLSPAN="2" CELLPADDING="4" ALIGN="CENTER" BGCOLOR="olivedrab4"
+     ><FONT FACE="Helvetica Bold" COLOR="white"
+     >NICType</FONT></TD></TR>
+
+    
+        
+        <TR><TD ALIGN="LEFT" BORDER="0"
+        ><FONT COLOR="#7B7B7B" FACE="Helvetica Bold">id</FONT
+        ></TD>
+        <TD ALIGN="LEFT"
+        ><FONT COLOR="#7B7B7B" FACE="Helvetica Bold">AutoField</FONT
+        ></TD></TR>
+        
+        <TR><TD ALIGN="LEFT" BORDER="0"
+        ><FONT FACE="Helvetica Bold">type</FONT
+        ></TD>
+        <TD ALIGN="LEFT"
+        ><FONT FACE="Helvetica Bold">CharField</FONT
+        ></TD></TR>
+        
+    
+    </TABLE>
+    >]
+  
+    exodus_models_Polar [label=<
+    <TABLE BGCOLOR="palegoldenrod" BORDER="0" CELLBORDER="0" CELLSPACING="0">
+     <TR><TD COLSPAN="2" CELLPADDING="4" ALIGN="CENTER" BGCOLOR="olivedrab4"
+     ><FONT FACE="Helvetica Bold" COLOR="white"
+     >Polar</FONT></TD></TR>
+
+    
+        
+        <TR><TD ALIGN="LEFT" BORDER="0"
+        ><FONT COLOR="#7B7B7B" FACE="Helvetica Bold">id</FONT
+        ></TD>
+        <TD ALIGN="LEFT"
+        ><FONT COLOR="#7B7B7B" FACE="Helvetica Bold">AutoField</FONT
+        ></TD></TR>
+        
+        <TR><TD ALIGN="LEFT" BORDER="0"
+        ><FONT FACE="Helvetica Bold">polar</FONT
+        ></TD>
+        <TD ALIGN="LEFT"
+        ><FONT FACE="Helvetica Bold">CharField</FONT
+        ></TD></TR>
+        
+    
+    </TABLE>
+    >]
+  
+    exodus_models_Status [label=<
+    <TABLE BGCOLOR="palegoldenrod" BORDER="0" CELLBORDER="0" CELLSPACING="0">
+     <TR><TD COLSPAN="2" CELLPADDING="4" ALIGN="CENTER" BGCOLOR="olivedrab4"
+     ><FONT FACE="Helvetica Bold" COLOR="white"
+     >Status</FONT></TD></TR>
+
+    
+        
+        <TR><TD ALIGN="LEFT" BORDER="0"
+        ><FONT COLOR="#7B7B7B" FACE="Helvetica Bold">id</FONT
+        ></TD>
+        <TD ALIGN="LEFT"
+        ><FONT COLOR="#7B7B7B" FACE="Helvetica Bold">AutoField</FONT
+        ></TD></TR>
+        
+        <TR><TD ALIGN="LEFT" BORDER="0"
+        ><FONT FACE="Helvetica Bold">status</FONT
+        ></TD>
+        <TD ALIGN="LEFT"
+        ><FONT FACE="Helvetica Bold">CharField</FONT
+        ></TD></TR>
+        
+    
+    </TABLE>
+    >]
+  
+    exodus_models_Mode [label=<
+    <TABLE BGCOLOR="palegoldenrod" BORDER="0" CELLBORDER="0" CELLSPACING="0">
+     <TR><TD COLSPAN="2" CELLPADDING="4" ALIGN="CENTER" BGCOLOR="olivedrab4"
+     ><FONT FACE="Helvetica Bold" COLOR="white"
+     >Mode</FONT></TD></TR>
+
+    
+        
+        <TR><TD ALIGN="LEFT" BORDER="0"
+        ><FONT COLOR="#7B7B7B" FACE="Helvetica Bold">id</FONT
+        ></TD>
+        <TD ALIGN="LEFT"
+        ><FONT COLOR="#7B7B7B" FACE="Helvetica Bold">AutoField</FONT
+        ></TD></TR>
+        
+        <TR><TD ALIGN="LEFT" BORDER="0"
+        ><FONT FACE="Helvetica Bold">mode</FONT
+        ></TD>
+        <TD ALIGN="LEFT"
+        ><FONT FACE="Helvetica Bold">CharField</FONT
+        ></TD></TR>
+        
+    
+    </TABLE>
+    >]
+  
+    exodus_models_Antenna [label=<
+    <TABLE BGCOLOR="palegoldenrod" BORDER="0" CELLBORDER="0" CELLSPACING="0">
+     <TR><TD COLSPAN="2" CELLPADDING="4" ALIGN="CENTER" BGCOLOR="olivedrab4"
+     ><FONT FACE="Helvetica Bold" COLOR="white"
+     >Antenna</FONT></TD></TR>
+
+    
+        
+        <TR><TD ALIGN="LEFT" BORDER="0"
+        ><FONT COLOR="#7B7B7B" FACE="Helvetica Bold">id</FONT
+        ></TD>
+        <TD ALIGN="LEFT"
+        ><FONT COLOR="#7B7B7B" FACE="Helvetica Bold">AutoField</FONT
+        ></TD></TR>
+        
+        <TR><TD ALIGN="LEFT" BORDER="0"
+        ><FONT FACE="Helvetica Bold">type</FONT
+        ></TD>
+        <TD ALIGN="LEFT"
+        ><FONT FACE="Helvetica Bold">CharField</FONT
+        ></TD></TR>
+        
+        <TR><TD ALIGN="LEFT" BORDER="0"
+        ><FONT FACE="Helvetica Bold">gain</FONT
+        ></TD>
+        <TD ALIGN="LEFT"
+        ><FONT FACE="Helvetica Bold">IntegerField</FONT
+        ></TD></TR>
+        
+    
+    </TABLE>
+    >]
+  
+    exodus_models_Location [label=<
+    <TABLE BGCOLOR="palegoldenrod" BORDER="0" CELLBORDER="0" CELLSPACING="0">
+     <TR><TD COLSPAN="2" CELLPADDING="4" ALIGN="CENTER" BGCOLOR="olivedrab4"
+     ><FONT FACE="Helvetica Bold" COLOR="white"
+     >Location</FONT></TD></TR>
+
+    
+        
+        <TR><TD ALIGN="LEFT" BORDER="0"
+        ><FONT COLOR="#7B7B7B" FACE="Helvetica Bold">id</FONT
+        ></TD>
+        <TD ALIGN="LEFT"
+        ><FONT COLOR="#7B7B7B" FACE="Helvetica Bold">AutoField</FONT
+        ></TD></TR>
+        
+        <TR><TD ALIGN="LEFT" BORDER="0"
+        ><FONT FACE="Helvetica Bold">description</FONT
+        ></TD>
+        <TD ALIGN="LEFT"
+        ><FONT FACE="Helvetica Bold">CharField</FONT
+        ></TD></TR>
+        
+        <TR><TD ALIGN="LEFT" BORDER="0"
+        ><FONT FACE="Helvetica Bold">Longitude</FONT
+        ></TD>
+        <TD ALIGN="LEFT"
+        ><FONT FACE="Helvetica Bold">DecimalField</FONT
+        ></TD></TR>
+        
+        <TR><TD ALIGN="LEFT" BORDER="0"
+        ><FONT FACE="Helvetica Bold">Latitude</FONT
+        ></TD>
+        <TD ALIGN="LEFT"
+        ><FONT FACE="Helvetica Bold">DecimalField</FONT
+        ></TD></TR>
+        
+    
+    </TABLE>
+    >]
+  
+    exodus_models_Network [label=<
+    <TABLE BGCOLOR="palegoldenrod" BORDER="0" CELLBORDER="0" CELLSPACING="0">
+     <TR><TD COLSPAN="2" CELLPADDING="4" ALIGN="CENTER" BGCOLOR="olivedrab4"
+     ><FONT FACE="Helvetica Bold" COLOR="white"
+     >Network</FONT></TD></TR>
+
+    
+        
+        <TR><TD ALIGN="LEFT" BORDER="0"
+        ><FONT COLOR="#7B7B7B" FACE="Helvetica Bold">id</FONT
+        ></TD>
+        <TD ALIGN="LEFT"
+        ><FONT COLOR="#7B7B7B" FACE="Helvetica Bold">AutoField</FONT
+        ></TD></TR>
+        
+        <TR><TD ALIGN="LEFT" BORDER="0"
+        ><FONT FACE="Helvetica Bold">domainname</FONT
+        ></TD>
+        <TD ALIGN="LEFT"
+        ><FONT FACE="Helvetica Bold">CharField</FONT
+        ></TD></TR>
+        
+        <TR><TD ALIGN="LEFT" BORDER="0"
+        ><FONT FACE="Helvetica Bold">ipspacestart</FONT
+        ></TD>
+        <TD ALIGN="LEFT"
+        ><FONT FACE="Helvetica Bold">IPAddressField</FONT
+        ></TD></TR>
+        
+        <TR><TD ALIGN="LEFT" BORDER="0"
+        ><FONT FACE="Helvetica Bold">netmask</FONT
+        ></TD>
+        <TD ALIGN="LEFT"
+        ><FONT FACE="Helvetica Bold">IntegerField</FONT
+        ></TD></TR>
+        
+    
+    </TABLE>
+    >]
+  
+    exodus_models_DnsServer [label=<
+    <TABLE BGCOLOR="palegoldenrod" BORDER="0" CELLBORDER="0" CELLSPACING="0">
+     <TR><TD COLSPAN="2" CELLPADDING="4" ALIGN="CENTER" BGCOLOR="olivedrab4"
+     ><FONT FACE="Helvetica Bold" COLOR="white"
+     >DnsServer</FONT></TD></TR>
+
+    
+        
+        <TR><TD ALIGN="LEFT" BORDER="0"
+        ><FONT COLOR="#7B7B7B" FACE="Helvetica Bold">id</FONT
+        ></TD>
+        <TD ALIGN="LEFT"
+        ><FONT COLOR="#7B7B7B" FACE="Helvetica Bold">AutoField</FONT
+        ></TD></TR>
+        
+        <TR><TD ALIGN="LEFT" BORDER="0"
+        ><FONT FACE="Helvetica Bold">ipaddress</FONT
+        ></TD>
+        <TD ALIGN="LEFT"
+        ><FONT FACE="Helvetica Bold">IPAddressField</FONT
+        ></TD></TR>
+        
+        <TR><TD ALIGN="LEFT" BORDER="0"
+        ><FONT FACE="Helvetica Bold">domainname</FONT
+        ></TD>
+        <TD ALIGN="LEFT"
+        ><FONT FACE="Helvetica Bold">ForeignKey</FONT
+        ></TD></TR>
+        
+    
+    </TABLE>
+    >]
+  
+    exodus_models_Node [label=<
+    <TABLE BGCOLOR="palegoldenrod" BORDER="0" CELLBORDER="0" CELLSPACING="0">
+     <TR><TD COLSPAN="2" CELLPADDING="4" ALIGN="CENTER" BGCOLOR="olivedrab4"
+     ><FONT FACE="Helvetica Bold" COLOR="white"
+     >Node</FONT></TD></TR>
+
+    
+        
+        <TR><TD ALIGN="LEFT" BORDER="0"
+        ><FONT COLOR="#7B7B7B" FACE="Helvetica Bold">id</FONT
+        ></TD>
+        <TD ALIGN="LEFT"
+        ><FONT COLOR="#7B7B7B" FACE="Helvetica Bold">AutoField</FONT
+        ></TD></TR>
+        
+        <TR><TD ALIGN="LEFT" BORDER="0"
+        ><FONT FACE="Helvetica Bold">name</FONT
+        ></TD>
+        <TD ALIGN="LEFT"
+        ><FONT FACE="Helvetica Bold">CharField</FONT
+        ></TD></TR>
+        
+        <TR><TD ALIGN="LEFT" BORDER="0"
+        ><FONT FACE="Helvetica Bold">location</FONT
+        ></TD>
+        <TD ALIGN="LEFT"
+        ><FONT FACE="Helvetica Bold">ForeignKey</FONT
+        ></TD></TR>
+        
+        <TR><TD ALIGN="LEFT" BORDER="0"
+        ><FONT FACE="Helvetica Bold">status</FONT
+        ></TD>
+        <TD ALIGN="LEFT"
+        ><FONT FACE="Helvetica Bold">ForeignKey</FONT
+        ></TD></TR>
+        
+        <TR><TD ALIGN="LEFT" BORDER="0"
+        ><FONT FACE="Helvetica Bold">masterip</FONT
+        ></TD>
+        <TD ALIGN="LEFT"
+        ><FONT FACE="Helvetica Bold">IPAddressField</FONT
+        ></TD></TR>
+        
+        <TR><TD ALIGN="LEFT" BORDER="0"
+        ><FONT FACE="Helvetica Bold">network</FONT
+        ></TD>
+        <TD ALIGN="LEFT"
+        ><FONT FACE="Helvetica Bold">ForeignKey</FONT
+        ></TD></TR>
+        
+    
+    </TABLE>
+    >]
+  
+    exodus_models_NIC [label=<
+    <TABLE BGCOLOR="palegoldenrod" BORDER="0" CELLBORDER="0" CELLSPACING="0">
+     <TR><TD COLSPAN="2" CELLPADDING="4" ALIGN="CENTER" BGCOLOR="olivedrab4"
+     ><FONT FACE="Helvetica Bold" COLOR="white"
+     >NIC</FONT></TD></TR>
+
+    
+        
+        <TR><TD ALIGN="LEFT" BORDER="0"
+        ><FONT COLOR="#7B7B7B" FACE="Helvetica Bold">id</FONT
+        ></TD>
+        <TD ALIGN="LEFT"
+        ><FONT COLOR="#7B7B7B" FACE="Helvetica Bold">AutoField</FONT
+        ></TD></TR>
+        
+        <TR><TD ALIGN="LEFT" BORDER="0"
+        ><FONT FACE="Helvetica Bold">node</FONT
+        ></TD>
+        <TD ALIGN="LEFT"
+        ><FONT FACE="Helvetica Bold">ForeignKey</FONT
+        ></TD></TR>
+        
+        <TR><TD ALIGN="LEFT" BORDER="0"
+        ><FONT FACE="Helvetica Bold">iface</FONT
+        ></TD>
+        <TD ALIGN="LEFT"
+        ><FONT FACE="Helvetica Bold">CharField</FONT
+        ></TD></TR>
+        
+        <TR><TD ALIGN="LEFT" BORDER="0"
+        ><FONT COLOR="#7B7B7B" FACE="Helvetica Bold">polar</FONT
+        ></TD>
+        <TD ALIGN="LEFT"
+        ><FONT COLOR="#7B7B7B" FACE="Helvetica Bold">ForeignKey</FONT
+        ></TD></TR>
+        
+        <TR><TD ALIGN="LEFT" BORDER="0"
+        ><FONT COLOR="#7B7B7B" FACE="Helvetica Bold">ssid</FONT
+        ></TD>
+        <TD ALIGN="LEFT"
+        ><FONT COLOR="#7B7B7B" FACE="Helvetica Bold">CharField</FONT
+        ></TD></TR>
+        
+        <TR><TD ALIGN="LEFT" BORDER="0"
+        ><FONT COLOR="#7B7B7B" FACE="Helvetica Bold">mode</FONT
+        ></TD>
+        <TD ALIGN="LEFT"
+        ><FONT COLOR="#7B7B7B" FACE="Helvetica Bold">ForeignKey</FONT
+        ></TD></TR>
+        
+        <TR><TD ALIGN="LEFT" BORDER="0"
+        ><FONT FACE="Helvetica Bold">status</FONT
+        ></TD>
+        <TD ALIGN="LEFT"
+        ><FONT FACE="Helvetica Bold">ForeignKey</FONT
+        ></TD></TR>
+        
+        <TR><TD ALIGN="LEFT" BORDER="0"
+        ><FONT COLOR="#7B7B7B" FACE="Helvetica Bold">channel</FONT
+        ></TD>
+        <TD ALIGN="LEFT"
+        ><FONT COLOR="#7B7B7B" FACE="Helvetica Bold">IntegerField</FONT
+        ></TD></TR>
+        
+        <TR><TD ALIGN="LEFT" BORDER="0"
+        ><FONT COLOR="#7B7B7B" FACE="Helvetica Bold">antenna</FONT
+        ></TD>
+        <TD ALIGN="LEFT"
+        ><FONT COLOR="#7B7B7B" FACE="Helvetica Bold">ForeignKey</FONT
+        ></TD></TR>
+        
+        <TR><TD ALIGN="LEFT" BORDER="0"
+        ><FONT FACE="Helvetica Bold">type</FONT
+        ></TD>
+        <TD ALIGN="LEFT"
+        ><FONT FACE="Helvetica Bold">ForeignKey</FONT
+        ></TD></TR>
+        
+    
+    </TABLE>
+    >]
+  
+    exodus_models_Interlink [label=<
+    <TABLE BGCOLOR="palegoldenrod" BORDER="0" CELLBORDER="0" CELLSPACING="0">
+     <TR><TD COLSPAN="2" CELLPADDING="4" ALIGN="CENTER" BGCOLOR="olivedrab4"
+     ><FONT FACE="Helvetica Bold" COLOR="white"
+     >Interlink</FONT></TD></TR>
+
+    
+        
+        <TR><TD ALIGN="LEFT" BORDER="0"
+        ><FONT COLOR="#7B7B7B" FACE="Helvetica Bold">id</FONT
+        ></TD>
+        <TD ALIGN="LEFT"
+        ><FONT COLOR="#7B7B7B" FACE="Helvetica Bold">AutoField</FONT
+        ></TD></TR>
+        
+        <TR><TD ALIGN="LEFT" BORDER="0"
+        ><FONT FACE="Helvetica Bold">status</FONT
+        ></TD>
+        <TD ALIGN="LEFT"
+        ><FONT FACE="Helvetica Bold">ForeignKey</FONT
+        ></TD></TR>
+        
+        <TR><TD ALIGN="LEFT" BORDER="0"
+        ><FONT FACE="Helvetica Bold">netmask</FONT
+        ></TD>
+        <TD ALIGN="LEFT"
+        ><FONT FACE="Helvetica Bold">IntegerField</FONT
+        ></TD></TR>
+        
+        <TR><TD ALIGN="LEFT" BORDER="0"
+        ><FONT FACE="Helvetica Bold">iface1</FONT
+        ></TD>
+        <TD ALIGN="LEFT"
+        ><FONT FACE="Helvetica Bold">ForeignKey</FONT
+        ></TD></TR>
+        
+        <TR><TD ALIGN="LEFT" BORDER="0"
+        ><FONT FACE="Helvetica Bold">ip1</FONT
+        ></TD>
+        <TD ALIGN="LEFT"
+        ><FONT FACE="Helvetica Bold">IPAddressField</FONT
+        ></TD></TR>
+        
+        <TR><TD ALIGN="LEFT" BORDER="0"
+        ><FONT FACE="Helvetica Bold">iface2</FONT
+        ></TD>
+        <TD ALIGN="LEFT"
+        ><FONT FACE="Helvetica Bold">ForeignKey</FONT
+        ></TD></TR>
+        
+        <TR><TD ALIGN="LEFT" BORDER="0"
+        ><FONT FACE="Helvetica Bold">ip2</FONT
+        ></TD>
+        <TD ALIGN="LEFT"
+        ><FONT FACE="Helvetica Bold">IPAddressField</FONT
+        ></TD></TR>
+        
+        <TR><TD ALIGN="LEFT" BORDER="0"
+        ><FONT FACE="Helvetica Bold">shortdesc1</FONT
+        ></TD>
+        <TD ALIGN="LEFT"
+        ><FONT FACE="Helvetica Bold">CharField</FONT
+        ></TD></TR>
+        
+        <TR><TD ALIGN="LEFT" BORDER="0"
+        ><FONT FACE="Helvetica Bold">shortdesc2</FONT
+        ></TD>
+        <TD ALIGN="LEFT"
+        ><FONT FACE="Helvetica Bold">CharField</FONT
+        ></TD></TR>
+        
+        <TR><TD ALIGN="LEFT" BORDER="0"
+        ><FONT FACE="Helvetica Bold">desc1</FONT
+        ></TD>
+        <TD ALIGN="LEFT"
+        ><FONT FACE="Helvetica Bold">CharField</FONT
+        ></TD></TR>
+        
+        <TR><TD ALIGN="LEFT" BORDER="0"
+        ><FONT FACE="Helvetica Bold">desc2</FONT
+        ></TD>
+        <TD ALIGN="LEFT"
+        ><FONT FACE="Helvetica Bold">CharField</FONT
+        ></TD></TR>
+        
+    
+    </TABLE>
+    >]
+  
+    exodus_models_Omni [label=<
+    <TABLE BGCOLOR="palegoldenrod" BORDER="0" CELLBORDER="0" CELLSPACING="0">
+     <TR><TD COLSPAN="2" CELLPADDING="4" ALIGN="CENTER" BGCOLOR="olivedrab4"
+     ><FONT FACE="Helvetica Bold" COLOR="white"
+     >Omni</FONT></TD></TR>
+
+    
+        
+        <TR><TD ALIGN="LEFT" BORDER="0"
+        ><FONT COLOR="#7B7B7B" FACE="Helvetica Bold">id</FONT
+        ></TD>
+        <TD ALIGN="LEFT"
+        ><FONT COLOR="#7B7B7B" FACE="Helvetica Bold">AutoField</FONT
+        ></TD></TR>
+        
+        <TR><TD ALIGN="LEFT" BORDER="0"
+        ><FONT FACE="Helvetica Bold">iface</FONT
+        ></TD>
+        <TD ALIGN="LEFT"
+        ><FONT FACE="Helvetica Bold">ForeignKey</FONT
+        ></TD></TR>
+        
+        <TR><TD ALIGN="LEFT" BORDER="0"
+        ><FONT FACE="Helvetica Bold">status</FONT
+        ></TD>
+        <TD ALIGN="LEFT"
+        ><FONT FACE="Helvetica Bold">ForeignKey</FONT
+        ></TD></TR>
+        
+        <TR><TD ALIGN="LEFT" BORDER="0"
+        ><FONT FACE="Helvetica Bold">ip</FONT
+        ></TD>
+        <TD ALIGN="LEFT"
+        ><FONT FACE="Helvetica Bold">IPAddressField</FONT
+        ></TD></TR>
+        
+        <TR><TD ALIGN="LEFT" BORDER="0"
+        ><FONT FACE="Helvetica Bold">netmask</FONT
+        ></TD>
+        <TD ALIGN="LEFT"
+        ><FONT FACE="Helvetica Bold">IntegerField</FONT
+        ></TD></TR>
+        
+        <TR><TD ALIGN="LEFT" BORDER="0"
+        ><FONT FACE="Helvetica Bold">dhcpstart</FONT
+        ></TD>
+        <TD ALIGN="LEFT"
+        ><FONT FACE="Helvetica Bold">IPAddressField</FONT
+        ></TD></TR>
+        
+        <TR><TD ALIGN="LEFT" BORDER="0"
+        ><FONT FACE="Helvetica Bold">dhcpstop</FONT
+        ></TD>
+        <TD ALIGN="LEFT"
+        ><FONT FACE="Helvetica Bold">IPAddressField</FONT
+        ></TD></TR>
+        
+        <TR><TD ALIGN="LEFT" BORDER="0"
+        ><FONT FACE="Helvetica Bold">shortdesc</FONT
+        ></TD>
+        <TD ALIGN="LEFT"
+        ><FONT FACE="Helvetica Bold">CharField</FONT
+        ></TD></TR>
+        
+        <TR><TD ALIGN="LEFT" BORDER="0"
+        ><FONT FACE="Helvetica Bold">desc</FONT
+        ></TD>
+        <TD ALIGN="LEFT"
+        ><FONT FACE="Helvetica Bold">CharField</FONT
+        ></TD></TR>
+        
+    
+    </TABLE>
+    >]
+  
+    exodus_models_DhcpStatic [label=<
+    <TABLE BGCOLOR="palegoldenrod" BORDER="0" CELLBORDER="0" CELLSPACING="0">
+     <TR><TD COLSPAN="2" CELLPADDING="4" ALIGN="CENTER" BGCOLOR="olivedrab4"
+     ><FONT FACE="Helvetica Bold" COLOR="white"
+     >DhcpStatic</FONT></TD></TR>
+
+    
+        
+        <TR><TD ALIGN="LEFT" BORDER="0"
+        ><FONT COLOR="#7B7B7B" FACE="Helvetica Bold">id</FONT
+        ></TD>
+        <TD ALIGN="LEFT"
+        ><FONT COLOR="#7B7B7B" FACE="Helvetica Bold">AutoField</FONT
+        ></TD></TR>
+        
+        <TR><TD ALIGN="LEFT" BORDER="0"
+        ><FONT FACE="Helvetica Bold">hostname</FONT
+        ></TD>
+        <TD ALIGN="LEFT"
+        ><FONT FACE="Helvetica Bold">CharField</FONT
+        ></TD></TR>
+        
+        <TR><TD ALIGN="LEFT" BORDER="0"
+        ><FONT FACE="Helvetica Bold">macaddress</FONT
+        ></TD>
+        <TD ALIGN="LEFT"
+        ><FONT FACE="Helvetica Bold">CharField</FONT
+        ></TD></TR>
+        
+        <TR><TD ALIGN="LEFT" BORDER="0"
+        ><FONT FACE="Helvetica Bold">address</FONT
+        ></TD>
+        <TD ALIGN="LEFT"
+        ><FONT FACE="Helvetica Bold">IPAddressField</FONT
+        ></TD></TR>
+        
+        <TR><TD ALIGN="LEFT" BORDER="0"
+        ><FONT FACE="Helvetica Bold">omni</FONT
+        ></TD>
+        <TD ALIGN="LEFT"
+        ><FONT FACE="Helvetica Bold">ForeignKey</FONT
+        ></TD></TR>
+        
+    
+    </TABLE>
+    >]
+  
+
+
+
+
+  
+    
+  
+    
+  
+    
+  
+    
+  
+    
+  
+    
+  
+    
+  
+    
+    
+    exodus_models_DnsServer -> exodus_models_Network
+    [label="domainname"] ;
+    
+  
+    
+    
+    exodus_models_Node -> exodus_models_Location
+    [label="location"] ;
+    
+    
+    exodus_models_Node -> exodus_models_Status
+    [label="status"] ;
+    
+    
+    exodus_models_Node -> exodus_models_Network
+    [label="network"] ;
+    
+  
+    
+    
+    exodus_models_NIC -> exodus_models_Node
+    [label="node"] ;
+    
+    
+    exodus_models_NIC -> exodus_models_Polar
+    [label="polar"] ;
+    
+    
+    exodus_models_NIC -> exodus_models_Mode
+    [label="mode"] ;
+    
+    
+    exodus_models_NIC -> exodus_models_Status
+    [label="status"] ;
+    
+    
+    exodus_models_NIC -> exodus_models_Antenna
+    [label="antenna"] ;
+    
+    
+    exodus_models_NIC -> exodus_models_NICType
+    [label="type"] ;
+    
+  
+    
+    
+    exodus_models_Interlink -> exodus_models_Status
+    [label="status"] ;
+    
+    
+    exodus_models_Interlink -> exodus_models_NIC
+    [label="iface1"] ;
+    
+    
+    exodus_models_Interlink -> exodus_models_NIC
+    [label="iface2"] ;
+    
+  
+    
+    
+    exodus_models_Omni -> exodus_models_NIC
+    [label="iface"] ;
+    
+    
+    exodus_models_Omni -> exodus_models_Status
+    [label="status"] ;
+    
+  
+    
+    
+    exodus_models_DhcpStatic -> exodus_models_Omni
+    [label="omni"] ;
+    
+  
+
+
+}
+
Index: branches/exodus-roland/exodus/admin.py
===================================================================
--- branches/exodus-roland/exodus/admin.py	(revision 6402)
+++ branches/exodus-roland/exodus/admin.py	(revision 6402)
@@ -0,0 +1,84 @@
+from django.contrib import admin
+from django.contrib import databrowse
+from django import forms
+from exodus.models import *
+
+from exodus.wllogic import free_masterip, newSSIDName, addInterlinkIP, \
+     freeInterlinkIP, freePublicAPIP
+
+from exodus.contrib import ReadOnlyAdminFields
+
+class InterfaceCommon(ReadOnlyAdminFields):
+    readonly = ('ip', )
+    def save_model(self, request, obj, form, change):
+        if obj.link and (obj.type != obj.link.type):
+            raise ValueError,'Type of local and remote interface needs to match'
+        if str(obj.type) != "eth":
+            obj.ssid = newSSIDName(obj.node, obj.iface, 'unused')
+            obj.channel = '1'
+            obj.mode = 1 # set to master
+
+        # Only change IP if changes in interface link/mask or new of course :-)
+        #if change or self.is_changed.has_key('link') or \
+        #        self.is_changed.has_key('netmask'):
+        if not obj.link:
+            obj.ip = freeInterlinkIP(obj)
+        else:
+            obj.ip = addInterlinkIP(obj.link)
+
+        # XXX: Change in netmask requires full range of netmask changes \
+        # on slaves
+        obj.save()
+        #Dirty to hack to get reference to self working
+        if not obj.link:
+            obj.link = obj
+            obj.save()
+
+
+class PublicAPInline(ReadOnlyAdminFields,admin.TabularInline):
+    model = PublicAP
+    extra = 1
+
+class InterfaceAdmin(InterfaceCommon, admin.ModelAdmin):
+    inlines = (PublicAPInline,)
+    pass
+
+class InterfaceInline(InterfaceCommon, admin.TabularInline):
+    model = Interface
+    extra = 1
+
+class DhcpStaticInline(admin.TabularInline):
+    model = DhcpStatic
+    extra = 1
+
+class PublicAPAdmin(ReadOnlyAdminFields,admin.ModelAdmin):
+    inlines = (DhcpStaticInline,)
+
+class NodeAdmin(admin.ModelAdmin):
+    exclude = ('masterip',)
+# Inline forms sucks bigtime, editing deleting, making readonly broken or non existing
+# XXX: Every save creates a new interface :-(
+    inlines = (InterfaceInline,)
+
+    def save_model(self, request, obj, form, change):
+        #XXX: Testing hidden field commits
+        obj.masterip = free_masterip(obj.network)
+        obj.save()
+
+admin.site.register(Antenna)
+admin.site.register(Location)
+admin.site.register(DnsServer)
+admin.site.register(Network)
+admin.site.register(Node,NodeAdmin)
+admin.site.register(Interface,InterfaceAdmin)
+admin.site.register(PublicAP, PublicAPAdmin)
+admin.site.register(DhcpStatic)
+
+databrowse.site.register(Antenna)
+databrowse.site.register(Location)
+databrowse.site.register(DnsServer)
+databrowse.site.register(Network)
+databrowse.site.register(Node)
+databrowse.site.register(Interface)
+databrowse.site.register(PublicAP)
+databrowse.site.register(DhcpStatic)
Index: branches/exodus-roland/exodus/contrib.py
===================================================================
--- branches/exodus-roland/exodus/contrib.py	(revision 6402)
+++ branches/exodus-roland/exodus/contrib.py	(revision 6402)
@@ -0,0 +1,36 @@
+from django import forms
+
+# http://www.djangosnippets.org/snippets/937/
+class ReadOnlyWidget(forms.Widget):
+    def __init__(self, original_value, display_value):
+        self.original_value = original_value
+        self.display_value = display_value
+
+        super(ReadOnlyWidget, self).__init__()
+
+    def render(self, name, value, attrs=None):
+        if self.display_value is not None:
+            return unicode(self.display_value)
+        return unicode(self.original_value)
+
+    def value_from_datadict(self, data, files, name):
+        return self.original_value
+
+class ReadOnlyAdminFields(object):
+    def get_form(self, request, obj=None):
+        form = super(ReadOnlyAdminFields, self).get_form(request, obj)
+
+        if hasattr(self, 'readonly'):
+            for field_name in self.readonly:
+                if field_name in form.base_fields:
+
+                    if hasattr(obj, 'get_%s_display' % field_name):
+                        display_value = getattr(obj, 'get_%s_display' % field_name)()
+                    else:
+                        display_value = None
+
+                    form.base_fields[field_name].widget = ReadOnlyWidget(getattr(obj, field_name, ''), display_value)
+                    form.base_fields[field_name].required = False
+
+        return form
+
Index: branches/exodus-roland/exodus/development.py
===================================================================
--- branches/exodus-roland/exodus/development.py	(revision 6402)
+++ branches/exodus-roland/exodus/development.py	(revision 6402)
@@ -0,0 +1,4 @@
+
+from exodus.settings import *
+DEBUG=True
+TEMPLATE_DEBUG=DEBUG
Index: branches/exodus-roland/exodus/models.py
===================================================================
--- branches/exodus-roland/exodus/models.py	(revision 6402)
+++ branches/exodus-roland/exodus/models.py	(revision 6402)
@@ -0,0 +1,171 @@
+# (c) Roland van Laar 2006
+# vi:et:ts=4
+from django.db import models
+from django.contrib import admin
+
+# Create your models here.
+
+#No need to formalize it; CHOICES don't change much
+
+STATUS_CHOICES = ( 
+			('up', 'up'),
+			('dw', 'down'),
+			('pl', 'planned'),
+			)
+
+POLAR_CHOICES = (
+			('hr', 'horizontal'),
+			('vr', 'vertical'),
+			)
+
+INTERFACE_TYPE_CHOICES = (
+			('eth', 'eth'),
+			('11a', '11a'),
+			('11b', '11b'),
+			('11g', '11g'),
+			)
+WIFI_MODE_CHOICES = (
+			('ms', 'master'),
+			('mn', 'managed'),
+			)
+
+class ExtendedModel(models.Model):
+    class Meta:
+        abstract = True
+        verbose_name = 'ExtendedModel'
+
+    def as_list(self):
+        meta = self._meta
+        values = [(f.verbose_name, getattr(self, f.attname))  \
+                for f in meta.local_fields if f.verbose_name not in ( 'ID')]
+        return values
+
+class Antenna(ExtendedModel):
+    type = models.CharField(max_length=20, unique=True)
+    gain = models.IntegerField(max_length=3)
+
+    class Meta:
+        verbose_name = 'Antenna'
+
+    def __str__(self):
+        return self.type
+
+class Location(ExtendedModel):
+    description = models.CharField(max_length=200, unique=True)
+    longitude = models.DecimalField(max_digits=8,decimal_places=6)
+    latitude = models.DecimalField(max_digits=8,decimal_places=6)
+
+    class Meta:
+        verbose_name = 'Location'
+
+    def __str__(self):
+        return self.description
+
+
+#XXX: Name Domain perhaps?
+class DnsServer(ExtendedModel):
+    ipaddress = models.IPAddressField()
+    domainname = models.CharField(max_length=50, unique=True)
+    
+    class Meta:
+        verbose_name = 'Dns Server'
+    
+    def __str__(self):
+        return "%s, %s"% (self.domainname, self.ipaddress )
+
+
+class Network(ExtendedModel):
+	name = models.CharField(max_length=50, unique=True)
+	ipspacestart = models.IPAddressField()
+	netmask = models.IntegerField()
+
+    class Meta:
+        verbose_name = 'Network'
+
+	def __str__(self):
+		return self.name
+
+
+
+
+class Node(ExtendedModel):
+    name = models.CharField(max_length=30, unique=True)
+    location = models.ForeignKey(Location)
+    status = models.CharField(max_length=10, choices=STATUS_CHOICES, default=1)
+    masterip = models.IPAddressField(unique=True)
+    network = models.ForeignKey(Network,default=1)
+ 
+    class Meta:
+        ordering = ['name']
+        verbose_name = 'Node'
+
+    def __str__(self):
+        return self.name
+
+class Interface(ExtendedModel):
+	node = models.ForeignKey(Node)
+	type = models.CharField(max_length=10, choices=INTERFACE_TYPE_CHOICES, \
+            default=1)
+	iface = models.CharField(max_length=10, verbose_name="interface", \
+            default='eth0')
+	ip = models.IPAddressField(unique=True, blank=True)
+	netmask = models.IntegerField(default=30)
+	polar = models.CharField(blank=True, max_length=10, choices=POLAR_CHOICES)
+	ssid = models.CharField(max_length=30, blank=True, null=True)
+	mode = models.CharField(max_length=10, choices=WIFI_MODE_CHOICES, blank=True)
+	channel = models.IntegerField(blank=True, null=True)
+	antenna = models.ForeignKey(Antenna, blank=True, null=True)
+	shortdesc = models.CharField(blank=True, max_length=10)
+	desc = models.CharField(blank=True, max_length=100)
+	link = models.ForeignKey('self', blank=True, null=True)
+	
+	class Meta:
+		#unique_together = ('node', 'iface')
+		verbose_name = 'Interface'
+	
+	def __str__(self):
+		return "%s/%s" % (self.node, self.iface)
+
+class InterfaceAlias(ExtendedModel):
+	iface = models.ForeignKey(Interface,related_name='child')
+	ip = models.IPAddressField(unique=True)
+	netmask = models.IntegerField(default=30)
+	shortdesc = models.CharField(blank=True, max_length=10)
+	desc = models.CharField(blank=True, max_length=100)
+	linkAlias = models.ForeignKey('self', blank=True, null=True)
+	link = models.ForeignKey(Interface, blank=True, null=True)
+	
+	def __str__(self):
+		return "Alias %s" % (self.iface)
+
+#
+# XXX: Needs rethinking, dhcp[start|stop] could be removed if whole publicAP is
+# going to be activated as DHCP range, or only number of fixed clients needs to
+# be defined, all other could be calculated on the fly if needed
+
+class PublicAP(ExtendedModel):
+    iface = models.ForeignKey(Interface,related_name='ap', verbose_name="interface")
+    ip = models.IPAddressField(unique=True)
+    netmask = models.IntegerField(default=28)
+    dhcpstart = models.IPAddressField()
+    dhcpstop = models.IPAddressField()
+    shortdesc = models.CharField(max_length=10)
+    desc = models.CharField(max_length=100)
+
+    class Meta:
+        verbose_name = "Public Access Point"
+
+    def __str__(self):
+        return "%s:%s" % (self.iface,self.pk)
+
+class DhcpStatic(ExtendedModel):
+    hostname = models.CharField(max_length=10,unique=True)
+    macaddress = models.CharField(max_length=17)
+    address = models.IPAddressField()
+    PublicAP = models.ForeignKey(PublicAP)
+
+    class Meta:
+        verbose_name = "Static host"
+
+    def __str__(self):
+        return self.hostname
Index: branches/exodus-roland/exodus/production.py
===================================================================
--- branches/exodus-roland/exodus/production.py	(revision 6402)
+++ branches/exodus-roland/exodus/production.py	(revision 6402)
@@ -0,0 +1,2 @@
+
+from exodus.settings import *
Index: branches/exodus-roland/exodus/settings.py
===================================================================
--- branches/exodus-roland/exodus/settings.py	(revision 6402)
+++ branches/exodus-roland/exodus/settings.py	(revision 6402)
@@ -0,0 +1,75 @@
+# Django settings for exodus project.
+import os
+
+DEBUG = True
+TEMPLATE_DEBUG = DEBUG
+
+ADMINS = (
+    ('rick', 'rick@wzoeterwoude.net'),
+    ('roland','roland@micite.net'),
+)
+
+MANAGERS = ADMINS
+
+# Nodes get a /24
+MASTERIP_NETMASK = 24
+
+# Master super path, set to current working path
+EXODUS_ROOT =  os.path.dirname(__file__)
+
+# Whether to find static files which are going to be served by django if running static mode
+EXODUS_STATIC_ROOT = os.path.join(EXODUS_ROOT, 'static')
+
+DATABASE_ENGINE = 'sqlite3'           # 'postgresql', 'mysql', 'sqlite3' or 'ado_mssql'.
+#Or path to database file if using sqlite3.
+DATABASE_NAME = os.path.join(EXODUS_ROOT, 'exodus.db')
+DATABASE_USER = ''             # Not used with sqlite3.
+DATABASE_PASSWORD = ''         # Not used with sqlite3.
+DATABASE_HOST = ''             # Set to empty string for localhost. Not used with sqlite3.
+DATABASE_PORT = ''             # Set to empty string for default. Not used with sqlite3.
+
+# Local time zone for this installation. All choices can be found here:
+# http://www.postgresql.org/docs/current/static/datetime-keywords.html#DATETIME-TIMEZONE-SET-TABLE
+TIME_ZONE = 'Europe/Amsterdam'
+
+# Language code for this installation. All choices can be found here:
+# http://www.w3.org/TR/REC-html40/struct/dirlang.html#langcodes
+# http://blogs.law.harvard.edu/tech/stories/storyReader$15
+LANGUAGE_CODE = 'en-us'
+
+SITE_ID = 1
+
+# Make this unique, and don't share it with anybody.
+SECRET_KEY = 'l+plhHJKNIkiasdfh12lsk0Lkf,.=+-asdjdknmnaladfasdmnm,90934jknmnsdaf09'
+
+# List of callables that know how to import templates from various sources.
+TEMPLATE_LOADERS = (
+    'django.template.loaders.filesystem.load_template_source',
+    'django.template.loaders.app_directories.load_template_source',
+#     'django.template.loaders.eggs.load_template_source',
+)
+
+MIDDLEWARE_CLASSES = (
+    'django.middleware.common.CommonMiddleware',
+    'django.contrib.sessions.middleware.SessionMiddleware',
+    'django.contrib.auth.middleware.AuthenticationMiddleware',
+    'django.middleware.doc.XViewMiddleware',
+)
+
+ROOT_URLCONF = 'exodus.urls'
+
+TEMPLATE_DIRS = (
+    # Put strings here, like "/home/html/django_templates".
+    # Always use forward slashes, even on Windows.
+)
+
+INSTALLED_APPS = (
+    'django.contrib.auth',
+    'django.contrib.contenttypes',
+    'django.contrib.sessions',
+    'django.contrib.sites',
+    'django.contrib.admin',
+    'django.contrib.databrowse',
+    'django_extensions',
+    'exodus',
+)
Index: branches/exodus-roland/exodus/sql/antenna.sql
===================================================================
--- branches/exodus-roland/exodus/sql/antenna.sql	(revision 6402)
+++ branches/exodus-roland/exodus/sql/antenna.sql	(revision 6402)
@@ -0,0 +1,7 @@
+INSERT INTO exodus_antenna VALUES ( 1, 'Omni','6');
+INSERT INTO exodus_antenna VALUES ( 2, 'SD9','9');
+INSERT INTO exodus_antenna VALUES ( 3, 'SD15','15');
+INSERT INTO exodus_antenna VALUES ( 4, 'SD19','19');
+INSERT INTO exodus_antenna VALUES ( 5, '2020','??');
+INSERT INTO exodus_antenna VALUES ( 6, '4040','??');
+INSERT INTO exodus_antenna VALUES ( 7, '8080','??');
Index: branches/exodus-roland/exodus/sql/dnsserver.sql
===================================================================
--- branches/exodus-roland/exodus/sql/dnsserver.sql	(revision 6402)
+++ branches/exodus-roland/exodus/sql/dnsserver.sql	(revision 6402)
@@ -0,0 +1,2 @@
+INSERT INTO exodus_DnsServer VALUES ( 1, '172.17.8.68', 'wleiden.net');
+INSERT INTO exodus_DnsServer VALUES ( 2, '172.17.143.4', 'wzoeterwoude.net');
Index: branches/exodus-roland/exodus/sql/linktype.sql
===================================================================
--- branches/exodus-roland/exodus/sql/linktype.sql	(revision 6402)
+++ branches/exodus-roland/exodus/sql/linktype.sql	(revision 6402)
@@ -0,0 +1,2 @@
+INSERT INTO exodus_linktype VALUES ( 1, 'interlink');
+INSERT INTO exodus_linktype VALUES ( 2, 'public');
Index: branches/exodus-roland/exodus/sql/network.sql
===================================================================
--- branches/exodus-roland/exodus/sql/network.sql	(revision 6402)
+++ branches/exodus-roland/exodus/sql/network.sql	(revision 6402)
@@ -0,0 +1,3 @@
+INSERT INTO exodus_network VALUES ( 1, 'wleiden.net', '172.16.0.0',16);
+INSERT INTO exodus_network VALUES ( 2, 'walphen.net', '172.18.0.0',16);
+INSERT INTO exodus_network VALUES ( 3, 'wzoeterwoude.net', '172.27.0.0',16);
Index: branches/exodus-roland/exodus/static/exodus.css
===================================================================
--- branches/exodus-roland/exodus/static/exodus.css	(revision 6402)
+++ branches/exodus-roland/exodus/static/exodus.css	(revision 6402)
@@ -0,0 +1,188 @@
+BODY {
+		font-family : Verdana, Geneva, Arial, Helvetica, sans-serif;
+		font-size : 12px;
+		color : Black;
+	}
+
+A	{
+		color : #4471AA;
+		text-decoration : None;
+	}
+
+A.list {
+		font-family : Verdana, Geneva, Arial, Helvetica, sans-serif;
+		font-size : 13px;
+		background-color : #CAFFFF;
+		}
+
+TD {
+		font-family : Verdana, Geneva, Arial, Helvetica, sans-serif;
+		font-size : 12px;
+		color : Black;
+	}
+
+TD.disclaimer {
+		text-align : center;
+	}
+
+TD.title {
+		font-family : Courier New, Courier, monospace;
+		font-weight : Bold;
+		font-size : 24px;
+		background-color : Black;
+		line-height : 31 px;
+		color : White;
+		text-align : right;
+		width : 644px;
+	}
+
+TD.list {
+		font-family : Verdana, Geneva, Arial, Helvetica, sans-serif;
+		font-size : 13px;
+		background-color : #CAFFFF;
+		}
+
+TD.menu {
+		font-family : Verdana, Geneva, Arial, Helvetica, sans-serif;
+		line-height : 20px;
+		font-size : 16px;
+		color : White;
+		background-color : #CA2424;
+		width : 130px;
+		}
+
+TD.menu2 {
+        font-family : Verdana, Geneva, Arial, Helvetica, sans-serif;
+        font-size : 16px;
+        color : White;
+		background-image : url("/exodus/static/topmenu.png");
+		background-position : bottom;
+		width : 644px;
+		height : 25px;
+		}
+
+TD.menu2table {
+        font-family : Verdana, Geneva, Arial, Helvetica, sans-serif;
+        font-size : 16px;
+        color : White;
+		background-position : bottom;
+		width : 644px;
+		height : 25px;
+		}
+
+TD.menu2table a {
+       color : White;
+       }
+
+TD.menu2l {
+		background-image : url("/exodus/static/topmenu_left.png");
+		background-position : top;
+		width : 8px;
+		height : 25px;
+		}
+
+TD.menu2r {
+		background-image : url("/exodus/static/topmenu_right.png");
+		background-position : top;
+		width : 8px;
+		height : 25px;
+		}
+
+TD.main {
+		font-family : Verdana, Geneva, Arial, Helvetica, sans-serif;
+		font-size : 12px;
+		background-image : url("/exodus/static/background.png");
+		background-position : 0;
+		width : 644px;
+		padding-top : 10px;
+		line-height: 16px;
+		}
+
+TD.mainl {
+		width : 8px;
+		}
+
+TD.mainr {
+		width : 8px;
+		background-color : #D3E0F1;
+		}
+
+TD.right {
+		font-family : Verdana, Geneva, Arial, Helvetica, sans-serif;
+		font-size : 12px;
+		background-position : 0;
+		width : 660px;
+		}
+
+TD.title {
+		font-family : Courier New, Courier, monospace;
+		font-weight : Bold;
+		background-color : black;
+		line-height : 31px;
+		color : White;
+		text-align : right;
+		width : 644px;
+		}
+
+TD.titlel {
+		background-color : Black;
+		line-height : 31px;
+		width : 8px;
+		}
+
+TD.titler {
+		background-color : Black;
+		line-height : 31px;
+		width : 8px;
+		}
+
+P.menu {
+		padding-left : 10px;
+		}
+
+P.title {
+		font-size : 16px;
+		font-weight : bold;
+		}
+
+P.blue {
+		font-size : 14px;
+		font-weight : Bold;
+		color : Black;
+		background-color : #EECC99;
+		padding-left : 10px;
+		padding-right : 10px;
+		padding-bottom : 2px;
+		}
+
+A.menu { 
+		text-decoration : none;
+		color : White;
+		background-color : #CA2424;
+		border-bottom-style : none;
+		border-bottom-width : 1px;
+		}
+
+A.title {
+		color : white;
+		background-color: black;
+		text-decoration : none;
+		border-bottom-style : none;
+		border-bottom-width : 1px;
+		border-bottom-color : #CA2424;
+		}
+
+A.menu:HOVER {
+        color : white;
+        background-color : #CA2424;
+        border-bottom-style : solid;
+        border-bottom-width : 1px;
+        border-bottom-color : black;
+		}
+
+A.disclaimer {
+		font-family : Verdana, Geneva, Arial, Helvetica, sans-serif;
+		font-size : 9px;
+		color : #444444;
+		text-decoration : none;
+		}
Index: branches/exodus-roland/exodus/templates/addInterface.html
===================================================================
--- branches/exodus-roland/exodus/templates/addInterface.html	(revision 6402)
+++ branches/exodus-roland/exodus/templates/addInterface.html	(revision 6402)
@@ -0,0 +1,23 @@
+<script type="text/javascript">
+window.onload = function()
+{
+	document.getElementById('id_polar').disabled = 'true';
+	document.getElementById('id_antenna').disabled = 'true';
+	document.getElementById('id_type').onchange = function()
+	{
+	var newValue = document.getElementById('id_type').value;
+	document.getElementById('id_iface').value = newValue;
+	if (newValue == 'eth') {
+		newValue = 'true';
+	} else {
+		newValue = '';
+	}
+	document.getElementById('id_polar').disabled = newValue;
+	document.getElementById('id_antenna').disabled = newValue;
+	}
+
+	/* XXX: Hack, dunno how to set this Django Style */
+	var linkSelect = document.getElementById('id_link');
+	linkSelect.options[0].text = "MASTER";
+}
+</script>
Index: branches/exodus-roland/exodus/templates/deleteNode.html
===================================================================
--- branches/exodus-roland/exodus/templates/deleteNode.html	(revision 6402)
+++ branches/exodus-roland/exodus/templates/deleteNode.html	(revision 6402)
@@ -0,0 +1,19 @@
+Interfaces to be deleted:
+<ol>
+{% for nic in  object.interface_set.all %}
+<li>{{ nic }}</li>
+{% endfor %}
+</ol>
+
+All connected interfaces to be converted to master interfaces:
+<ol>
+{% for nic in  object.interface_set.all %}
+	{% ifequal nic.link nic %}
+		{% for interlink in nic.link.interface_set.all %}
+			{% ifnotequal interlink nic %}
+			<li> {{ interlink }}</li>
+			{% endifnotequal %}
+		{% endfor %}
+	{% endifequal %}
+{% endfor %}
+</ol>
Index: branches/exodus-roland/exodus/templates/deletePublicAP.html
===================================================================
--- branches/exodus-roland/exodus/templates/deletePublicAP.html	(revision 6402)
+++ branches/exodus-roland/exodus/templates/deletePublicAP.html	(revision 6402)
@@ -0,0 +1,7 @@
+<table>
+{% for key, value in object.as_list %}
+<tr><th>{{ key }}</th><td>:</td><td>{{ value }}</td><tr>
+{% endfor %}
+
+{# XXX: Include static-dhcp assignments #}
+</table>
Index: branches/exodus-roland/exodus/templates/editInterface.html
===================================================================
--- branches/exodus-roland/exodus/templates/editInterface.html	(revision 6402)
+++ branches/exodus-roland/exodus/templates/editInterface.html	(revision 6402)
@@ -0,0 +1,1 @@
+{% include 'addInterface.html' %}
Index: branches/exodus-roland/exodus/templates/exodus-template.html
===================================================================
--- branches/exodus-roland/exodus/templates/exodus-template.html	(revision 6402)
+++ branches/exodus-roland/exodus/templates/exodus-template.html	(revision 6402)
@@ -0,0 +1,68 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<HTML>
+<HEAD>
+<TITLE>
+WirelessLeiden Exodus
+</TITLE>
+<link rel="stylesheet" href="{% url static path="exodus.css" %}" title="Default" type="text/css"> 
+</HEAD>
+<BODY>
+<TABLE border="0" cellspacing="0" cellpadding="0">
+  <TR>
+    <td class="menu" valign="top">
+      <div style="min-height:400px">
+	  <A name="top"><img width="130" height="82" alt="logo" src="{% url static path="logo.png" %}" border=0></A><br>
+	  <img width="130" height="97" alt="foto" src="{% url static path="foto.jpg" %}"/><br>
+	  &nbsp;&nbsp;<A class="menu" href="{% url root %}">Exodus</a><br>
+	  &nbsp;&nbsp;<A class="menu" href="{% url exodus.views.viewNodelist %}">Node List</a><br>
+	  &nbsp;&nbsp;<A class="menu" href="{% url exodus.views.genericModel "add" "location" "new" %}">Add Location</a><br>
+	  &nbsp;&nbsp;<A class="menu" href="{% url exodus.views.genericNode "add" "new" %}">Add Node</a><br>
+	  <center>--- Admin ---</center>
+	  &nbsp;&nbsp;<A class="menu" href="{% url exodus.views.viewList "location" %}">Location List</a><br>
+	  &nbsp;&nbsp;<A class="menu" href="{% url exodus.views.viewList "network" %}">Network List</a><br>
+	  &nbsp;&nbsp;<A class="menu" href="{% url exodus.views.viewList "dnsServer" %}">DNS List</a><br>
+	  </div>
+	</td>
+	<td valign="top" class="right">
+
+	<table border=0 cellspacing="0" cellpadding="0">
+	  <tr>
+	    <td class="titlel">&nbsp;</td>
+		<td class="title">
+		  <a class="title" href="http://www.wirelessleiden.nl">WirelessLeiden.NL</a>
+	    </td>
+		<td class="titler">&nbsp;</td>
+	  </tr>
+	  <tr>
+		<td class="menu2l">&nbsp;</td> 
+		<td class="menu2">
+		  <table border="0">
+		    <tr>
+			  <td class="menu2table">Exodus</td>
+			</tr>
+		  </table>
+		</td>
+		<td class="menu2r">&nbsp;</td>
+	  </tr>
+   	  <tr>
+		<td class="mainl">&nbsp;</td>
+		<td class="main"> 
+		  <br>
+		  {% block content %}	 {% endblock %}
+	 	</td>
+	    <td class="mainr">&nbsp;</td>
+	  </tr>
+	  <tr><td class="bottom" colspan="3"></td></tr>
+	</table>
+	</td>
+  </TR>
+  <tr>
+    <td class="disclaimer" colspan="2">
+    <a class="disclaimer" href="http://www.wirelessleiden.nl/stichting/disclaimer.shtml">disclaimer &copy; 2008</a> 
+	&nbsp;|&nbsp;<a class="disclaimer" href="#top">top</a>
+	</td>
+  </tr>
+</TABLE>
+</BODY>
+
+</HTML>
Index: branches/exodus-roland/exodus/templates/freebsd-5.0/dhcpd.conf
===================================================================
--- branches/exodus-roland/exodus/templates/freebsd-5.0/dhcpd.conf	(revision 6402)
+++ branches/exodus-roland/exodus/templates/freebsd-5.0/dhcpd.conf	(revision 6402)
@@ -0,0 +1,31 @@
+{% load network %}
+# The file is WirelessLeiden specific. Please make all changes in exodus
+#
+# Generated on {% now "G:i j-n-Y" %} 
+# by {{ node.host }}
+#
+# Node: {{ node.name }}
+# File: rc.local
+
+
+default-lease-time 7200;
+max-lease-time 2592000;
+
+ddns-update-style none;
+
+option domain-name "{{ node.network }}";
+
+{% for nic in node.interface_set.all %}
+	{% for omni in nic.omni.all %}
+# {{ omni.desc }}
+subnet {% network omni.ip omni.netmask %} netmask {{ omni.netmask|subnet }} {
+	range {{ omni.dhcpstart }} {{ omni.dhcpstop }};
+	option routers {{ omni.ip }};
+	option broadcast-address {% broadcast omni.ip omni.netmask %};
+	option subnet-mask {{ omni.netmask|subnet }};
+	option domain-name-servers {{ omni.ip }};
+} {% endfor %}
+
+# {{ nic.desc }}
+subnet {% network nic.ip nic.netmask %} netmask {{ nic.netmask|subnet }} { not authoritative; }
+{% endfor %}
Index: branches/exodus-roland/exodus/templates/freebsd-5.0/named.conf
===================================================================
--- branches/exodus-roland/exodus/templates/freebsd-5.0/named.conf	(revision 6402)
+++ branches/exodus-roland/exodus/templates/freebsd-5.0/named.conf	(revision 6402)
@@ -0,0 +1,48 @@
+# The file is WirelessLeiden specific. Please make all changes in exodus
+#
+# Generated on {% now "G:i j-n-Y" %} 
+# by {{ node.host }}
+#
+# Node: {{ node.name }}
+# File: named.conf 
+
+options {
+	directory "/etc/namedb";
+	pid-file "/var/run/named.pid";
+	forwarders {
+	{% for server in node.network.dnsserver_set.all %}
+	{{server.ipaddress }}; {% endfor %}
+	};
+};
+
+
+zone "." {
+	type hint;
+	file "/etc/namedb/named.root";
+};
+
+zone "0.0.127.IN-ADDR.ARPA" {
+	type master;
+	file "/etc/namedb/localhost.rev";
+};
+
+zone "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.IP6.INT" {
+	type master;
+	file "/etc/namedb/localhost-v6.rev";
+};
+
+{#XXX: Needs some rethinking on how to allow this to be called again #}
+{% for domain in node.domains %}
+
+zone "{{domain.domainname}}" {
+	type slave;
+	file "slave.{{domain.domainname}}";
+	masters {
+		{% for server in domain.dnsserver_set.all %}
+		{{ server.ipaddress }}; {% endfor %}
+	};
+};
+
+{% endfor %}
+
+
Index: branches/exodus-roland/exodus/templates/freebsd-5.0/rc.local
===================================================================
--- branches/exodus-roland/exodus/templates/freebsd-5.0/rc.local	(revision 6402)
+++ branches/exodus-roland/exodus/templates/freebsd-5.0/rc.local	(revision 6402)
@@ -0,0 +1,20 @@
+# The file is WirelessLeiden specific. Please make all changes in exodus
+#
+# Generated on {% now "G:i j-n-Y" %}
+# by {{ node.host }}
+#
+# Node: {{ node.name }}
+# File: rc.local
+
+{% for nic in node.interface_set.all %}
+{% if nic.ssid %}
+/usr/sbin/wicontrol -i {{ nic.iface }} -s {{ nic.shortdesc }} # Nickname
+/usr/sbin/wicontrol -i {{ nic.iface }} -P 0  # PowerSave
+/usr/sbin/wicontrol -i {{ nic.iface }} -Z    # Zero SNR cache
+/usr/sbin/wicontrol -i {{ nic.iface }} -p {{ nic.wifimode }}  # {{ nic.wifidesc }}
+/usr/sbin/wicontrol -i {{ nic.iface }} -c 1  # broadcasting essid on
+/usr/sbin/wicontrol -i {{ nic.iface }} -n {{ nic.ssid }} # network name
+/usr/sbin/wicontrol -i {{ nic.iface }} -q {{ nic.ssid }} # ESSID
+/usr/sbin/wicontrol -i {{ nic.iface }} -f {{ nic.channel }} # Channel
+{% endif %}
+{% endfor %}
Index: branches/exodus-roland/exodus/templates/freebsd-5.0/rc.node.local
===================================================================
--- branches/exodus-roland/exodus/templates/freebsd-5.0/rc.node.local	(revision 6402)
+++ branches/exodus-roland/exodus/templates/freebsd-5.0/rc.node.local	(revision 6402)
@@ -0,0 +1,20 @@
+# The file is WirelessLeiden specific. Please make all changes in exodus
+#
+# Generated on {% now "G:i j-n-Y" %} 
+# by {{ node.host }}
+#
+# Node: {{ node.name }}
+# File: rc.node.local
+
+hostname="{{ node.name }}.{{ node.network }}"
+location="{{ node.location }} - {{ node.location.desc }}"
+
+# Dirty hack for use by WL proxy
+ifconfig_lo0_alias0="172.31.255.1/32"
+# Allow masterip to be linked to lo0 interface to keep it reachable all the time
+ifconfig_lo0_alias1="{{ node.masterip }}/32"
+
+{% for nic in node.interface_set.all %}
+ifconfig_{{ nic.iface }}="inet {{nic.ip}}/{{nic.netmask}}"
+{% endfor %}
+
Index: branches/exodus-roland/exodus/templates/freebsd-5.0/resolv.conf
===================================================================
--- branches/exodus-roland/exodus/templates/freebsd-5.0/resolv.conf	(revision 6402)
+++ branches/exodus-roland/exodus/templates/freebsd-5.0/resolv.conf	(revision 6402)
@@ -0,0 +1,19 @@
+# The file is WirelessLeiden specific. Please make all changes in exodus
+# next door interlink nighboors to be specified, in case of own DNS failures
+#
+# Generated on {% now "G:i j-n-Y" %} 
+# by {{ node.host }}
+#
+# Node: {{ node.name }}
+# File: resolv.conf
+
+nameserver 127.0.0.1 #localhost
+{% spaceless %}
+{% for interface in node.interface_set.all %}
+	{% for interlink in interface.interface_set.all %}
+		{% ifnotequal interlink interface %}
+nameserver {{ interlink.ip }} # {{ interlink.node }}
+		{% endifnotequal %} 
+	{% endfor %} 
+{% endfor %}
+{% endspaceless %}
Index: branches/exodus-roland/exodus/templates/genericForm.html
===================================================================
--- branches/exodus-roland/exodus/templates/genericForm.html	(revision 6402)
+++ branches/exodus-roland/exodus/templates/genericForm.html	(revision 6402)
@@ -0,0 +1,33 @@
+{% extends "exodus-template.html" %}
+
+{% block content %}
+
+<h1>{{ title }} - {{ mode }}</h1>
+{% ifnotequal mode "delete" %}
+<h2><em> {{ message }} </em></h2>
+{% endifnotequal %}
+
+<form method="post" action=".">
+{# On delete, make sure not to enable editing, and view detail instead #}
+{% ifequal mode "delete" %}
+<table>
+{% for key, value in object.as_list %}
+<tr><th>{{ key }}</th><td>:</td><td>{{ value }}</td><tr>
+{% endfor %}
+</table>
+{% include delInclude %}
+<h3>Are you sure you want to <b>DELETE</b> this data <b>PERMANENTLY</b>?</h3>
+{% else %}
+	{# Include custom code, like the javascript code #}
+	{% ifequal mode "add" %}
+		{% include addInclude %}
+	{% else %}
+		{% include editInclude %}
+	{% endifequal %}
+	<table>{{ form.as_table }}</table>
+{% endifequal %}
+<input type="submit" name="cancel" value="NO, cancel">
+<input type="submit" name="proceed" value="OK, proceed">
+</form>
+
+{% endblock %}
Index: branches/exodus-roland/exodus/templates/index.html
===================================================================
--- branches/exodus-roland/exodus/templates/index.html	(revision 6402)
+++ branches/exodus-roland/exodus/templates/index.html	(revision 6402)
@@ -0,0 +1,40 @@
+{% extends "exodus-template.html" %}
+
+{% block content %}
+
+<P class="blue">Wireless Leiden Exodus</P>
+This is the Wireless Leiden Exodus site. 
+
+Exodus is the next generation configuration management tool for
+Wiresless Leiden. Development is still ongoing and not all the features of
+<a href="http://wiki.wirelessleiden.nl/Genesis">Genesis</a>, our current management system, are already available in Exodus.  
+<p />
+<h3>New node</h3>
+<p>
+The normal workflow is to add a new node at a new location to an existing network. 
+<br>In this case:
+<ul>
+<li> <a href="/add/location/new">Add Location</a>
+<li> <a href="/add/node/new">Add Node</a>
+</ul>
+</p>
+<h3>Changes to existing nodes</h3>
+<p>
+Changes to existing nodes, such as redefining interlinks, can be made via the 
+<a href="/view/nodelist">'Node List'</a> page (klik on the node name in the list). 
+
+
+<p>TODO: More documentation is being written. </br>
+</br>
+
+</p>
+<strong>This site is under construction and not fully operational.</strong></br>
+<br />
+For questions or remarks: <a href="mailto:techniek@lijst.wirelessleiden.nl?subject=exodus%20development">techniek@lijst.wirelessleiden.nl</a> Exodus code to be found in subversion at URL <a href="http://svn.wirelessleiden.nl/svn/code/exodus/">http://svn.wirelessleiden.nl/svn/code/exodus/</a>, please feel free to send patches or participate if needed.
+
+<p />
+Enjoy Exodus!
+<br />
+<a href="mailto:roland@wirelessleiden.nl">Roland</a> and <a href="mailto:rick@wirelessleiden.nl">Rick</a>
+
+{% endblock %}
Index: branches/exodus-roland/exodus/templates/viewList.html
===================================================================
--- branches/exodus-roland/exodus/templates/viewList.html	(revision 6402)
+++ branches/exodus-roland/exodus/templates/viewList.html	(revision 6402)
@@ -0,0 +1,29 @@
+{% extends "exodus-template.html" %}
+
+{% block content %}
+
+<P class="blue">{{ modelName }} List </P>
+{# XXX: Not DRY #}
+<a href="/add/{{ modelURL }}/new/">New {{ modelName }}</a><p />
+
+{% if objects %}
+	<table border="0" cellpadding="1">
+	{% for object in objects %}
+		<tr>
+		<td>
+		{{ object }}
+		</td>
+		<td>
+		:
+		<a href="{% url exodus.views.genericModel "edit" modelURL object %}">EDIT</a>
+		<a href="{% url exodus.views.genericModel "delete" modelURL object %}">DELETE</a>
+		</td>
+		</tr>
+    {% endfor %}
+	</table>
+{% else %}
+    <p class="blue"> No {{ modelName }} are available </p>
+{% endif %}
+
+{% endblock %}
+
Index: branches/exodus-roland/exodus/templates/viewNode.html
===================================================================
--- branches/exodus-roland/exodus/templates/viewNode.html	(revision 6402)
+++ branches/exodus-roland/exodus/templates/viewNode.html	(revision 6402)
@@ -0,0 +1,73 @@
+{% extends "exodus-template.html" %}
+
+{% block content %}
+<a href="{% url exodus.views.viewNodelist %}">Node List</a>
+
+<div style="border:1px solid #000000;">
+<h1> {{ node.name }} </h1>
+<h3> Location </h3>
+Description: {{ node.location.description }} <br>
+Longitude: {{ node.location.longitude }} <br>
+Latitude: {{ node.location.latitude }} <br>
+
+Master ip: {{ node.masterip }} <br>
+<a href="{% url exodus.views.genericNode "edit" node.name %}">EDIT</a>
+<a href="{% url exodus.views.genericNode "delete" node.name %}">DELETE</a>
+</div>
+
+<h2>Interfaces</h2>
+<a href="{% url exodus.views.genericInterface "add" node.name "new" %}">Add new interface</a><p />
+
+{% for nic in node.interface_set.all %}
+	<div style="border:1px solid #000000;">
+	<div style="border:1px solid #000000;">
+	<h3>iface: {{ nic.iface }} </h3>
+	<a href="{% url exodus.views.genericPublicAP "add" node.name nic.iface "new" %}">Add new public access point</a><p />
+	Description: {{ nic.shortdesc }} <br>
+	IP: {{ nic.ip }}/{{ nic.netmask }}<br />
+	Type: {% ifequal nic nic.link %}Master{% else %}Slave {% endifequal %}<br />
+	
+	{% ifnotequal nic.type "eth" %}
+		SSID: {{ nic.ssid }} <br>
+		Channel: {{ nic.channel }} <br>
+		Antenna: {{ nic.antenna }} <br>
+		Polar: {{ nic.polar }} <br>	
+		Mode: {{ nic.mode }} <br>
+	{% endifnotequal %}
+	<a href="{% url exodus.views.genericInterface "edit" node.name nic.iface %}">EDIT</a>
+	<a href="{% url exodus.views.genericInterface "delete" node.name nic.iface %}">DELETE</a>
+	</div>
+
+	{% for interlink in nic.link.interface_set.all %}
+		{% ifnotequal interlink nic %}
+		<ul>
+		<li>Interlink to Node: <a href="{% url exodus.views.viewNode interlink.node %}">
+			{{ interlink.node }}</a> </li>
+		<li>Link Name: {{ interlink }}</li>
+		<li>IP: {{ interlink.ip }}/{{ interlink.netmask }} </li>
+		</ul>
+		{% endifnotequal %}
+	{% endfor %}
+	<ol>
+	{% for accessPoint in nic.ap.all %}
+		<div style="border:1px solid #000000;">
+		<p />
+		<li>Public Access Point: {{ accessPoint.ip}}/{{accessPoint.netmask }}
+		<a href="{% url exodus.views.genericPublicAP "edit" node.name nic.iface accessPoint.pk %}">EDIT</a>
+		<a href="{% url exodus.views.genericPublicAP "delete" node.name nic.iface accessPoint.pk %}">DELETE</a>
+		{% for dhcp in accessPoint.dhcpstatic.all %}
+			<ul>
+			    <li>Hostname: {{ dhcp.hostname }} </li>
+			    <li>MacAddress: {{ dhcp.macaddress }} </li>
+			</ul>
+		{% endfor %}
+        </li>
+		<p />
+		</div>
+	{% endfor %}
+	</ol>
+	</div>
+	<p />
+{% endfor %}
+
+{% endblock %}
Index: branches/exodus-roland/exodus/templates/viewNodelist.html
===================================================================
--- branches/exodus-roland/exodus/templates/viewNodelist.html	(revision 6402)
+++ branches/exodus-roland/exodus/templates/viewNodelist.html	(revision 6402)
@@ -0,0 +1,24 @@
+{% extends "exodus-template.html" %}
+
+{% block content %}
+
+<P class="blue"> Node List </P>
+
+{% if nodes %}
+	<table border="3" cellpadding="1">
+	{% for node in nodes %}
+		<tr>
+			<td class="list" ><a class="list"href="{% url exodus.views.viewNode node.name %}">{{ node.name }}</a></td>
+			{% for configFile in configFiles %}
+				<td><a href="{% url exodus.views.configFile "freebsd-5.0" node.name configFile %}">{{ configFile }}</a></td>
+			{% endfor %}
+		</tr>
+
+    {% endfor %}
+	</table>
+{% else %}
+    <p class="blue"> No nodes are available </p>
+{% endif %}
+
+{% endblock %}
+
Index: branches/exodus-roland/exodus/templatetags/network.py
===================================================================
--- branches/exodus-roland/exodus/templatetags/network.py	(revision 6402)
+++ branches/exodus-roland/exodus/templatetags/network.py	(revision 6402)
@@ -0,0 +1,45 @@
+from django import template
+from django.template import resolve_variable
+from exodus import wllogic
+
+register = template.Library()
+
+class NetworkNode(template.Node):
+	def __init__(self, address, subnet):
+		self.address = address
+		self.subnet = subnet
+	def render(self, context):
+		return (wllogic.getNetwork(resolve_variable(self.address, context), \
+                resolve_variable(self.subnet,context)))
+
+class BroadcastNode(template.Node):
+	def __init__(self, address, subnet):
+		self.address = address
+		self.subnet = subnet
+	def render(self, context):
+		return (wllogic.getBroadcast(resolve_variable(self.address, context), \
+                resolve_variable(self.subnet,context)))
+
+@register.tag
+def network(parser, token):
+    try:
+        # split_contents() knows not to split quoted strings.
+        tag_name, address, subnet = token.split_contents()
+    except ValueError:
+        raise template.TemplateSyntaxError, "%r tag requires a two arguments" \
+                % token.contents.split()[0]
+    return NetworkNode(address, subnet)
+
+@register.tag
+def broadcast(parser, token):
+    try:
+        # split_contents() knows not to split quoted strings.
+        tag_name, address, subnet = token.split_contents()
+    except ValueError:
+        raise template.TemplateSyntaxError, "%r tag requires a two arguments" \
+                % token.contents.split()[0]
+    return BroadcastNode(address, subnet)
+
+@register.filter
+def subnet(value):
+	return wllogic.getSubnet(value)
Index: branches/exodus-roland/exodus/tests.py
===================================================================
--- branches/exodus-roland/exodus/tests.py	(revision 6402)
+++ branches/exodus-roland/exodus/tests.py	(revision 6402)
@@ -0,0 +1,188 @@
+import unittest
+
+class AddTest(unittest.TestCase):
+    def setUp(self):
+        #Set up the client
+        from django.test.client import Client
+        self.client = Client()
+        
+        #Set up the database
+        from exodus import settings
+        self.olddbname = settings.DATABASE_NAME
+        from django.db import connection
+        self.dbname = connection.creation.create_test_db(0)
+        
+        from exodus.models import Location
+
+        self.location = Location(description = 'Hooigracht', \
+            longitude = '45.675433', latitude = '56.543332')
+        self.location.save()
+
+    def tearDown(self):
+        from django.db import connection
+        connection.creation.destroy_test_db(self.olddbname, 0)
+
+    def test_addlocation(self):
+        post_data = {'description': 'JohnLaan2', 'longitude': '34.55', \
+            'latitude' : '44.55', 'proceed': 'OK, proceed'}
+        response = self.client.post('/add/location/new/', post_data) 
+
+        self.failUnlessEqual(response.status_code, 302)
+        self.failUnlessEqual(response['Location'], \
+                'http://testserver/add/node/new/?location=2')
+
+        from exodus.models import Location
+    
+        l = Location.objects.get(description = 'JohnLaan2')
+        self.failUnless(str(l.longitude), '34.55')
+        self.failUnless(str(l.latitude), '44.55')
+
+    def test_addnode(self):
+        post_data = {'name' : 'Tabitha', 'location' : 1, \
+            'status' : 'up', 'network' : 1, 'proceed': 'OK, proceed'}
+        response = self.client.post('/add/node/new/', post_data) 
+
+        self.failUnlessEqual(response.status_code, 302)
+        self.failUnlessEqual(response['Location'], \
+                'http://testserver/view/node/Tabitha/')
+
+        from exodus.models import Node
+    
+        try:
+             n = Node.objects.get(name = 'Tabitha')
+        except Node.DoesNotExist: 
+            self.fail("Node wasn't added.")
+
+        node = n 
+        self.failUnlessEqual(n.location.id, 1)
+        self.failUnlessEqual(n.status, 'up')
+        self.failUnlessEqual(n.network.id, 1) 
+
+        c = {}
+
+        for i in n.as_list():
+           c[i[0]] = i[1] 
+
+        self.failUnless(c.has_key('masterip'))
+
+class wllogic(unittest.TestCase):
+    def setUp(self):
+        #Set up the client
+        from django.test.client import Client
+        self.client = Client()
+        
+        #Set up the database
+        from django.db import connection
+        from exodus import settings
+        self.olddbname = settings.DATABASE_NAME
+        self.dbname = connection.creation.create_test_db(0)
+
+    def tearDown(self):
+        from django.db import connection
+        connection.creation.destroy_test_db(self.olddbname, 0)
+
+    def test_new_SSID_name(self):
+        class node(object):
+            name = 'CeTIM'
+            class network(object):
+                name = 'wleiden.net'
+        nic = 'ath0'
+        desc = '2cope'
+        from exodus.wllogic import newSSIDName
+        ssid = newSSIDName(node(), nic, desc)        
+        
+        self.failUnless(ssid, '2cope-ath0.CeTIM.wleiden.net')
+
+    def test_parse_show_addr(self):
+        ip = '172.16.2.0'
+        from exodus.wllogic import parse_addr
+        parsed = parse_addr(ip)
+        self.failUnlessEqual(str(parsed), '2886730240')
+
+        from exodus.wllogic import show_addr
+        o_ip = show_addr(parsed)
+        self.failUnlessEqual(o_ip, ip)
+
+        # when using an address larger then 256, move 1 address up.
+        ip = '172.16.2.256'
+        o_ip = show_addr(parse_addr(ip))
+        self.failUnlessEqual(o_ip, '172.16.3.0')
+
+    def test_netmask2subnet(self):
+        from wllogic import netmask2subnet
+
+        self.failUnlessRaises(ValueError, netmask2subnet, 33)
+        self.failUnlessRaises(ValueError, netmask2subnet, -1)
+
+        valid_subnet = netmask2subnet(30)
+        self.failUnlessEqual(str(valid_subnet), '17179869180')
+    
+    def test_getSubnet(self):
+        from wllogic import getSubnet
+        self.failUnlessRaises(ValueError, getSubnet, -1)
+        self.failUnlessRaises(ValueError, getSubnet, 33)
+        
+        valid_netmask = getSubnet(30)
+        self.failUnless(valid_netmask, '255.255.255.252')
+    
+    def test_network(self):
+        from wllogic import network
+        from wllogic import show_addr
+        network_addr = show_addr(network('172.18.5.10', 24))
+        self.failUnlessEqual(network_addr, '172.18.5.0')
+
+    def test_broadcast(self):
+        from wllogic import broadcast
+        from wllogic import show_addr
+        
+        broadcast_addr = show_addr(broadcast('172.16.5.232', 24))
+        self.failUnlessEqual(broadcast_addr, '172.16.5.255')
+
+        self.failUnlessRaises(ValueError, broadcast, '172.16.5.1', -1)
+        self.failUnlessRaises(ValueError, broadcast, '172.16.5.1', 33)
+    
+    def test_getNetwork(self):
+        from wllogic import getNetwork
+        network_addr = getNetwork('172.17.2.2', 30)
+        self. failUnlessEqual(network_addr, '172.17.2.0')
+
+        self.failUnlessRaises(ValueError, getNetwork, '172.17.2.0', -1)
+        self.failUnlessRaises(ValueError, getNetwork, '172.17.2.0', 33)
+
+    def test_getBroadcast(self):
+        from wllogic import getBroadcast
+        broadcast_addr = getBroadcast('172.17.2.2', 24)
+        self.failUnlessEqual(broadcast_addr, '172.17.2.255')
+        
+        self.failUnlessRaises(ValueError, getBroadcast, '172.17.2.0', -1)
+        self.failUnlessRaises(ValueError, getBroadcast, '172.17.2.0', 33)
+
+    def test_free_masterip(self):
+        from wllogic import free_masterip
+        from exodus.models import Node, Network
+
+        node = Node.objects.create(location_id = 1, \
+                masterip = '172.16.0.1', name = 'testnode')
+        network = Network.objects.get(pk=1)
+        ip = free_masterip(network) 
+        self.failIfEqual(ip, '172.16.0.1') 
+        self.failUnlessEqual(ip, '172.16.1.1')
+        
+    def test_freePublicAPIP(self):
+        from wllogic import freePublicAPIP
+        self.fail('test not implemented')
+
+    def test_freeInterlinkIP(self):
+        from wllogic import freeInterlinkIP
+        self.fail('Test not implemented')
+
+    def test_addInterlinkIP(self):
+        from wllogic import addInterlinkIP
+        self.fail('Test not implemented')
+    
+def suite():
+    s = unittest.TestSuite()
+    s.addTest(unittest.makeSuite(AddTest))
+    s.addTest(unittest.makeSuite(wllogic))
+
+    return s
Index: branches/exodus-roland/exodus/urls.py
===================================================================
--- branches/exodus-roland/exodus/urls.py	(revision 6402)
+++ branches/exodus-roland/exodus/urls.py	(revision 6402)
@@ -0,0 +1,42 @@
+# (c) Roland van Laar
+
+from django.conf.urls.defaults import *
+from exodus.models import *
+from os import path as os_path
+from django.conf import settings
+from django.views.generic.simple import direct_to_template
+from django.contrib import databrowse
+from django.contrib import admin
+admin.autodiscover()
+
+#
+# experimental databrowse code
+
+urlpatterns = patterns('',
+	# generic view
+	url(r'^$', 'exodus.urls.direct_to_template', {'template': 'index.html'}, \
+            "root"),
+	(r'^view/nodelist/$', 'exodus.views.viewNodelist'),
+	(r'^view/list/(?P<model>.+)/$', 'exodus.views.viewList'),
+	(r'^view/node/(?P<node>.+)/$', 'exodus.views.viewNode'),
+
+	# config urls
+	(r'^config/(?P<version>.+)/(?P<node>.+)/(?P<file>.+)$', 'exodus.views.configFile'),
+
+	# Generic urls
+	(r'^(?P<mode>(add|edit|delete))/node/(?P<node>.+)/$', 'exodus.views.genericNode'),
+	(r'^(?P<mode>(add|edit|delete))/nic/(?P<node>.+)/(?P<interface>.+)/$', 'exodus.views.genericInterface'),
+	(r'^(?P<mode>(add|edit|delete))/dhcp/(?P<node>.+)/(?P<interface>.+)/(?P<publicAP>.+)/$', 'exodus.views.genericPublicAP'),
+	(r'^(?P<mode>(add|edit|delete))/(?P<model>.+)/(?P<object>.+)/$', 'exodus.views.genericModel'),
+
+	# 3th party/embedded database browser
+	(r'^databrowse/(.*)', databrowse.site.root),
+
+    (r'^admin/(.*)', admin.site.root),
+)
+
+if settings.DEBUG:
+	urlpatterns += patterns('',
+	url(r'^static/(?P<path>.*)$$', 'django.views.static.serve', {'document_root': 
+	 settings.EXODUS_STATIC_ROOT }, "static"),
+	)
Index: branches/exodus-roland/exodus/utils.py
===================================================================
--- branches/exodus-roland/exodus/utils.py	(revision 6402)
+++ branches/exodus-roland/exodus/utils.py	(revision 6402)
@@ -0,0 +1,5 @@
+from exodus.settings import DEBUG
+
+def pdebug(level, message):
+    if level > 0 and DEBUG:
+        print "DEBUG [%s] %s" % (level, message)
Index: branches/exodus-roland/exodus/views.py
===================================================================
--- branches/exodus-roland/exodus/views.py	(revision 6402)
+++ branches/exodus-roland/exodus/views.py	(revision 6402)
@@ -0,0 +1,395 @@
+# (c) Roland van Laar 2006
+
+from django.db import models
+from django.http import HttpResponse, HttpResponseRedirect
+from django.template import Context, loader
+from django.shortcuts import render_to_response
+from django.core.urlresolvers import reverse, NoReverseMatch
+from django.forms.formsets import formset_factory
+from django import forms
+from socket import gethostname 
+
+from exodus.models import *
+from exodus.wllogic import free_masterip, newSSIDName, addInterlinkIP, \
+		freeInterlinkIP, freePublicAPIP
+
+from exodus.utils import pdebug
+
+class GenericHandler(object):
+	"""Conventions used: type has to to the same name as the dependency 
+	object, delet template is named delete<type>.html
+	"""
+	def __init__(self, request, mode):
+		pdebug(100, "Function: GenericHandler.__init__")
+		# Strip 'Handler' of name
+		type = self.__class__.__name__[:-7:]
+		formClass = eval(type + 'Form')
+
+		self.object = eval('self.' + type[0].lower() + type[1::])
+		self.title = self.object._meta.verbose_name
+		self.request = request
+		self.mode = mode
+		self.is_changed = {}
+		
+		if request.POST.has_key('cancel'):
+			pdebug(100, "Action cancel")
+			self.form_action = 'cancel'
+			# Allow overriding cancel call, in particular with regards to the
+			# response call
+			self._cancel()
+		elif request.POST.has_key('proceed'):
+			pdebug(100, "Action proceed")
+			self.form_action = 'proceed'
+			
+			# Delete does not require a valid form
+			if mode == 'delete':
+				self._delete()
+			else:
+				# First checking whether form is valid, then add/edit actions
+				self.form = formClass(request.POST, instance=self.object)
+				try:
+					if self.form.is_valid():
+						pdebug(100, "Form valid")
+						# Set response on forehand, to allow override \
+						# in procedure
+						try:
+							self.response = HttpResponseRedirect(reverse( \
+								'exodus.views.viewNode', args=[self.node.name]))
+						except (AttributeError, NoReverseMatch):
+							self.response = HttpResponseRedirect(reverse( \
+								'exodus.views.viewNodelist'))
+						
+						#Checking whether data did change
+						_oldInstance = dict(self.object.as_list())
+						_instance  = dict(self.form.save(commit=False).as_list())
+						for key,value in _instance.items():
+							if value != _oldInstance[key]:
+								pdebug(100, "Key %s changed value '%s' -> '%s'"\
+									% (key, _oldInstance[key], value))
+								self.is_changed[key] = value
+						
+						# Call override procedure
+						if mode == 'add':
+							self._add()
+						elif mode == 'edit':
+							self._edit()
+						else:
+							raise ValueError, 'Mode "%s" not defined' % mode
+					else:
+						raise ValueError, 'Form error, please edit and resubmit'
+				except ValueError, message:
+					self.response = render_to_response('genericForm.html', {
+						'form': self.form, 'message' : message, 
+						'title' : self.title, 'mode' : mode, 
+						'type' : type, 'object': self.object,
+						'delInclude' : "delete" + type.capitalize() + ".html",
+						'addInclude' : "add" + type.capitalize() + ".html",
+						'editInclude' : "edit" + type.capitalize() + ".html" })
+		else:
+			message = 'Please edit and submit'
+			
+			# Dirty? hack to allow initial form to be filled with date \
+			# for GET request, no errors raised
+			if request.GET and mode == 'add':
+				self.form = formClass(request.GET, instance=self.object)
+				self.form._errors = {}
+			else:
+				self.form = formClass(instance=self.object)
+			
+			self.response = render_to_response('genericForm.html', {
+				'form': self.form, 'message' : message, 'title' : self.title,
+				'mode' : mode, 'type' : type, 'object': self.object,
+				'delInclude' : "delete" + type.capitalize() + ".html",
+				'addInclude' : "add" + type.capitalize() + ".html",
+				'editInclude' : "edit" + type.capitalize() + ".html" })
+	
+	def _add(self):
+		pdebug(100, "Function: GenericHandler._add")
+		self.form.save()
+	
+	def _edit(self):
+		pdebug(100, "Function: GenericHandler._edit")
+		self.form.save()
+	
+	def _delete(self):
+		pdebug(100, "Function: GenericHandler._delete")
+		self.object.delete()
+		self.response = HttpResponseRedirect(reverse('exodus.views.viewNode',\
+				args=[self.node.name]))
+	
+	def _cancel(self):
+		pdebug(100, "Function: GenericHandler._cancel")
+		self.response = HttpResponseRedirect(reverse('exodus.views.viewNode',\
+				 args=[self.node.name]))
+		#self.response = HttpResponseRedirect(reverse('exodus.views.viewNodelist'))
+	
+	def render_to_response(self):
+		pdebug(100, "Function: GenericHandler.render_to_response")
+		return self.response
+
+
+#
+# PublicAP
+class PublicAPForm(forms.ModelForm):
+	class Meta:
+		model = PublicAP
+		exclude = ('shortdesc', 'desc', 'ip', 'dhcpstart', 'dhcpstop')
+
+class PublicAPHandler(GenericHandler):
+
+	def __init__(self, request, node, interface, publicAP, mode):
+		pdebug(100, "Function: PublicAPHandler.__init__")
+		self.node = Node.objects.get(name=node)
+		self.interface = Interface.objects.get(node=self.node, iface=interface)
+		if mode == 'add':
+			self.publicAP = PublicAP(iface=self.interface)
+		else:
+			self.publicAP =  PublicAP.objects.get(iface=self.interface, \
+					pk=publicAP)
+		super(PublicAPHandler, self).__init__(request, mode)
+
+	def _add(self):
+		pdebug(100, "Function: PublicAPHandler._add")
+		_instance = self.form.save(commit=False)
+		# Find IP range inside interface range with disired size/subnet
+		_instance.ip = freePublicAPIP(_instance.iface, _instance.netmask)
+		_instance.dhcpstart = 1
+		_instance.dhcpstop = 2
+		# If wireless generate ssid name
+		_instance.ssid = newSSIDName(_instance.iface.node, _instance.iface, \
+				'omni')
+		_instance.save()
+
+def genericPublicAP(request, node, interface, publicAP, mode):
+	pdebug(100, "Function: genericPublicAP")
+	handler = PublicAPHandler(request, node, interface, publicAP, mode)
+	return handler.render_to_response()
+
+#
+# Interface 
+class InterfaceForm(forms.ModelForm):
+	class Meta:
+		model = Interface
+		exclude = ( 'ip', 'ssid', 'mode', 'channel', 'shortdesc', \
+				'netmask' )
+
+class InterfaceHandler(GenericHandler):
+	def __init__(self, request, node, interface, mode):
+		pdebug(100, "Function: InterfaceHandler.__init__")
+		self.node = Node.objects.get(name=node)
+		if mode == 'add':
+			self.interface = Interface(node=self.node)
+		else:
+			self.interface = Interface.objects.get(node=self.node, \
+				iface=interface)
+		super(InterfaceHandler, self).__init__(request, mode)
+
+	def _add(self):
+		pdebug(100, "Function: InterfaceHandler._add")
+		self._saveInterface()
+
+	def _edit(self):
+		pdebug(100, "Function: InterfaceHandler._edit")
+		self._saveInterface()
+
+	def _saveInterface(self):
+		pdebug(100, "Function: InterfaceHandler._saveInterface")
+		_instance  = self.form.save(commit=False)
+		if _instance.link and (_instance.type != _instance.link.type):
+			raise ValueError,'Type of local and remote interface needs to match'
+		if str(_instance.type) != "eth":
+			_instance.ssid = newSSIDName(_instance.node, _instance.iface, \
+					'unused')
+			_instance.channel = '1'
+			_instance.mode = 1 # set to master
+		
+		# Only change IP if changes in interface link/mask or new of course :-)
+		if self.mode == 'add' or self.is_changed.has_key('link') or \
+				self.is_changed.has_key('netmask'):
+			if not _instance.link:
+				_instance.ip = freeInterlinkIP(_instance)
+			else:
+				_instance.ip = addInterlinkIP(_instance.link)
+
+		# XXX: Change in netmask requires full range of netmask changes \
+		# on slaves
+		_instance.save()
+		#Dirty to hack to get reference to self working
+		if not _instance.link:
+			_instance.link = _instance
+			_instance.save()
+
+def genericInterface(request, node, interface, mode):
+	pdebug(100, "Function: genericInterface")
+	handler = InterfaceHandler(request, node, interface, mode)
+	return handler.render_to_response()
+
+#
+# Node
+class NodeForm(forms.ModelForm):
+	class Meta:
+		model = Node
+		exclude = ( 'masterip' )
+
+class NodeHandler(GenericHandler):
+	def __init__(self, request, node, mode):
+		pdebug(100, "Function: NodeHandler.__init__")
+		if mode == 'add':
+			self.node = Node()
+		else:
+			self.node = Node.objects.get(name=node)
+		super(NodeHandler, self).__init__(request, mode)
+
+	def _add(self):
+		pdebug(100, "Function: NodeHandler._add")
+		# input a valid master ip into new_data
+		_instance  = self.form.save(commit=False)
+		_instance.masterip = free_masterip(_instance.network)
+		_instance.save()
+		self.response = HttpResponseRedirect(reverse('exodus.views.viewNode', \
+				args=[_instance.name]))
+
+	def _delete(self):
+		pdebug(100, "Function: NodeHandler._delete")
+		for _master in Interface.objects.filter(node=self.object):
+			if _master.link == _master:
+				for _makeMaster in Interface.objects.filter(link=_master):
+					_makeMaster.link = _makeMaster
+					_makeMaster.save()
+		self.object.delete()
+		# As node is deleted, goto overview page
+		self.response = HttpResponseRedirect(reverse( \
+					'exodus.views.viewNodelist'))
+
+	def _cancel(self):
+		pdebug(100, "Function: NodeHandler._cancel")
+		if self.mode == 'new':
+			self.response = HttpResponseRedirect(reverse( \
+					'exodus.views.viewNodelist'))
+		else:
+			self.response = HttpResponseRedirect(reverse( \
+					'exodus.views.viewNode', args=[self.node.name]))
+
+def genericNode(request, node, mode):
+	pdebug(100, "Function: genericNode")
+	handler = NodeHandler(request, node, mode)
+	return handler.render_to_response()
+
+#
+# location
+class LocationForm(forms.ModelForm):
+	class Meta:
+		model = Location
+
+class LocationHandler(GenericHandler):
+	def __init__(self, request, location, mode):
+		pdebug(100, "Function: LocationHandler.__init__")
+		if mode == 'add':
+			self.location = Location()
+		else:
+			self.location = Location.objects.get(description=location)
+	
+		super(LocationHandler,self).__init__(request, mode)
+
+	def _add(self):
+		pdebug(100, "Function: LocationHandler._add")
+		_instance = self.form.save()
+		# After adding a location, allow adding a Node with this location
+		self.response = HttpResponseRedirect( \
+				reverse('exodus.views.genericNode', args=["add", "new"]) + \
+				"?location=%i" % _instance.pk)
+
+	def _delete(self):
+		pdebug(100, "Function: LocationHandler._delete")
+		self.object.delete()
+		self.response = HttpResponseRedirect(reverse( \
+				'exodus.views.viewNodelist'))
+
+	def _cancel(self):
+		pdebug(100, "Function: LocationHandler._cancel")
+		self.response = HttpResponseRedirect(reverse( \
+				'exodus.views.viewNodelist'))
+
+#
+# Views
+def viewNode(request, node):
+	pdebug(100, "Function: viewNode")
+	node = Node.objects.get(name=node)
+	return render_to_response('viewNode.html', {'node': node})
+
+def viewNodelist(request):
+	pdebug(100, "Function: viewNodelist")
+	nodes = Node.objects.all()
+	configFiles = (
+		'rc.local',
+		'rc.node.local',
+		'dhcpd.conf',
+		'named.conf',
+		'resolv.conf' )
+	return render_to_response('viewNodelist.html', {'nodes' : nodes, \
+			'configFiles' : configFiles})
+
+def configFile(request, version, node, file):
+	pdebug(100, "Function: configFile")
+	node = Node.objects.get(name=node)
+	
+	# Extra statictics information for use of generation
+	server = {}
+	server['host'] = gethostname()
+	
+	templateFile = version + '/' + file
+	return render_to_response(templateFile, {'node' : node, 'server' : server},\
+			mimetype='text/plain')
+
+#
+# DnsServer
+class DnsServerForm(forms.ModelForm):
+	class Meta:
+		model = DnsServer
+
+class DnsServerHandler(GenericHandler):
+	def __init__(self, request, dnsServer, mode):
+		pdebug(100, "Function: DnsServerHandler.__init__")
+		if mode == 'add':
+			self.dnsServer = DnsServer()
+		else:
+			self.dnsServer= DnsServer.objects.get(ipaddress=dnsServer)
+		super(DnsServerHandler, self).__init__(request, mode)
+
+	def _add(self):
+		pdebug(100, "Function: DnsServerHandler._add")
+		_instance = self.form.save()
+		self.response = HttpResponseRedirect(reverse('exodus.views.viewList', \
+				args=['dnsServer']))
+
+	def _delete(self):
+		pdebug(100, "Function: DnsServerHandler._delete")
+		self.object.delete()
+		self.response = HttpResponseRedirect(reverse('exodus.views.viewList', \
+				args=['dnsServer']))
+
+	def _cancel(self):
+		pdebug(100, "Function: DnsServerHandler._cancel")
+		self.response = HttpResponseRedirect(reverse('exodus.views.viewList', \
+				args=['dnsServer']))
+
+def genericModel(request, model, mode, object):
+	pdebug(100, "Function: genericModel %s, %s, %s" % (model, mode, object))
+	"""Wrapper, to get to the function needed"""
+	model = model[0].upper() + model[1::]
+	model = eval(model + 'Handler')
+	handler = model(request, object, mode)
+	return handler.render_to_response()
+
+def viewList(request, model):
+	pdebug(100, "Function: viewList")
+	"""Standard interface for simple overview pages, with view/edit/delete 
+	buttons on it
+	"""
+	modelURL = model
+	model = model[0].upper() + model[1::]
+	modelName = model
+	model = eval(model)
+	objects = model.objects.all()
+	return render_to_response('viewList.html', {'objects': objects, \
+			'modelURL' : modelURL, 'modelName' : modelName})
Index: branches/exodus-roland/exodus/wllogic.py
===================================================================
--- branches/exodus-roland/exodus/wllogic.py	(revision 6402)
+++ branches/exodus-roland/exodus/wllogic.py	(revision 6402)
@@ -0,0 +1,145 @@
+# (c) Roland van Laar
+
+from exodus.models import Node, Interface, PublicAP
+from exodus.settings import MASTERIP_NETMASK
+
+def newSSIDName(node, nic, desc):
+	"""Generates a new ssid name for a new wifi NIC"""
+
+	# nic is used instead of nic.iface, because string nic is passed on, 
+	# instead of object nic.
+
+	return "%s-%s.%s.%s" % (desc, nic, node.name, node.network.name)
+
+#
+# Taken from lvoege@gmail.com's getrange.py
+#
+def parse_addr(s):
+	"""Remember when using an address has a section which is higher than
+	255, such as 172.16.0.256, the ip address from show_addr turns out as
+	172.16.1.0
+	"""
+	f = s.split('.')
+	return (long(f[0]) << 24L) + \
+		(long(f[1]) << 16L) + \
+		(long(f[2]) << 8L) + \
+		long(f[3])
+
+def show_addr(a):
+	return "%d.%d.%d.%d" % ((a >> 24) & 0xff, (a >> 16) & 0xff, (a >> 8) & 0xff, a & 0xff)
+
+def netmask2subnet(s):
+	"""IPv4 netmask to subnet"""
+	if s < 0 :
+		raise ValueError, 'subnet is too small'
+	return (0xffffffff << (32 - s))
+
+def getSubnet(netmask):
+	return(show_addr(netmask2subnet(netmask)))
+
+def network(address, netmask):
+	"""IPv4 network address when address and netmask are given"""
+	return(parse_addr(address) & netmask2subnet(netmask))
+
+def broadcast(address, netmask):
+	"""IPv4 network address when address and netmask are given"""
+	if netmask > 32:
+		raise ValueError, 'netmask too large'
+	return(parse_addr(address) | 0xffffffff >> netmask)
+
+def getNetwork(address, netmask):
+	return(show_addr(network(address,netmask)))
+
+def getBroadcast(address, netmask):
+	return(show_addr(broadcast(address,netmask)))
+
+#XXX: free_masterip/addInterlinkIP should be more general by writing a function
+#     which finds a range based on a given range, taken ip/netmask and 
+#     requested subnet
+
+def free_masterip(city_network, netmask = None):
+	if netmask == None:
+		netmask = MASTERIP_NETMASK
+
+	if netmask < 0 or netmask > 32 :
+		raise ValueError, 'netmask out of bounds'	
+
+	taken = {}
+
+	for node in Node.objects.all():
+		addr = network(node.masterip, netmask)
+		taken[addr] = 1
+    
+	numaddrs = 1 << (32 - netmask)
+
+	#XXX: No out of bond checking done yet
+	i = parse_addr(city_network.ipspacestart)
+	while taken.has_key(i):
+		i = i + numaddrs
+    
+    # go from network address to a valid ip.
+	return show_addr(i+1)
+
+#
+# XXX: Needs merging with freeInterlinkIP as interface could have both AP, 
+# interlink defined on one link
+def freePublicAPIP(masterLink, netmask):
+	taken = {}
+
+	for interface in Interface.objects.filter(link=masterLink):
+		#Grr, needs all interfaces which are master requires a bit of a hack
+		addr = network(interface.ip, interface.netmask)
+		addrMax = broadcast(interface.ip, interface.netmask)
+		while addr < addrMax:
+			taken[addr] = 1
+			addr = addr + 1
+
+	for accessPoint in PublicAP.objects.filter(iface=masterLink):
+		#Grr, needs all interfaces which are master requires a bit of a hack
+		addr = network(accessPoint.ip, accessPoint.netmask)
+		addrMax = broadcast(accessPoint.ip, accessPoint.netmask)
+		while addr < addrMax:
+			taken[addr] = 1
+			addr = addr + 1
+
+	#Should be dynamic based on the number of hosts in here
+	size = netmask
+	numaddrs = 1 << (32 - size)
+	i = network(masterLink.ip,masterLink.netmask)
+	while taken.has_key(i):
+		i = i + numaddrs
+	
+	return show_addr(i)
+
+def freeInterlinkIP(masterLink):
+	taken = {}
+	
+	for interface in Interface.objects.filter(node=masterLink.node):
+		#Grr, needs all interfaces which are master requires a bit of a hack
+		addr = network(interface.ip, interface.netmask)
+		taken[addr] = 1
+
+	#Should be dynamic based on the number of hosts in here
+	size = masterLink.netmask
+	numaddrs = 1 << (32 - size)
+	i = network(masterLink.node.masterip,24) + 4
+	while taken.has_key(i):
+		i = i + numaddrs
+	
+	return show_addr(i)
+
+def addInterlinkIP(masterLink):
+	taken = {}
+	
+	for interface in Interface.objects.filter(link=masterLink):
+		#Grr, needs all interfaces which are master requires a bit of a hack
+		addr = parse_addr(interface.ip)
+		taken[addr] = 1
+
+	size = 32
+	numaddrs = 1 << (32 - size)
+	i = network(masterLink.ip, masterLink.netmask) + 1
+	while taken.has_key(i):
+		i = i + 1
+	
+	return show_addr(i)
Index: branches/exodus-roland/initial_data.json
===================================================================
--- branches/exodus-roland/initial_data.json	(revision 6402)
+++ branches/exodus-roland/initial_data.json	(revision 6402)
@@ -0,0 +1,487 @@
+[
+  {
+    "pk": 22, 
+    "model": "auth.permission", 
+    "fields": {
+      "codename": "add_logentry", 
+      "name": "Can add log entry", 
+      "content_type": 8
+    }
+  }, 
+  {
+    "pk": 23, 
+    "model": "auth.permission", 
+    "fields": {
+      "codename": "change_logentry", 
+      "name": "Can change log entry", 
+      "content_type": 8
+    }
+  }, 
+  {
+    "pk": 24, 
+    "model": "auth.permission", 
+    "fields": {
+      "codename": "delete_logentry", 
+      "name": "Can delete log entry", 
+      "content_type": 8
+    }
+  }, 
+  {
+    "pk": 4, 
+    "model": "auth.permission", 
+    "fields": {
+      "codename": "add_group", 
+      "name": "Can add group", 
+      "content_type": 2
+    }
+  }, 
+  {
+    "pk": 10, 
+    "model": "auth.permission", 
+    "fields": {
+      "codename": "add_message", 
+      "name": "Can add message", 
+      "content_type": 4
+    }
+  }, 
+  {
+    "pk": 1, 
+    "model": "auth.permission", 
+    "fields": {
+      "codename": "add_permission", 
+      "name": "Can add permission", 
+      "content_type": 1
+    }
+  }, 
+  {
+    "pk": 7, 
+    "model": "auth.permission", 
+    "fields": {
+      "codename": "add_user", 
+      "name": "Can add user", 
+      "content_type": 3
+    }
+  }, 
+  {
+    "pk": 5, 
+    "model": "auth.permission", 
+    "fields": {
+      "codename": "change_group", 
+      "name": "Can change group", 
+      "content_type": 2
+    }
+  }, 
+  {
+    "pk": 11, 
+    "model": "auth.permission", 
+    "fields": {
+      "codename": "change_message", 
+      "name": "Can change message", 
+      "content_type": 4
+    }
+  }, 
+  {
+    "pk": 2, 
+    "model": "auth.permission", 
+    "fields": {
+      "codename": "change_permission", 
+      "name": "Can change permission", 
+      "content_type": 1
+    }
+  }, 
+  {
+    "pk": 8, 
+    "model": "auth.permission", 
+    "fields": {
+      "codename": "change_user", 
+      "name": "Can change user", 
+      "content_type": 3
+    }
+  }, 
+  {
+    "pk": 6, 
+    "model": "auth.permission", 
+    "fields": {
+      "codename": "delete_group", 
+      "name": "Can delete group", 
+      "content_type": 2
+    }
+  }, 
+  {
+    "pk": 12, 
+    "model": "auth.permission", 
+    "fields": {
+      "codename": "delete_message", 
+      "name": "Can delete message", 
+      "content_type": 4
+    }
+  }, 
+  {
+    "pk": 3, 
+    "model": "auth.permission", 
+    "fields": {
+      "codename": "delete_permission", 
+      "name": "Can delete permission", 
+      "content_type": 1
+    }
+  }, 
+  {
+    "pk": 9, 
+    "model": "auth.permission", 
+    "fields": {
+      "codename": "delete_user", 
+      "name": "Can delete user", 
+      "content_type": 3
+    }
+  }, 
+  {
+    "pk": 13, 
+    "model": "auth.permission", 
+    "fields": {
+      "codename": "add_contenttype", 
+      "name": "Can add content type", 
+      "content_type": 5
+    }
+  }, 
+  {
+    "pk": 14, 
+    "model": "auth.permission", 
+    "fields": {
+      "codename": "change_contenttype", 
+      "name": "Can change content type", 
+      "content_type": 5
+    }
+  }, 
+  {
+    "pk": 15, 
+    "model": "auth.permission", 
+    "fields": {
+      "codename": "delete_contenttype", 
+      "name": "Can delete content type", 
+      "content_type": 5
+    }
+  }, 
+  {
+    "pk": 25, 
+    "model": "auth.permission", 
+    "fields": {
+      "codename": "add_antenna", 
+      "name": "Can add ExtendedModel", 
+      "content_type": 9
+    }
+  }, 
+  {
+    "pk": 49, 
+    "model": "auth.permission", 
+    "fields": {
+      "codename": "add_dhcpstatic", 
+      "name": "Can add Static host", 
+      "content_type": 17
+    }
+  }, 
+  {
+    "pk": 31, 
+    "model": "auth.permission", 
+    "fields": {
+      "codename": "add_dnsserver", 
+      "name": "Can add Dns Server", 
+      "content_type": 11
+    }
+  }, 
+  {
+    "pk": 40, 
+    "model": "auth.permission", 
+    "fields": {
+      "codename": "add_interface", 
+      "name": "Can add Interface", 
+      "content_type": 14
+    }
+  }, 
+  {
+    "pk": 43, 
+    "model": "auth.permission", 
+    "fields": {
+      "codename": "add_interfacealias", 
+      "name": "Can add ExtendedModel", 
+      "content_type": 15
+    }
+  }, 
+  {
+    "pk": 28, 
+    "model": "auth.permission", 
+    "fields": {
+      "codename": "add_location", 
+      "name": "Can add Location", 
+      "content_type": 10
+    }
+  }, 
+  {
+    "pk": 34, 
+    "model": "auth.permission", 
+    "fields": {
+      "codename": "add_network", 
+      "name": "Can add Network", 
+      "content_type": 12
+    }
+  }, 
+  {
+    "pk": 37, 
+    "model": "auth.permission", 
+    "fields": {
+      "codename": "add_node", 
+      "name": "Can add Node", 
+      "content_type": 13
+    }
+  }, 
+  {
+    "pk": 46, 
+    "model": "auth.permission", 
+    "fields": {
+      "codename": "add_publicap", 
+      "name": "Can add Public Access Point", 
+      "content_type": 16
+    }
+  }, 
+  {
+    "pk": 26, 
+    "model": "auth.permission", 
+    "fields": {
+      "codename": "change_antenna", 
+      "name": "Can change ExtendedModel", 
+      "content_type": 9
+    }
+  }, 
+  {
+    "pk": 50, 
+    "model": "auth.permission", 
+    "fields": {
+      "codename": "change_dhcpstatic", 
+      "name": "Can change Static host", 
+      "content_type": 17
+    }
+  }, 
+  {
+    "pk": 32, 
+    "model": "auth.permission", 
+    "fields": {
+      "codename": "change_dnsserver", 
+      "name": "Can change Dns Server", 
+      "content_type": 11
+    }
+  }, 
+  {
+    "pk": 41, 
+    "model": "auth.permission", 
+    "fields": {
+      "codename": "change_interface", 
+      "name": "Can change Interface", 
+      "content_type": 14
+    }
+  }, 
+  {
+    "pk": 44, 
+    "model": "auth.permission", 
+    "fields": {
+      "codename": "change_interfacealias", 
+      "name": "Can change ExtendedModel", 
+      "content_type": 15
+    }
+  }, 
+  {
+    "pk": 29, 
+    "model": "auth.permission", 
+    "fields": {
+      "codename": "change_location", 
+      "name": "Can change Location", 
+      "content_type": 10
+    }
+  }, 
+  {
+    "pk": 35, 
+    "model": "auth.permission", 
+    "fields": {
+      "codename": "change_network", 
+      "name": "Can change Network", 
+      "content_type": 12
+    }
+  }, 
+  {
+    "pk": 38, 
+    "model": "auth.permission", 
+    "fields": {
+      "codename": "change_node", 
+      "name": "Can change Node", 
+      "content_type": 13
+    }
+  }, 
+  {
+    "pk": 47, 
+    "model": "auth.permission", 
+    "fields": {
+      "codename": "change_publicap", 
+      "name": "Can change Public Access Point", 
+      "content_type": 16
+    }
+  }, 
+  {
+    "pk": 27, 
+    "model": "auth.permission", 
+    "fields": {
+      "codename": "delete_antenna", 
+      "name": "Can delete ExtendedModel", 
+      "content_type": 9
+    }
+  }, 
+  {
+    "pk": 51, 
+    "model": "auth.permission", 
+    "fields": {
+      "codename": "delete_dhcpstatic", 
+      "name": "Can delete Static host", 
+      "content_type": 17
+    }
+  }, 
+  {
+    "pk": 33, 
+    "model": "auth.permission", 
+    "fields": {
+      "codename": "delete_dnsserver", 
+      "name": "Can delete Dns Server", 
+      "content_type": 11
+    }
+  }, 
+  {
+    "pk": 42, 
+    "model": "auth.permission", 
+    "fields": {
+      "codename": "delete_interface", 
+      "name": "Can delete Interface", 
+      "content_type": 14
+    }
+  }, 
+  {
+    "pk": 45, 
+    "model": "auth.permission", 
+    "fields": {
+      "codename": "delete_interfacealias", 
+      "name": "Can delete ExtendedModel", 
+      "content_type": 15
+    }
+  }, 
+  {
+    "pk": 30, 
+    "model": "auth.permission", 
+    "fields": {
+      "codename": "delete_location", 
+      "name": "Can delete Location", 
+      "content_type": 10
+    }
+  }, 
+  {
+    "pk": 36, 
+    "model": "auth.permission", 
+    "fields": {
+      "codename": "delete_network", 
+      "name": "Can delete Network", 
+      "content_type": 12
+    }
+  }, 
+  {
+    "pk": 39, 
+    "model": "auth.permission", 
+    "fields": {
+      "codename": "delete_node", 
+      "name": "Can delete Node", 
+      "content_type": 13
+    }
+  }, 
+  {
+    "pk": 48, 
+    "model": "auth.permission", 
+    "fields": {
+      "codename": "delete_publicap", 
+      "name": "Can delete Public Access Point", 
+      "content_type": 16
+    }
+  }, 
+  {
+    "pk": 16, 
+    "model": "auth.permission", 
+    "fields": {
+      "codename": "add_session", 
+      "name": "Can add session", 
+      "content_type": 6
+    }
+  }, 
+  {
+    "pk": 17, 
+    "model": "auth.permission", 
+    "fields": {
+      "codename": "change_session", 
+      "name": "Can change session", 
+      "content_type": 6
+    }
+  }, 
+  {
+    "pk": 18, 
+    "model": "auth.permission", 
+    "fields": {
+      "codename": "delete_session", 
+      "name": "Can delete session", 
+      "content_type": 6
+    }
+  }, 
+  {
+    "pk": 19, 
+    "model": "auth.permission", 
+    "fields": {
+      "codename": "add_site", 
+      "name": "Can add site", 
+      "content_type": 7
+    }
+  }, 
+  {
+    "pk": 20, 
+    "model": "auth.permission", 
+    "fields": {
+      "codename": "change_site", 
+      "name": "Can change site", 
+      "content_type": 7
+    }
+  }, 
+  {
+    "pk": 21, 
+    "model": "auth.permission", 
+    "fields": {
+      "codename": "delete_site", 
+      "name": "Can delete site", 
+      "content_type": 7
+    }
+  }, 
+  {
+    "pk": 1, 
+    "model": "auth.user", 
+    "fields": {
+      "username": "admin", 
+      "first_name": "", 
+      "last_name": "", 
+      "is_active": true, 
+      "is_superuser": true, 
+      "is_staff": true, 
+      "last_login": "2008-11-28 20:23:55", 
+      "groups": [], 
+      "user_permissions": [], 
+      "password": "sha1$b2535$dbc48961d155733361f2ad083032bbbd9b125188", 
+      "email": "techniek@lijst.wirelessleiden.nl", 
+      "date_joined": "2008-11-28 20:23:55"
+    }
+  },
+  {
+    "pk": "bb3fbe95caf6f28db4fa835dad4468ca", 
+    "model": "sessions.session", 
+    "fields": {
+      "expire_date": "2009-12-12 20:26:34", 
+      "session_data": "gAJ9cQEoVRJfYXV0aF91c2VyX2JhY2tlbmRxAlUpZGphbmdvLmNvbnRyaWIuYXV0aC5iYWNrZW5k\ncy5Nb2RlbEJhY2tlbmRxA1UNX2F1dGhfdXNlcl9pZHEESwF1LmQ2ZjkxZjcxZTMwYmVjYzcyNDk1\nYTk5MjRlNWYxNTJm\n"
+    }
+  }
+]
