source: genesis/tools/gformat.py@ 10047

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

Do not chdir as it confuses storing files without an prefix in the WC.

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