The unified diff between revisions [1fc0ef33..] and [5b5ff777..] is displayed below. It can also be downloaded as a raw diff.
#
#
# add_file "switchconf.py"
# content [c61bc683468cace06cad14d1f4ff8c714c776e87]
#
# set "switchconf.py"
# attr "mtn:execute"
# value "true"
#
============================================================
--- switchconf.py c61bc683468cace06cad14d1f4ff8c714c776e87
+++ switchconf.py c61bc683468cace06cad14d1f4ff8c714c776e87
@@ -0,0 +1,201 @@
+#!/usr/bin/python
+
+import getpass
+import urllib
+import time
+import sys
+import re
+import os
+
+# configurable variables
+tftp_path = '/var/tftp'
+template_path = './templates/'
+dhcp_config_file = '/etc/dhcpd.conf'
+tftp_server_address = '192.168.0.1'
+
+class ConfigLibrary:
+ def __init__(self, template_path):
+ self.template_path = template_path
+ self.update()
+ def load_module(self, module_path, module_name):
+ orig_path = sys.path
+ sys.path.insert(0, module_path)
+ mod = __import__(module_name, globals(), locals(), [''])
+ sys.path = orig_path
+ return mod
+ def get_required_attributes(self, config_file):
+ rv = []
+ for line in open(config_file):
+ l = line.strip().split('_', 1)
+ while True:
+ if len(l) != 2 or not l: break
+ chaff, interesting = l
+ m = re.match(r'^([A-Za-z0-9]+)_', interesting)
+ if m:
+ attr = m.groups()[0]
+ rv.append(attr)
+ interesting = interesting[len(attr):]
+ l = interesting.split('_', 1)
+ return rv
+ def update(self):
+ self.templates = {}
+ for template in os.listdir(template_path):
+ attrs = self.templates[template] = {}
+ config_file = os.path.join(template_path, template, 'config.txt')
+ if os.access(config_file, os.R_OK):
+ attrs['config_file'] = config_file
+ attrs['required'] = self.get_required_attributes(config_file)
+ module_path = os.path.join(template_path, template)
+ module_file = os.path.join(template_path, template, 'commands.py')
+ if os.access(module_file, os.R_OK):
+ attrs['module'] = self.load_module(module_path, 'commands')
+ def get_config(self, template_name, attrs):
+ if not self.templates.has_key(template_name): return None
+ template = self.templates[template_name]
+ data = open(template['config_file']).read()
+ for attr in attrs.keys():
+ data = data.replace('_'+attr+'_', urllib.unquote(attrs[attr]))
+ return data
+
+class HostLibrary:
+ def __init__(self, config_file=dhcp_config_file):
+ self.config_file = config_file
+ self.update()
+ def update(self):
+ def add_host(h):
+ if not h.has_key('attrs'):
+ raise Exception("Host without attributes.")
+ if not h.has_key('ethernet_addr'):
+ raise Exception("Host without ethernet address.")
+ if not h['attrs'].has_key('template'):
+ raise Exception("Host without template attributes (# template=...)")
+ self.hosts[h['ethernet_addr']] = h
+ self.hosts, in_host = {}, False
+ for line in open(self.config_file):
+ line = line.strip()
+ if in_host:
+ if line == '}':
+ in_host = False
+ add_host(host)
+ if line.startswith('#'):
+ key, val = line[2:].split('=', 1)
+ host.setdefault('attrs', {})[key] = urllib.unquote(val)
+ else:
+ m = re.match(r'hardware ethernet ([0-9a-fA-F:]+);$', line)
+ if m:
+ host['ethernet_addr'] = m.groups()[0]
+ continue
+ elif line.startswith('host switchconf_'):
+ in_host = True
+ host = {}
+
+def tail_file(fname):
+ fd = open(fname)
+ fd.seek(0, 2) # end of the file
+ while 1:
+ where = fd.tell()
+ line = fd.readline()
+ if not line:
+ time.sleep(1)
+ fd.seek(where)
+ else:
+ yield line
+
+class DHCPWatcher:
+ def __init__(self, log_file='/var/log/syslog'):
+ self.log_file = log_file
+ def watch(self):
+ # Aug 12 13:27:10 localhost dhcpd: DHCPACK on 192.168.0.2 to 00:14:6a:a1:82:40 via eth0
+ dhcpre = re.compile(r'^.*DHCPACK on (\d+\.\d+\.\d+\.\d+) to (.*) via (.*)$')
+ for line in tail_file(self.log_file):
+ m = dhcpre.match(line)
+ if m: yield m.groups()
+
+
+def daemon():
+ cl = ConfigLibrary(template_path)
+ hl = HostLibrary()
+ print "Configuration for %d hosts and %d host types loaded." % (len(hl.hosts.keys()), len(cl.templates.keys()))
+ username = raw_input("Username: ")
+ password = getpass.getpass("Password: ")
+ enable = getpass.getpass("Enable password: ")
+ print "Watching for DHCP requests."
+ for ip_address, mac_address, interface in DHCPWatcher().watch():
+ print ip_address, mac_address, interface
+ print hl.hosts.keys()
+ if hl.hosts.has_key(mac_address):
+ print "Match!", hl.hosts[mac_address]
+ template_name = hl.hosts[mac_address]['attrs']['template']
+ template = cl.templates[template_name]
+ if template.has_key('module'):
+ print "Running configuration code."
+ try:
+ template['module'].configure(hl.hosts[mac_address], ip_address, mac_address, interface, username, password, enable)
+ except:
+ print "Some exception running this code."
+ raise
+ else:
+ print "No match, this is not one of our hosts."
+
+def config():
+ class ConfigException(Exception):
+ def __init__(self, val):
+ self.args = val
+ def read_host_details():
+ print
+ print "Enter the details as prompted, and the host will be added"
+ print "to the database."
+ print
+ mac_address = raw_input('MAC address: ').strip().lower()
+ if len(mac_address) != 17 or not re.match(r'^[A-Fa-f\d:]+$', mac_address):
+ raise ConfigException('Invalid MAC address')
+ print "Select a template:"
+ templates = cl.templates.keys()
+ if len(templates) > 1:
+ for idx, item in enumerate(templates):
+ print " %s: %s" % (idx, item)
+ idx = raw_input('Selection: ').strip()
+ idx = int(idx)
+ else: idx = 0
+ if idx < 0 or idx > len(templates) - 1:
+ raise ConfigException("Invalid selection.")
+ template_name = templates[idx]
+ template = cl.templates[template_name]
+ attrs = {'template' : template_name}
+ print "Please enter required attributes."
+ for attr in template['required']:
+ val = raw_input(attr + ': ')
+ attrs[attr] = urllib.quote(val)
+
+ # output
+ id = mac_address.replace(':', '')
+ tftp_fname = id + '.conf'
+ stanza = "\nhost switchconf_" + id + " {\n"
+ for attr in attrs: stanza += " # %s=%s\n" % (attr, urllib.quote(attrs[attr]))
+ stanza += ' hardware ethernet ' + mac_address.lower() + ';\n'
+ stanza += ' server-name "%s";\n' % tftp_server_address
+ stanza += ' option tftp-server-name "%s";\n' % tftp_server_address
+ stanza += ' filename "' + tftp_fname + '";\n'
+ stanza += '}\n'
+ open(dhcp_config_file, 'a').write(stanza)
+ open(os.path.join(tftp_path, tftp_fname), 'w').write(cl.get_config(template_name, attrs))
+ os.system('/etc/init.d/dhcp restart')
+ print "** Restart the daemon if it is running."
+
+ cl = ConfigLibrary(template_path)
+ hl = HostLibrary()
+ while 1:
+ try:
+ host = read_host_details()
+ except ConfigException, e:
+ print e
+ continue
+
+modes = { 'daemon' : daemon, 'config' : config }
+
+if __name__ == '__main__':
+ if len(sys.argv) != 2 or not modes.has_key(sys.argv[1]):
+ sys.stderr.write("Usage: %s <%s>\n" % (sys.argv[0], '|'.join(modes.keys())))
+ sys.exit(1)
+ modes[sys.argv[1]]()
+