source: genesis/tools/gformat.py@ 9287

Last change on this file since 9287 was 9286, checked in by rick, 13 years ago

The pool has to be formatted smarter, else the name execed the maximum allowed characters.

  • Property svn:executable set to *
  • Property svn:keywords set to Id
File size: 22.2 KB
RevLine 
[8242]1#!/usr/bin/env python
2#
3# vim:ts=2:et:sw=2:ai
4# Wireless Leiden configuration generator, based on yaml files'
5# Rick van der Zwet <info@rickvanderzwet.nl>
[8622]6
7# Hack to make the script directory is also threated as a module search path.
8import sys
9import os
[9286]10import re
[8622]11sys.path.append(os.path.dirname(__file__))
12
[8242]13import cgi
[8267]14import cgitb
15import copy
[8242]16import glob
17import socket
18import string
19import subprocess
20import time
[8622]21import rdnap
[8584]22from pprint import pprint
[8575]23try:
24 import yaml
25except ImportError, e:
26 print e
27 print "[ERROR] Please install the python-yaml or devel/py-yaml package"
28 exit(1)
[8588]29
30try:
31 from yaml import CLoader as Loader
32 from yaml import CDumper as Dumper
33except ImportError:
34 from yaml import Loader, Dumper
35
[8242]36
[9283]37
[8948]38if os.environ.has_key('CONFIGROOT'):
39 NODE_DIR = os.environ['CONFIGROOT']
40else:
[9283]41 NODE_DIR = os.path.abspath(os.path.dirname(__file__)) + '/../nodes'
[8242]42__version__ = '$Id: gformat.py 9286 2011-06-23 19:33:04Z rick $'
43
[8267]44
[9283]45files = [
[8242]46 'authorized_keys',
47 'dnsmasq.conf',
48 'rc.conf.local',
49 'resolv.conf',
50 'wleiden.yaml'
51 ]
52
[8319]53# Global variables uses
[8323]54OK = 10
55DOWN = 20
56UNKNOWN = 90
[8257]57
58
[8267]59def get_proxylist():
60 """Get all available proxies proxyX sorting based on X number"""
61 os.chdir(NODE_DIR)
62 proxylist = sorted(glob.glob("proxy*"),
63 key=lambda name: int(''.join([c for c in name if c in string.digits])),
64 cmp=lambda x,y: x - y)
65 return proxylist
66
67
68
[8321]69def valid_addr(addr):
70 """ Show which address is valid in which are not """
71 return str(addr).startswith('172.')
72
73
[8267]74def get_nodelist():
75 """ Get all available nodes - sorted """
76 os.chdir(NODE_DIR)
77 nodelist = sorted(glob.glob("CNode*"))
78 return nodelist
79
[8296]80def get_hostlist():
81 """ Combined hosts and proxy list"""
82 return get_nodelist() + get_proxylist()
[8267]83
[8588]84def angle_between_points(lat1,lat2,long1,long2):
[9283]85 """
[8588]86 Return Angle in radians between two GPS coordinates
87 See: http://stackoverflow.com/questions/3809179/angle-between-2-gps-coordinates
88 """
89 dy = lat2 - lat1
90 dx = math.cos(math.pi/180*lat1)*(long2 - long1)
91 angle = math.atan2(dy,dx)
92 return angle
[8267]93
[8588]94def angle_to_cd(angle):
95 """ Return Dutch Cardinal Direction estimation in 'one digit' of radian angle """
96
97 # For easy conversion get positive degree
98 degrees = math.degrees(angle)
99 if degrees < 0:
100 360 - abs(degrees)
101
102 # Numbers can be confusing calculate from the 4 main directions
103 p = 22.5
104 if degrees < p:
105 return "n"
[9283]106 elif degrees < (90 - p):
[8588]107 return "no"
[9283]108 elif degrees < (90 + p):
[8588]109 return "o"
[9283]110 elif degrees < (180 - p):
[8588]111 return "zo"
[9283]112 elif degrees < (180 + p):
[8588]113 return "z"
[9283]114 elif degrees < (270 - p):
[8588]115 return "zw"
[9283]116 elif degrees < (270 + p):
[8588]117 return "w"
[9283]118 elif degrees < (360 - p):
[8588]119 return "nw"
120 else:
121 return "n"
122
123
[8267]124def generate_title(nodelist):
[8257]125 """ Main overview page """
[9283]126 items = {'root' : "." }
[8267]127 output = """
[8257]128<html>
129 <head>
130 <title>Wireless leiden Configurator - GFormat</title>
131 <style type="text/css">
132 th {background-color: #999999}
133 tr:nth-child(odd) {background-color: #cccccc}
134 tr:nth-child(even) {background-color: #ffffff}
135 th, td {padding: 0.1em 1em}
136 </style>
137 </head>
138 <body>
139 <center>
[8259]140 <form type="GET" action="%(root)s">
[8257]141 <input type="hidden" name="action" value="update">
142 <input type="submit" value="Update Configuration Database (SVN)">
143 </form>
144 <table>
145 <caption><h3>Wireless Leiden Configurator</h3></caption>
146 """ % items
[8242]147
[8296]148 for node in nodelist:
[8257]149 items['node'] = node
[8267]150 output += '<tr><td><a href="%(root)s/%(node)s">%(node)s</a></td>' % items
[8257]151 for config in files:
152 items['config'] = config
[8267]153 output += '<td><a href="%(root)s/%(node)s/%(config)s">%(config)s</a></td>' % items
154 output += "</tr>"
155 output += """
[8257]156 </table>
157 <hr />
158 <em>%s</em>
159 </center>
160 </body>
161</html>
162 """ % __version__
[8242]163
[8267]164 return output
[8257]165
166
[8267]167
168def generate_node(node):
[8257]169 """ Print overview of all files available for node """
[8267]170 return "\n".join(files)
[8242]171
[8257]172
173
[8242]174def generate_header(ctag="#"):
175 return """\
[9283]176%(ctag)s
[8242]177%(ctag)s DO NOT EDIT - Automatically generated by 'gformat'
178%(ctag)s Generated at %(date)s by %(host)s
[9283]179%(ctag)s
[8242]180""" % { 'ctag' : ctag, 'date' : time.ctime(), 'host' : socket.gethostname() }
181
[8257]182
183
[8242]184def parseaddr(s):
[8257]185 """ Process IPv4 CIDR notation addr to a (binary) number """
[8242]186 f = s.split('.')
187 return (long(f[0]) << 24L) + \
188 (long(f[1]) << 16L) + \
189 (long(f[2]) << 8L) + \
190 long(f[3])
191
[8257]192
193
[8242]194def showaddr(a):
[8257]195 """ Display IPv4 addr in (dotted) CIDR notation """
[8242]196 return "%d.%d.%d.%d" % ((a >> 24) & 0xff, (a >> 16) & 0xff, (a >> 8) & 0xff, a & 0xff)
197
[8257]198
[8584]199def is_member(ip, mask, canidate):
200 """ Return True if canidate is part of ip/mask block"""
201 ip_addr = gformat.parseaddr(ip)
202 ip_canidate = gformat.parseaddr(canidate)
203 mask = int(mask)
204 ip_addr = ip_addr & ~((1 << (32 - mask)) - 1)
205 ip_canidate = ip_canidate & ~((1 << (32 - mask)) - 1)
206 return ip_addr == ip_canidate
[8257]207
[8584]208
209
[9283]210
[8242]211def netmask2subnet(netmask):
[8257]212 """ Given a 'netmask' return corresponding CIDR """
[8242]213 return showaddr(0xffffffff & (0xffffffff << (32 - int(netmask))))
214
[8257]215
216
[8242]217def generate_dnsmasq_conf(datadump):
[8257]218 """ Generate configuration file '/usr/local/etc/dnsmasq.conf' """
[8242]219 output = generate_header()
220 output += """\
[9283]221# DHCP server options
[8242]222dhcp-authoritative
223dhcp-fqdn
224domain=dhcp.%(nodename_lower)s.%(domain)s
225domain-needed
226expand-hosts
227
228# Low memory footprint
229cache-size=10000
230 \n""" % datadump
231
232 for iface_key in datadump['iface_keys']:
[8262]233 if not datadump[iface_key].has_key('comment'):
234 datadump[iface_key]['comment'] = None
235 output += "## %(interface)s - %(desc)s - %(comment)s\n" % datadump[iface_key]
[8242]236
237 try:
[8257]238 (dhcp_start, dhcp_stop) = datadump[iface_key]['dhcp'].split('-')
[8242]239 (ip, netmask) = datadump[iface_key]['ip'].split('/')
240 datadump[iface_key]['subnet'] = netmask2subnet(netmask)
[8262]241 except (AttributeError, ValueError):
[8242]242 output += "# not autoritive\n\n"
243 continue
244
245 dhcp_part = ".".join(ip.split('.')[0:3])
246 datadump[iface_key]['dhcp_start'] = dhcp_part + "." + dhcp_start
247 datadump[iface_key]['dhcp_stop'] = dhcp_part + "." + dhcp_stop
248 output += "dhcp-range=%(interface)s,%(dhcp_start)s,%(dhcp_stop)s,%(subnet)s,24h\n\n" % datadump[iface_key]
[9283]249
[8242]250 return output
251
[8257]252
253
[8242]254def generate_rc_conf_local(datadump):
[8257]255 """ Generate configuration file '/etc/rc.conf.local' """
[8242]256 output = generate_header("#");
257 output += """\
258hostname='%(nodetype)s%(nodename)s.%(domain)s'
[9283]259location='%(location)s'
[8242]260""" % datadump
[9283]261
[8242]262 # TProxy configuration
263 output += "\n"
264 try:
265 if datadump['tproxy']:
266 output += """\
267tproxy_enable='YES'
268tproxy_range='%(tproxy)s'
269""" % datadump
270 except KeyError:
271 output += "tproxy_enable='NO'\n"
[9283]272
[8242]273 output += '\n'
274 # lo0 configuration:
275 # - 172.32.255.1/32 is the proxy.wleiden.net deflector
[9283]276 # - masterip is special as it needs to be assigned to at
[8242]277 # least one interface, so if not used assign to lo0
278 addrs_list = { 'lo0' : ["127.0.0.1/8", "172.31.255.1/32"] }
[9283]279 iface_map = {'lo0' : 'lo0'}
[8242]280
[8297]281 masterip_used = False
282 for iface_key in datadump['iface_keys']:
283 if datadump[iface_key]['ip'].startswith(datadump['masterip']):
284 masterip_used = True
285 break
[9283]286 if not masterip_used:
[8297]287 addrs_list['lo0'].append(datadump['masterip'] + "/32")
288
[8242]289 wlan_count = 0
290 for iface_key in datadump['iface_keys']:
291 ifacedump = datadump[iface_key]
292 interface = ifacedump['interface']
293 # By default no special interface mapping
294 iface_map[interface] = interface
295
296 # Add interface IP to list
297 if addrs_list.has_key(interface):
298 addrs_list[interface].append(ifacedump['ip'])
299 else:
300 addrs_list[interface] = [ifacedump['ip']]
301
302 # Alias only needs IP assignment for now, this might change if we
303 # are going to use virtual accesspoints
304 if "alias" in iface_key:
305 continue
306
307 # XXX: Might want to deduct type directly from interface name
308 if ifacedump['type'] in ['11a', '11b', '11g', 'wireless']:
309 # Create wlanX interface
[9283]310 ifacedump['wlanif'] ="wlan%i" % wlan_count
[8242]311 iface_map[interface] = ifacedump['wlanif']
312 wlan_count += 1
313
314 # Default to station (client) mode
315 ifacedump['wlanmode'] = "sta"
[8274]316 if ifacedump['mode'] in ['master', 'master-wds']:
[8242]317 ifacedump['wlanmode'] = "ap"
318 # Default to 802.11b mode
319 ifacedump['mode'] = '11b'
320 if ifacedump['type'] in ['11a', '11b' '11g']:
[9283]321 ifacedump['mode'] = ifacedump['type']
[8242]322
323 if not ifacedump.has_key('channel'):
324 if ifacedump['type'] == '11a':
325 ifacedump['channel'] = 36
326 else:
327 ifacedump['channel'] = 1
328
329 # Allow special hacks at the back like wds and stuff
330 if not ifacedump.has_key('extra'):
331 ifacedump['extra'] = 'regdomain ETSI country NL'
332
333 output += "wlans_%(interface)s='%(wlanif)s'\n" % ifacedump
334 output += ("create_args_%(wlanif)s='wlanmode %(wlanmode)s mode " +\
[8274]335 "%(mode)s ssid %(ssid)s %(extra)s channel %(channel)s'\n") % ifacedump
[9283]336
[8242]337 elif ifacedump['type'] in ['ethernet', 'eth']:
338 # No special config needed besides IP
339 pass
340 else:
341 assert False, "Unknown type " + ifacedump['type']
342
[9283]343 # Print IP address which needs to be assigned over here
[8242]344 output += "\n"
345 for iface,addrs in sorted(addrs_list.iteritems()):
346 output += "ipv4_addrs_%s='%s'\n" % (iface_map[iface], " ".join(addrs))
347
348 return output
349
[8257]350
351
[8242]352def get_yaml(item):
[8257]353 """ Get configuration yaml for 'item'"""
[9284]354 gfile = os.path.join(NODE_DIR,item,'wleiden.yaml')
[8242]355
356 f = open(gfile, 'r')
[8588]357 datadump = yaml.load(f,Loader=Loader)
[8242]358 f.close()
359
360 return datadump
361
[8588]362def store_yaml(datadump):
363 """ Store configuration yaml for 'item'"""
[9284]364 gfile = os.path.join(NODE_DIR,item,'wleiden.yaml')
[8257]365
[8588]366 f = open(gfile, 'w')
367 f.write(generate_wleiden_yaml(datadump))
368 f.close()
[8257]369
[8588]370
371
[8317]372def get_all_configs():
373 """ Get dict with key 'host' with all configs present """
374 configs = dict()
375 for host in get_hostlist():
376 datadump = get_yaml(host)
377 configs[host] = datadump
378 return configs
379
380
[8319]381def get_interface_keys(config):
382 """ Quick hack to get all interface keys, later stage convert this to a iterator """
383 return [elem for elem in config.keys() if (elem.startswith('iface_') and not "lo0" in elem)]
[8317]384
[8319]385
[8317]386def get_used_ips(configs):
387 """ Return array of all IPs used in config files"""
388 ip_list = []
[8319]389 for config in configs:
[8317]390 ip_list.append(config['masterip'])
[8319]391 for iface_key in get_interface_keys(config):
[8317]392 l = config[iface_key]['ip']
393 addr, mask = l.split('/')
394 # Special case do not process
[8332]395 if valid_addr(addr):
396 ip_list.append(addr)
397 else:
398 print "## IP '%s' in '%s' not valid" % (addr, config['nodename'])
[8317]399 return sorted(ip_list)
400
401
402
[8267]403def write_yaml(item, datadump):
404 """ Write configuration yaml for 'item'"""
[9284]405 gfile = os.path.join(NODE_DIR,item,'wleiden.yaml')
[8267]406
407 f = open(gfile, 'w')
408 f.write(format_wleiden_yaml(datadump))
409 f.close()
410
411
412
[8242]413def generate_resolv_conf(datadump):
[8257]414 """ Generate configuration file '/etc/resolv.conf' """
[8242]415 output = generate_header("#");
416 output += """\
417search wleiden.net
418# Try local (cache) first
419nameserver 127.0.0.1
420
[9283]421# Proxies are recursive nameservers
[8242]422# needs to be in resolv.conf for dnsmasq as well
423""" % datadump
[9283]424
[8267]425 for proxy in get_proxylist():
[8242]426 proxy_ip = get_yaml(proxy)['masterip']
427 output += "nameserver %-15s # %s\n" % (proxy_ip, proxy)
428 return output
429
430
[8257]431
[8267]432def format_yaml_value(value):
433 """ Get yaml value in right syntax for outputting """
434 if isinstance(value,str):
435 output = "'%s'" % value
436 else:
437 output = value
[9283]438 return output
[8267]439
440
441
442def format_wleiden_yaml(datadump):
[8242]443 """ Special formatting to ensure it is editable"""
[9283]444 output = "# Genesis config yaml style\n"
[8262]445 output += "# vim:ts=2:et:sw=2:ai\n"
[8242]446 output += "#\n"
447 iface_keys = [elem for elem in datadump.keys() if elem.startswith('iface_')]
448 for key in sorted(set(datadump.keys()) - set(iface_keys)):
[8267]449 output += "%-10s: %s\n" % (key, format_yaml_value(datadump[key]))
[9283]450
[8242]451 output += "\n\n"
[9283]452
[8272]453 key_order = [ 'comment', 'interface', 'ip', 'desc', 'sdesc', 'mode', 'type',
454 'extra_type', 'channel', 'ssid', 'dhcp' ]
455
[8242]456 for iface_key in sorted(iface_keys):
457 output += "%s:\n" % iface_key
[8272]458 for key in key_order + list(sorted(set(datadump[iface_key].keys()) - set(key_order))):
459 if datadump[iface_key].has_key(key):
[9283]460 output += " %-11s: %s\n" % (key, format_yaml_value(datadump[iface_key][key]))
[8242]461 output += "\n\n"
462
463 return output
464
465
[8257]466
[8267]467def generate_wleiden_yaml(datadump):
468 """ Generate (petty) version of wleiden.yaml"""
469 output = generate_header("#")
470 output += format_wleiden_yaml(datadump)
471 return output
472
473
[8588]474def generate_yaml(datadump):
475 return generate_config(datadump['nodename'], "wleiden.yaml", datadump)
[8267]476
[8588]477
[9283]478
[8298]479def generate_config(node, config, datadump=None):
[8257]480 """ Print configuration file 'config' of 'node' """
[8267]481 output = ""
[8242]482 try:
483 # Load config file
[8298]484 if datadump == None:
485 datadump = get_yaml(node)
[9283]486
[8267]487 # Preformat certain needed variables for formatting and push those into special object
488 datadump_extra = copy.deepcopy(datadump)
489 if not datadump_extra.has_key('domain'):
490 datadump_extra['domain'] = 'wleiden.net'
491 datadump_extra['nodename_lower'] = datadump_extra['nodename'].lower()
492 datadump_extra['iface_keys'] = sorted([elem for elem in datadump.keys() if elem.startswith('iface_')])
493
[8242]494 if config == 'wleiden.yaml':
[8267]495 output += generate_wleiden_yaml(datadump)
496 elif config == 'authorized_keys':
[8242]497 f = open("global_keys", 'r')
[8267]498 output += f.read()
[8242]499 f.close()
500 elif config == 'dnsmasq.conf':
[8267]501 output += generate_dnsmasq_conf(datadump_extra)
[8242]502 elif config == 'rc.conf.local':
[8267]503 output += generate_rc_conf_local(datadump_extra)
[8242]504 elif config == 'resolv.conf':
[8267]505 output += generate_resolv_conf(datadump_extra)
[8242]506 else:
[9283]507 assert False, "Config not found!"
[8242]508 except IOError, e:
[8267]509 output += "[ERROR] Config file not found"
510 return output
[8242]511
512
[8257]513
[8258]514def process_cgi_request():
515 """ When calling from CGI """
516 # Update repository if requested
517 form = cgi.FieldStorage()
518 if form.getvalue("action") == "update":
[8259]519 print "Refresh: 5; url=."
[8258]520 print "Content-type:text/plain\r\n\r\n",
521 print "[INFO] Updating subverion, please wait..."
522 print subprocess.Popen(['svn', 'up', NODE_DIR], stderr=subprocess.STDOUT, stdout=subprocess.PIPE).communicate()[0],
523 print "[INFO] All done, redirecting in 5 seconds"
524 sys.exit(0)
[9283]525
526
[8258]527 uri = os.environ['PATH_INFO'].strip('/').split('/')
[8267]528 output = ""
[8258]529 if not uri[0]:
[8267]530 output += "Content-type:text/html\r\n\r\n"
[8296]531 output += generate_title(get_hostlist())
[8258]532 elif len(uri) == 1:
[8267]533 output += "Content-type:text/plain\r\n\r\n"
534 output += generate_node(uri[0])
[8258]535 elif len(uri) == 2:
[8267]536 output += "Content-type:text/plain\r\n\r\n"
537 output += generate_config(uri[0], uri[1])
[8258]538 else:
539 assert False, "Invalid option"
[8267]540 print output
[8242]541
[8588]542def get_fqdn(datadump):
543 # Proxy naming convention is special
544 if datadump['nodetype'] == 'Proxy':
545 fqdn = datadump['nodename']
546 else:
547 # By default the full name is listed and also a shortname CNAME for easy use.
548 fqdn = datadump['nodetype'] + datadump['nodename']
549 return(fqdn)
[8259]550
[9283]551
552
[9284]553def make_dns(output_dir = 'dns'):
[8588]554 items = dict()
[8598]555
[8588]556 # hostname is key, IP is value
557 wleiden_zone = dict()
558 wleiden_cname = dict()
[8598]559
[8588]560 pool = dict()
561 for node in get_hostlist():
562 datadump = get_yaml(node)
[9283]563
[8588]564 # Proxy naming convention is special
565 fqdn = get_fqdn(datadump)
566 if datadump['nodetype'] == 'CNode':
567 wleiden_cname[datadump['nodename']] = fqdn
568
569 wleiden_zone[fqdn] = datadump['masterip']
570
[8598]571 # Hacking to get proper DHCP IPs and hostnames
[8588]572 for iface_key in get_interface_keys(datadump):
[8598]573 iface_name = datadump[iface_key]['interface'].replace(':',"-alias-")
[8588]574 (ip, netmask) = datadump[iface_key]['ip'].split('/')
575 try:
576 (dhcp_start, dhcp_stop) = datadump[iface_key]['dhcp'].split('-')
577 datadump[iface_key]['subnet'] = netmask2subnet(netmask)
578 dhcp_part = ".".join(ip.split('.')[0:3])
579 if ip != datadump['masterip']:
580 wleiden_zone["dhcp-gateway-%s.%s" % (iface_name, fqdn)] = ip
581 for i in range(int(dhcp_start), int(dhcp_stop) + 1):
582 wleiden_zone["dhcp-%s-%s.%s" % (i, iface_name, fqdn)] = "%s.%s" % (dhcp_part, i)
583 except (AttributeError, ValueError):
584 # First push it into a pool, to indentify the counter-part later on
585 addr = parseaddr(ip)
586 netmask = int(netmask)
587 addr = addr & ~((1 << (32 - netmask)) - 1)
[9283]588 if pool.has_key(addr):
[8588]589 pool[addr] += [(iface_name, fqdn, ip)]
[9283]590 else:
[8588]591 pool[addr] = [(iface_name, fqdn, ip)]
592 continue
593
[9286]594
595 def pool_to_name(node, pool_members):
596 """Convert the joined name to a usable pool name"""
597
598 # Get rid of the own entry
599 pool_members = list(set(pool_members) - set([fqdn]))
600
601 target = oldname = ''
602 for node in sorted(pool_members):
603 (name, number) = re.match('^([A-Za-z]+)([0-9]*)$',node).group(1,2)
604 target += "-" + number if name == oldname else "-" + node if target else node
605 oldname = name
606
607 return target
608
609
[8598]610 # Automatic naming convention of interlinks namely 2 + remote.lower()
[8588]611 for (key,value) in pool.iteritems():
612 if len(value) == 1:
613 (iface_name, fqdn, ip) = value[0]
614 wleiden_zone["2unused-%s.%s" % (iface_name, fqdn)] = ip
615 elif len(value) == 2:
616 (a_iface_name, a_fqdn, a_ip) = value[0]
617 (b_iface_name, b_fqdn, b_ip) = value[1]
618 wleiden_zone["2%s.%s" % (b_fqdn,a_fqdn)] = a_ip
619 wleiden_zone["2%s.%s" % (a_fqdn,b_fqdn)] = b_ip
620 else:
621 pool_members = [k[1] for k in value]
622 for item in value:
[9283]623 (iface_name, fqdn, ip) = item
[9286]624 pool_name = "2pool-" + showaddr(key).replace('.','-') + "-" + pool_to_name(fqdn,pool_members)
[8588]625 wleiden_zone["%s.%s" % (pool_name, fqdn)] = ip
[8598]626
627 # Include static DNS entries
628 # XXX: Should they override the autogenerated results?
629 # XXX: Convert input to yaml more useable.
630 # Format:
631 ##; this is a comment
632 ## roomburgh=CNodeRoomburgh1
633 ## apkerk1.CNodeVosko=172.17.176.8 ;this as well
[9284]634 dns = yaml.load(open(os.path.join(NODE_DIR,'../dns/staticDNS.yaml'),'r'))
[8622]635 for comment, block in dns.iteritems():
636 for k,v in block.iteritems():
[8598]637 if valid_addr(v):
638 wleiden_zone[k] = v
639 else:
640 wleiden_cname[k] = v
[9283]641
[8598]642 details = dict()
643 # 24 updates a day allowed
644 details['serial'] = time.strftime('%Y%m%d%H')
645
646 dns_header = '''
647$TTL 3h
648%(zone)s. SOA sunny.wleiden.net. beheer.lijst.wirelessleiden.nl. ( %(serial)s 1d 12h 1w 3h )
649 ; Serial, Refresh, Retry, Expire, Neg. cache TTL
650
651 NS sunny.wleiden.net.
652 \n'''
653
[9283]654
[8598]655 if not os.path.isdir('dns'):
656 os.makedirs('dns')
657 details['zone'] = 'wleiden.net'
[9284]658 f = open(os.path.join(output_dir,"db." + details['zone']), "w")
[8598]659 f.write(dns_header % details)
660
[8588]661 for host,ip in wleiden_zone.iteritems():
[8598]662 if valid_addr(ip):
[9283]663 f.write("%s.wleiden.net. IN A %s \n" % (host.lower(), ip))
[8588]664 for source,dest in wleiden_cname.iteritems():
[8636]665 f.write("%s.wleiden.net. IN CNAME %s.wleiden.net.\n" % (source.lower(), dest.lower()))
[8588]666 f.close()
[9283]667
[8598]668 # Create whole bunch of specific sub arpa zones. To keep it compliant
669 for s in range(16,32):
670 details['zone'] = '%i.172.in-addr.arpa' % s
[9284]671 f = open(os.path.join(output_dir,"db." + details['zone']), "w")
[8598]672 f.write(dns_header % details)
[8588]673
[8598]674 #XXX: Not effient, fix to proper data structure and do checks at other
675 # stages
676 for host,ip in wleiden_zone.iteritems():
677 if valid_addr(ip):
678 if int(ip.split('.')[1]) == s:
679 rev_ip = '.'.join(reversed(ip.split('.')))
[9283]680 f.write("%s.in-addr.arpa. IN PTR %s.wleiden.net.\n" % (rev_ip.lower(), host.lower()))
[8598]681 f.close()
[8588]682
[8598]683
[8259]684def usage():
[8598]685 print """Usage: %s <standalone [port] |test [test arguments]|static|dns>
[8259]686Examples:
[9284]687\tdns [outputdir] = Generate BIND compliant zone files in dns.
[8259]688\tstandalone = Run configurator webserver [default port=8000]
[8296]689\tstatic = Generate all config files and store on disk
690\t with format ./static/%%NODE%%/%%FILE%%
[9283]691\ttest CNodeRick dnsmasq.conf = Receive output of CGI script
[8259]692\t for arguments CNodeRick/dnsmasq.conf
693"""
694 exit(0)
695
696
697
[8267]698def main():
699 """Hard working sub"""
700 # Allow easy hacking using the CLI
701 if not os.environ.has_key('PATH_INFO'):
702 if len(sys.argv) < 2:
703 usage()
[9283]704
[8267]705 if sys.argv[1] == "standalone":
706 import SocketServer
707 import CGIHTTPServer
[8867]708 # CGI does not go backward, little hack to get ourself in the right working directory.
709 os.chdir(os.path.dirname(__file__) + '/..')
[8267]710 try:
711 PORT = int(sys.argv[2])
712 except (IndexError,ValueError):
713 PORT = 8000
[9283]714
[8267]715 class MyCGIHTTPRequestHandler(CGIHTTPServer.CGIHTTPRequestHandler):
716 """ Serve this CGI from the root of the webserver """
717 def is_cgi(self):
718 if "favicon" in self.path:
719 return False
[9283]720
[8267]721 self.cgi_info = (__file__, self.path)
722 self.path = ''
723 return True
724 handler = MyCGIHTTPRequestHandler
725 httpd = SocketServer.TCPServer(("", PORT), handler)
726 httpd.server_name = 'localhost'
727 httpd.server_port = PORT
[9283]728
[8267]729 print "serving at port", PORT
[8860]730 try:
731 httpd.serve_forever()
732 except KeyboardInterrupt:
733 httpd.shutdown()
[8267]734 elif sys.argv[1] == "test":
735 os.environ['PATH_INFO'] = "/".join(sys.argv[2:])
736 os.environ['SCRIPT_NAME'] = __file__
737 process_cgi_request()
[8296]738 elif sys.argv[1] == "static":
739 items = dict()
740 for node in get_hostlist():
741 items['node'] = node
742 items['wdir'] = "./static/%(node)s" % items
743 if not os.path.isdir(items['wdir']):
744 os.makedirs(items['wdir'])
[8298]745 datadump = get_yaml(node)
[8296]746 for config in files:
747 items['config'] = config
748 print "## Generating %(node)s %(config)s" % items
749 f = open("%(wdir)s/%(config)s" % items, "w")
[8298]750 f.write(generate_config(node, config, datadump))
[8296]751 f.close()
[8584]752 elif sys.argv[1] == "dns":
[9285]753 make_dns(sys.argv[2] if len(sys.argv) > 2 else 'dns')
[9283]754 elif sys.argv[1] == "cleanup":
[8588]755 # First generate all datadumps
756 datadumps = dict()
757 for host in get_hostlist():
758 datadump = get_yaml(host)
759 datadumps[get_fqdn(datadump)] = datadump
[9283]760
[8622]761 datadump['latitude'], datadump['longitude'] = rdnap.rd2etrs(datadump['rdnap_x'], datadump['rdnap_y'])
[8588]762 write_yaml(host, datadump)
[9283]763 else:
764 usage()
765 else:
766 cgitb.enable()
767 process_cgi_request()
768
769
770if __name__ == "__main__":
771 main()
Note: See TracBrowser for help on using the repository browser.