source: genesis/tools/gformat.py@ 9958

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

Make sure master is lower and slave is high.

Fix typo causing master ip to be re-used twice.

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