Below is the file 'portalloc.py' from this revision. You can also download the file.

#!/usr/bin/env python

import csv
import sys
import os

# available switch resources; building, rack, ports
cp = lambda port_base, ports: map(lambda port: port_base+str(port), ports)
switches = []
switches.append(['650', '1A', [('bmrc-650a-2', cp('Fa0/', range(2, 48))),
                               ('bmrc-650a-3', cp('Fa0/', range(2, 48)))
                              ]])
switches.append(['650', '1B', [('bmrc-650b-1', cp('Fa0/', range(2, 48))),
                               ('bmrc-650b-2', cp('Fa0/', range(2, 48)))
                              ]])

if __name__ == '__main__':
    class Switch:
        def __init__(self, switch_name, switch_ports):
            self.switch_name, self.switch_ports = switch_name, switch_ports
            self.switch_ports.reverse()
            self.config = "! -- portalloc.py configuration for %s\n" % self.switch_name
        def allocate_port(self, port_details):
            if not self.switch_ports:
                return None
            port = self.switch_ports.pop()
            port_details['port'] = port
            self.config += """
interface %(port)s
description %(building)s/%(rack)s/%(patch)s: %(person)s
no shutdown
switchport mode access
switchport access vlan %(vlan)s
spanning-tree portfast
storm-control broadcast level 10.00
spanning-tree guard root
!""" % port_details
            return port
        def write_config(self):
            open(os.path.join('configs', self.switch_name+'.txt'), 'w').write(self.config)

    class Allocation:
        def __init__(self, building, rack, switches):
            self.building, self.rack = building, rack
            self.switches = map(lambda s_args: Switch(*s_args), switches)
        def config_port_for(self, patch, person, vlan):
            # get a free port
            port_details = { 'building' : self.building, 'rack' : self.rack, 'patch' : patch, 'person' : person, 'vlan' : vlan }
            for switch in self.switches:
                allocated = switch.allocate_port(port_details)
                if allocated: break
            if not allocated:
                raise Exception("Unable to find a spare port for person %s on VLAN %s. Out of ports!" % (person, vlan))
        def write_configs(self):
            for switch in self.switches:
                switch.write_config()

    def get_pairs():
        for i in range(2):
            yield '650', '1B', str(i), 'Grahame Bowland', '86'

    # initialise a switch port allocator for each building/rack
    allocators = {}
    for building, rack, switch_list in switches:
        allocators[(building, rack)] = Allocation(building, rack, switch_list)

    for building, rack, patch, person, vlan in get_pairs():
        if not allocators.has_key((building, rack)):
            print "Port configuration request for %s is in unknown location: %s/%s" % (person, building, rack)
        alloc = allocators[(building, rack)]
        alloc.config_port_for(patch, person, vlan)

    alloc.write_configs()
    sys.exit(0)

    fd = open("BMRC - Voice and Data.csv", "rU")
    for fields in csv.reader(fd):
        for idx, field in enumerate(fields):
            print idx, field
        if len(fields) < 10:
                continue
        person = fields[1].strip()
        ep = fields[2].strip()
        if person and ep:
            person += " : " + ep
        if not person:
                person = "(unallocated)"
        building = fields[8]
        rack = fields[9]
        vlan = fields[13].strip()
        a.config_pair(building, rack, vlan, person, fields[9])
        a.config_pair(building, rack, vlan, person, fields[10])
        a.config_pair(building, rack, vlan, person, fields[11])
    for building in a.building_config.keys():
        print "\n!\n! -- config for building %s -- \n!" % (building)
        print ''.join(a.building_config[building])