source: genesis/tools/gformat.py@ 10053

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

Serious hacking and cleanups to get all keys in the right format and stored to the proper files.

Please do mind that I remove the Bullet2,etc... information now, I will dynamically add this at an later stage.

Related-To: ticket:117

  • Property svn:executable set to *
  • Property svn:keywords set to Id
File size: 26.8 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 10053 2012-03-07 22:08:06Z 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 # Extra Proxy configuration
280 if datadump['nodetype'] == 'Proxy':
281 output += '\n# Disable routing\ngateway_enable="no"\n'
282
283 output += '\ninternalif="%s"\n' % datadump['internalif']
284
285 output += '\n'
286 # lo0 configuration:
287 # - 172.32.255.1/32 is the proxy.wleiden.net deflector
288 # - masterip is special as it needs to be assigned to at
289 # least one interface, so if not used assign to lo0
290 addrs_list = { 'lo0' : [("127.0.0.1/8", "LocalHost"), ("172.31.255.1/32","Proxy IP")] }
291 iface_map = {'lo0' : 'lo0'}
292
293 masterip_used = False
294 for iface_key in datadump['iface_keys']:
295 if datadump[iface_key]['ip'].startswith(datadump['masterip']):
296 masterip_used = True
297 break
298 if not masterip_used:
299 addrs_list['lo0'].append(datadump['masterip'] + "/32")
300
301 wlan_count = 0
302 for iface_key in datadump['iface_keys']:
303 ifacedump = datadump[iface_key]
304 interface = ifacedump['interface']
305 # By default no special interface mapping
306 iface_map[interface] = interface
307
308 # Add interface IP to list
309 item = (ifacedump['ip'], ifacedump['desc'])
310 if addrs_list.has_key(interface):
311 addrs_list[interface].append(item)
312 else:
313 addrs_list[interface] = [item]
314
315 # Alias only needs IP assignment for now, this might change if we
316 # are going to use virtual accesspoints
317 if "alias" in iface_key:
318 continue
319
320 # XXX: Might want to deduct type directly from interface name
321 if ifacedump['type'] in ['11a', '11b', '11g', 'wireless']:
322 # Create wlanX interface
323 ifacedump['wlanif'] ="wlan%i" % wlan_count
324 iface_map[interface] = ifacedump['wlanif']
325 wlan_count += 1
326
327 # Default to station (client) mode
328 ifacedump['wlanmode'] = "sta"
329 if ifacedump['mode'] in ['master', 'master-wds']:
330 ifacedump['wlanmode'] = "ap"
331 # Default to 802.11b mode
332 ifacedump['mode'] = '11b'
333 if ifacedump['type'] in ['11a', '11b' '11g']:
334 ifacedump['mode'] = ifacedump['type']
335
336 if not ifacedump.has_key('channel'):
337 if ifacedump['type'] == '11a':
338 ifacedump['channel'] = 36
339 else:
340 ifacedump['channel'] = 1
341
342 # Allow special hacks at the back like wds and stuff
343 if not ifacedump.has_key('extra'):
344 ifacedump['extra'] = 'regdomain ETSI country NL'
345
346 output += "wlans_%(interface)s='%(wlanif)s'\n" % ifacedump
347 output += ("create_args_%(wlanif)s='wlanmode %(wlanmode)s mode " +\
348 "%(mode)s ssid %(ssid)s %(extra)s channel %(channel)s'\n") % ifacedump
349
350 elif ifacedump['type'] in ['ethernet', 'eth']:
351 # No special config needed besides IP
352 pass
353 else:
354 assert False, "Unknown type " + ifacedump['type']
355
356 # Print IP address which needs to be assigned over here
357 output += "\n"
358 for iface,addrs in sorted(addrs_list.iteritems()):
359 for addr,comment in addrs:
360 output += "# %s || %s || %s\n" % (iface, addr, comment)
361 output += "ipv4_addrs_%s='%s'\n\n" % (iface_map[iface], " ".join([x[0] for x in addrs]))
362
363 return output
364
365
366
367def get_yaml(item):
368 """ Get configuration yaml for 'item'"""
369 gfile = os.path.join(NODE_DIR,item,'wleiden.yaml')
370
371 f = open(gfile, 'r')
372 datadump = yaml.load(f,Loader=Loader)
373 datadump['autogen_iface_keys'] = get_interface_keys(datadump)
374 datadump['autogen_item'] = item
375 f.close()
376
377 return datadump
378
379def store_yaml(datadump):
380 """ Store configuration yaml for 'item'"""
381 item = datadump['autogen_item']
382 gfile = os.path.join(NODE_DIR,item,'wleiden.yaml')
383
384 f = open(gfile, 'w')
385 f.write(generate_wleiden_yaml(datadump))
386 f.close()
387
388
389
390def get_all_configs():
391 """ Get dict with key 'host' with all configs present """
392 configs = dict()
393 for host in get_hostlist():
394 datadump = get_yaml(host)
395 configs[host] = datadump
396 return configs
397
398
399def get_interface_keys(config):
400 """ Quick hack to get all interface keys, later stage convert this to a iterator """
401 return [elem for elem in config.keys() if (elem.startswith('iface_') and not "lo0" in elem)]
402
403
404def get_used_ips(configs):
405 """ Return array of all IPs used in config files"""
406 ip_list = []
407 for config in configs:
408 ip_list.append(config['masterip'])
409 for iface_key in get_interface_keys(config):
410 l = config[iface_key]['ip']
411 addr, mask = l.split('/')
412 # Special case do not process
413 if valid_addr(addr):
414 ip_list.append(addr)
415 else:
416 logger.error("## IP '%s' in '%s' not valid" % (addr, config['nodename']))
417 return sorted(ip_list)
418
419
420
421def generate_resolv_conf(datadump):
422 """ Generate configuration file '/etc/resolv.conf' """
423 output = generate_header("#");
424 output += """\
425search wleiden.net
426# Try local (cache) first
427nameserver 127.0.0.1
428"""
429 if datadump['nodetype'] == 'Proxy':
430 output += """\
431nameserver 8.8.8.8 # Google Public NameServer
432nameserver 8.8.4.4 # Google Public NameServer
433"""
434 else:
435 output += """\
436# Proxies are recursive nameservers
437# needs to be in resolv.conf for dnsmasq as well
438""" % datadump
439 for proxy in get_proxylist():
440 proxy_ip = get_yaml(proxy)['masterip']
441 output += "nameserver %-15s # %s\n" % (proxy_ip, proxy)
442
443 return output
444
445
446
447def format_yaml_value(value):
448 """ Get yaml value in right syntax for outputting """
449 if isinstance(value,str):
450 output = '"%s"' % value
451 else:
452 output = value
453 return output
454
455
456
457def format_wleiden_yaml(datadump):
458 """ Special formatting to ensure it is editable"""
459 output = "# Genesis config yaml style\n"
460 output += "# vim:ts=2:et:sw=2:ai\n"
461 output += "#\n"
462 iface_keys = [elem for elem in datadump.keys() if elem.startswith('iface_')]
463 for key in sorted(set(datadump.keys()) - set(iface_keys)):
464 output += "%-10s: %s\n" % (key, format_yaml_value(datadump[key]))
465
466 output += "\n\n"
467
468 key_order = [ 'comment', 'interface', 'ip', 'desc', 'sdesc', 'mode', 'type',
469 'extra_type', 'channel', 'ssid', 'dhcp' ]
470
471 for iface_key in sorted(iface_keys):
472 output += "%s:\n" % iface_key
473 for key in key_order + list(sorted(set(datadump[iface_key].keys()) - set(key_order))):
474 if datadump[iface_key].has_key(key):
475 output += " %-11s: %s\n" % (key, format_yaml_value(datadump[iface_key][key]))
476 output += "\n\n"
477
478 return output
479
480
481
482def generate_wleiden_yaml(datadump):
483 """ Generate (petty) version of wleiden.yaml"""
484 for key in datadump.keys():
485 if key.startswith('autogen_'):
486 del datadump[key]
487
488 output = generate_header("#")
489 output += format_wleiden_yaml(datadump)
490 return output
491
492
493def generate_yaml(datadump):
494 return generate_config(datadump['nodename'], "wleiden.yaml", datadump)
495
496
497
498def generate_config(node, config, datadump=None):
499 """ Print configuration file 'config' of 'node' """
500 output = ""
501 try:
502 # Load config file
503 if datadump == None:
504 datadump = get_yaml(node)
505
506 # Preformat certain needed variables for formatting and push those into special object
507 datadump_extra = copy.deepcopy(datadump)
508 if not datadump_extra.has_key('domain'):
509 datadump_extra['domain'] = 'wleiden.net'
510 datadump_extra['nodename_lower'] = datadump_extra['nodename'].lower()
511 datadump_extra['iface_keys'] = sorted([elem for elem in datadump.keys() if elem.startswith('iface_')])
512
513 if config == 'wleiden.yaml':
514 output += generate_wleiden_yaml(datadump)
515 elif config == 'authorized_keys':
516 f = open(os.path.join(NODE_DIR,"global_keys"), 'r')
517 output += f.read()
518 f.close()
519 elif config == 'dnsmasq.conf':
520 output += generate_dnsmasq_conf(datadump_extra)
521 elif config == 'rc.conf.local':
522 output += generate_rc_conf_local(datadump_extra)
523 elif config == 'resolv.conf':
524 output += generate_resolv_conf(datadump_extra)
525 else:
526 assert False, "Config not found!"
527 except IOError, e:
528 output += "[ERROR] Config file not found"
529 return output
530
531
532
533def process_cgi_request():
534 """ When calling from CGI """
535 # Update repository if requested
536 form = cgi.FieldStorage()
537 if form.getvalue("action") == "update":
538 print "Refresh: 5; url=."
539 print "Content-type:text/plain\r\n\r\n",
540 print "[INFO] Updating subverion, please wait..."
541 print subprocess.Popen(['svn', 'up', NODE_DIR], stderr=subprocess.STDOUT, stdout=subprocess.PIPE).communicate()[0],
542 print "[INFO] All done, redirecting in 5 seconds"
543 sys.exit(0)
544
545
546 uri = os.environ['PATH_INFO'].strip('/').split('/')
547 output = ""
548 if not uri[0]:
549 output += "Content-type:text/html\r\n\r\n"
550 output += generate_title(get_hostlist())
551 elif len(uri) == 1:
552 output += "Content-type:text/plain\r\n\r\n"
553 output += generate_node(uri[0])
554 elif len(uri) == 2:
555 output += "Content-type:text/plain\r\n\r\n"
556 output += generate_config(uri[0], uri[1])
557 else:
558 assert False, "Invalid option"
559 print output
560
561def get_fqdn(datadump):
562 # Proxy naming convention is special
563 if datadump['nodetype'] == 'Proxy':
564 fqdn = datadump['nodename']
565 else:
566 # By default the full name is listed and also a shortname CNAME for easy use.
567 fqdn = datadump['nodetype'] + datadump['nodename']
568 return(fqdn)
569
570
571
572def make_dns(output_dir = 'dns'):
573 items = dict()
574
575 # hostname is key, IP is value
576 wleiden_zone = dict()
577 wleiden_cname = dict()
578
579 pool = dict()
580 for node in get_hostlist():
581 logger.info("Processing host %s", node)
582 datadump = get_yaml(node)
583
584 # Proxy naming convention is special
585 fqdn = get_fqdn(datadump)
586 if datadump['nodetype'] == 'CNode':
587 wleiden_cname[datadump['nodename']] = fqdn
588
589 wleiden_zone[fqdn] = datadump['masterip']
590
591 # Hacking to get proper DHCP IPs and hostnames
592 for iface_key in get_interface_keys(datadump):
593 iface_name = datadump[iface_key]['interface'].replace(':',"-alias-")
594 (ip, netmask) = datadump[iface_key]['ip'].split('/')
595 try:
596 (dhcp_start, dhcp_stop) = datadump[iface_key]['dhcp'].split('-')
597 datadump[iface_key]['subnet'] = netmask2subnet(netmask)
598 dhcp_part = ".".join(ip.split('.')[0:3])
599 if ip != datadump['masterip']:
600 wleiden_zone["dhcp-gateway-%s.%s" % (iface_name, fqdn)] = ip
601 for i in range(int(dhcp_start), int(dhcp_stop) + 1):
602 wleiden_zone["dhcp-%s-%s.%s" % (i, iface_name, fqdn)] = "%s.%s" % (dhcp_part, i)
603 except (AttributeError, ValueError):
604 # First push it into a pool, to indentify the counter-part later on
605 addr = parseaddr(ip)
606 netmask = int(netmask)
607 addr = addr & ~((1 << (32 - netmask)) - 1)
608 if pool.has_key(addr):
609 pool[addr] += [(iface_name, fqdn, ip)]
610 else:
611 pool[addr] = [(iface_name, fqdn, ip)]
612 continue
613
614
615 def pool_to_name(node, pool_members):
616 """Convert the joined name to a usable pool name"""
617
618 # Get rid of the own entry
619 pool_members = list(set(pool_members) - set([fqdn]))
620
621 target = oldname = ''
622 for node in sorted(pool_members):
623 (name, number) = re.match('^([A-Za-z]+)([0-9]*)$',node).group(1,2)
624 target += "-" + number if name == oldname else "-" + node if target else node
625 oldname = name
626
627 return target
628
629
630 # WL uses an /29 to configure an interface. IP's are ordered like this:
631 # MasterA (.1) -- DeviceA (.2) <<>> DeviceB (.3) --- SlaveB (.4)
632
633 sn = lambda x: re.sub(r'(?i)^cnode','',x)
634
635 # Automatic naming convention of interlinks namely 2 + remote.lower()
636 for (key,value) in pool.iteritems():
637 # Make sure they are sorted from low-ip to high-ip
638 value = sorted(value, key=lambda x: parseaddr(x[2]))
639
640 if len(value) == 1:
641 (iface_name, fqdn, ip) = value[0]
642 wleiden_zone["2unused-%s.%s" % (iface_name, fqdn)] = ip
643
644 # Device DNS names
645 if 'cnode' in fqdn.lower():
646 wleiden_zone["d-at-%s.%s" % (iface_name, fqdn)] = showaddr(parseaddr(ip) + 1)
647 wleiden_cname["d-at-%s.%s" % (iface_name,sn(fqdn))] = "d-at-%s.%s" % (iface_name, fqdn)
648
649 elif len(value) == 2:
650 (a_iface_name, a_fqdn, a_ip) = value[0]
651 (b_iface_name, b_fqdn, b_ip) = value[1]
652 wleiden_zone["2%s.%s" % (b_fqdn,a_fqdn)] = a_ip
653 wleiden_zone["2%s.%s" % (a_fqdn,b_fqdn)] = b_ip
654
655 # Device DNS names
656 if 'cnode' in a_fqdn.lower() and 'cnode' in b_fqdn.lower():
657 wleiden_zone["d-at-%s.%s" % (a_iface_name, a_fqdn)] = showaddr(parseaddr(a_ip) + 1)
658 wleiden_zone["d-at-%s.%s" % (b_iface_name, b_fqdn)] = showaddr(parseaddr(b_ip) - 1)
659 wleiden_cname["d-at-%s.%s" % (a_iface_name,sn(a_fqdn))] = "d-at-%s.%s" % (a_iface_name, a_fqdn)
660 wleiden_cname["d-at-%s.%s" % (b_iface_name,sn(b_fqdn))] = "d-at-%s.%s" % (b_iface_name, b_fqdn)
661 wleiden_cname["d2%s.%s" % (sn(b_fqdn),sn(a_fqdn))] = "d-at-%s.%s" % (a_iface_name, a_fqdn)
662 wleiden_cname["d2%s.%s" % (sn(a_fqdn),sn(b_fqdn))] = "d-at-%s.%s" % (b_iface_name, b_fqdn)
663
664 else:
665 pool_members = [k[1] for k in value]
666 for item in value:
667 (iface_name, fqdn, ip) = item
668 pool_name = "2pool-" + showaddr(key).replace('.','-') + "-" + pool_to_name(fqdn,pool_members)
669 wleiden_zone["%s.%s" % (pool_name, fqdn)] = ip
670
671 # Include static DNS entries
672 # XXX: Should they override the autogenerated results?
673 # XXX: Convert input to yaml more useable.
674 # Format:
675 ##; this is a comment
676 ## roomburgh=CNodeRoomburgh1
677 ## apkerk1.CNodeVosko=172.17.176.8 ;this as well
678 dns = yaml.load(open(os.path.join(NODE_DIR,'../dns/staticDNS.yaml'),'r'))
679
680 # Hack to allow special entries, for development
681 wleiden_raw = dns['raw']
682 del dns['raw']
683
684 for comment, block in dns.iteritems():
685 for k,v in block.iteritems():
686 if valid_addr(v):
687 wleiden_zone[k] = v
688 else:
689 wleiden_cname[k] = v
690
691 details = dict()
692 # 24 updates a day allowed
693 details['serial'] = time.strftime('%Y%m%d%H')
694
695 dns_header = '''
696$TTL 3h
697%(zone)s. SOA sunny.wleiden.net. beheer.lijst.wirelessleiden.nl. ( %(serial)s 1d 12h 1w 3h )
698 ; Serial, Refresh, Retry, Expire, Neg. cache TTL
699
700 NS sunny.wleiden.net.
701 \n'''
702
703
704 if not os.path.isdir('dns'):
705 os.makedirs('dns')
706 details['zone'] = 'wleiden.net'
707 f = open(os.path.join(output_dir,"db." + details['zone']), "w")
708 f.write(dns_header % details)
709
710 for host,ip in wleiden_zone.iteritems():
711 if valid_addr(ip):
712 f.write("%s.wleiden.net. IN A %s \n" % (host.lower(), ip))
713 for source,dest in wleiden_cname.iteritems():
714 f.write("%s.wleiden.net. IN CNAME %s.wleiden.net.\n" % (source.lower(), dest.lower()))
715 for source, dest in wleiden_raw.iteritems():
716 f.write("%s.wleiden.net. %s\n" % (source, dest))
717 f.close()
718
719 # Create whole bunch of specific sub arpa zones. To keep it compliant
720 for s in range(16,32):
721 details['zone'] = '%i.172.in-addr.arpa' % s
722 f = open(os.path.join(output_dir,"db." + details['zone']), "w")
723 f.write(dns_header % details)
724
725 #XXX: Not effient, fix to proper data structure and do checks at other
726 # stages
727 for host,ip in wleiden_zone.iteritems():
728 if valid_addr(ip):
729 if int(ip.split('.')[1]) == s:
730 rev_ip = '.'.join(reversed(ip.split('.')))
731 f.write("%s.in-addr.arpa. IN PTR %s.wleiden.net.\n" % (rev_ip.lower(), host.lower()))
732 f.close()
733
734
735def usage():
736 print """Usage: %s <standalone [port] |test [test arguments]|static|dns>
737Examples:
738\tdns [outputdir] = Generate BIND compliant zone files in dns.
739\tstandalone = Run configurator webserver [default port=8000]
740\twind-export = Generate SQL import scripts for WIND database
741\tfull-export = Generate yaml export script for heatmap.
742\tstatic = Generate all config files and store on disk
743\t with format ./static/%%NODE%%/%%FILE%%
744\ttest CNodeRick dnsmasq.conf = Receive output of CGI script
745\t for arguments CNodeRick/dnsmasq.conf
746\tlist <nodes|proxies> = List systems which marked up.
747"""
748 exit(0)
749
750
751
752def main():
753 """Hard working sub"""
754 # Allow easy hacking using the CLI
755 if not os.environ.has_key('PATH_INFO'):
756 if len(sys.argv) < 2:
757 usage()
758
759 if sys.argv[1] == "standalone":
760 import SocketServer
761 import CGIHTTPServer
762 # CGI does not go backward, little hack to get ourself in the right working directory.
763 os.chdir(os.path.dirname(__file__) + '/..')
764 try:
765 PORT = int(sys.argv[2])
766 except (IndexError,ValueError):
767 PORT = 8000
768
769 class MyCGIHTTPRequestHandler(CGIHTTPServer.CGIHTTPRequestHandler):
770 """ Serve this CGI from the root of the webserver """
771 def is_cgi(self):
772 if "favicon" in self.path:
773 return False
774
775 self.cgi_info = (__file__, self.path)
776 self.path = ''
777 return True
778 handler = MyCGIHTTPRequestHandler
779 SocketServer.TCPServer.allow_reuse_address = True
780 httpd = SocketServer.TCPServer(("", PORT), handler)
781 httpd.server_name = 'localhost'
782 httpd.server_port = PORT
783
784 logger.info("serving at port %s", PORT)
785 try:
786 httpd.serve_forever()
787 except KeyboardInterrupt:
788 httpd.shutdown()
789 logger.info("All done goodbye")
790 elif sys.argv[1] == "test":
791 os.environ['PATH_INFO'] = "/".join(sys.argv[2:])
792 os.environ['SCRIPT_NAME'] = __file__
793 process_cgi_request()
794 elif sys.argv[1] == "static":
795 items = dict()
796 for node in get_hostlist():
797 items['node'] = node
798 items['wdir'] = "./static/%(node)s" % items
799 if not os.path.isdir(items['wdir']):
800 os.makedirs(items['wdir'])
801 datadump = get_yaml(node)
802 for config in files:
803 items['config'] = config
804 logger.info("## Generating %(node)s %(config)s" % items)
805 f = open("%(wdir)s/%(config)s" % items, "w")
806 f.write(generate_config(node, config, datadump))
807 f.close()
808 elif sys.argv[1] == "wind-export":
809 items = dict()
810 for node in get_hostlist():
811 datadump = get_yaml(node)
812 sql = """INSERT IGNORE INTO nodes (name, name_ns, longitude, latitude)
813 VALUES ('%(nodename)s', '%(nodename)s', %(latitude)s, %(longitude)s);""" % datadump;
814 sql = """INSERT IGNORE INTO users_nodes (user_id, node_id, owner)
815 VALUES (
816 (SELECT id FROM users WHERE username = 'rvdzwet'),
817 (SELECT id FROM nodes WHERE name = '%(nodename)s'),
818 'Y');""" % datadump
819 #for config in files:
820 # items['config'] = config
821 # print "## Generating %(node)s %(config)s" % items
822 # f = open("%(wdir)s/%(config)s" % items, "w")
823 # f.write(generate_config(node, config, datadump))
824 # f.close()
825 for node in get_hostlist():
826 datadump = get_yaml(node)
827 for iface_key in sorted([elem for elem in datadump.keys() if elem.startswith('iface_')]):
828 ifacedump = datadump[iface_key]
829 if ifacedump.has_key('mode') and ifacedump['mode'] == 'ap-wds':
830 ifacedump['nodename'] = datadump['nodename']
831 if not ifacedump.has_key('channel') or not ifacedump['channel']:
832 ifacedump['channel'] = 0
833 sql = """INSERT INTO links (node_id, type, ssid, protocol, channel, status)
834 VALUES ((SELECT id FROM nodes WHERE name = '%(nodename)s'), 'ap',
835 '%(ssid)s', 'IEEE 802.11b', %(channel)s, 'active');""" % ifacedump
836 elif sys.argv[1] == "full-export":
837 hosts = {}
838 for node in get_hostlist():
839 datadump = get_yaml(node)
840 hosts[datadump['nodename']] = datadump
841 print yaml.dump(hosts)
842
843 elif sys.argv[1] == "dns":
844 make_dns(sys.argv[2] if len(sys.argv) > 2 else 'dns')
845 elif sys.argv[1] == "cleanup":
846 # First generate all datadumps
847 datadumps = dict()
848 for host in get_hostlist():
849 logger.info("# Processing: %s", host)
850 datadump = get_yaml(host)
851 datadumps[get_fqdn(datadump)] = datadump
852
853 for key,datadump in datadumps.iteritems():
854 datadump['latitude'], datadump['longitude'] = rdnap.rd2etrs(datadump['rdnap_x'], datadump['rdnap_y'])
855 store_yaml(datadump)
856 elif sys.argv[1] == "list":
857 if sys.argv[2] == "nodes":
858 systems = get_nodelist()
859 elif sys.argv[2] == "proxies":
860 systems = get_proxylist()
861 else:
862 usage()
863 for system in systems:
864 datadump = get_yaml(system)
865 if datadump['status'] == "up":
866 print system
867 else:
868 usage()
869 else:
870 cgitb.enable()
871 process_cgi_request()
872
873
874if __name__ == "__main__":
875 main()
Note: See TracBrowser for help on using the repository browser.