source: genesis/tools/gformat.py@ 10043

Last change on this file since 10043 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
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'
[9957]5#
6# XXX: This should be rewritten to make use of the ipaddr.py library.
7#
[8242]8# Rick van der Zwet <info@rickvanderzwet.nl>
[9957]9#
[8622]10
11# Hack to make the script directory is also threated as a module search path.
12import sys
13import os
[9286]14import re
[8622]15sys.path.append(os.path.dirname(__file__))
16
[8242]17import cgi
[8267]18import cgitb
19import copy
[8242]20import glob
21import socket
22import string
23import subprocess
24import time
[8622]25import rdnap
[8584]26from pprint import pprint
[8575]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)
[8588]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
[9697]40import logging
41logging.basicConfig(format='# %(levelname)s: %(message)s' )
42logger = logging.getLogger()
43logger.setLevel(logging.DEBUG)
[8242]44
[9283]45
[8948]46if os.environ.has_key('CONFIGROOT'):
47 NODE_DIR = os.environ['CONFIGROOT']
48else:
[9283]49 NODE_DIR = os.path.abspath(os.path.dirname(__file__)) + '/../nodes'
[8242]50__version__ = '$Id: gformat.py 10041 2012-03-06 14:12:00Z rick $'
51
[8267]52
[9283]53files = [
[8242]54 'authorized_keys',
55 'dnsmasq.conf',
56 'rc.conf.local',
57 'resolv.conf',
58 'wleiden.yaml'
59 ]
60
[8319]61# Global variables uses
[8323]62OK = 10
63DOWN = 20
64UNKNOWN = 90
[8257]65
66
[8267]67def get_proxylist():
68 """Get all available proxies proxyX sorting based on X number"""
[10041]69 proxylist = sorted([os.path.basename(x) for x in glob.glob("%s/proxy*" % NODE_DIR)],
[8267]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
[8321]76def valid_addr(addr):
77 """ Show which address is valid in which are not """
78 return str(addr).startswith('172.')
79
80
[8267]81def get_nodelist():
82 """ Get all available nodes - sorted """
[10041]83 nodelist = sorted([os.path.basename(x) for x in glob.glob("%s/CNode*" % NODE_DIR)])
[8267]84 return nodelist
85
[8296]86def get_hostlist():
87 """ Combined hosts and proxy list"""
88 return get_nodelist() + get_proxylist()
[8267]89
[8588]90def angle_between_points(lat1,lat2,long1,long2):
[9283]91 """
[8588]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
[8267]99
[8588]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"
[9283]112 elif degrees < (90 - p):
[8588]113 return "no"
[9283]114 elif degrees < (90 + p):
[8588]115 return "o"
[9283]116 elif degrees < (180 - p):
[8588]117 return "zo"
[9283]118 elif degrees < (180 + p):
[8588]119 return "z"
[9283]120 elif degrees < (270 - p):
[8588]121 return "zw"
[9283]122 elif degrees < (270 + p):
[8588]123 return "w"
[9283]124 elif degrees < (360 - p):
[8588]125 return "nw"
126 else:
127 return "n"
128
129
[8267]130def generate_title(nodelist):
[8257]131 """ Main overview page """
[9283]132 items = {'root' : "." }
[8267]133 output = """
[8257]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>
[8259]146 <form type="GET" action="%(root)s">
[8257]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
[8242]153
[8296]154 for node in nodelist:
[8257]155 items['node'] = node
[8267]156 output += '<tr><td><a href="%(root)s/%(node)s">%(node)s</a></td>' % items
[8257]157 for config in files:
158 items['config'] = config
[8267]159 output += '<td><a href="%(root)s/%(node)s/%(config)s">%(config)s</a></td>' % items
160 output += "</tr>"
161 output += """
[8257]162 </table>
163 <hr />
164 <em>%s</em>
165 </center>
166 </body>
167</html>
168 """ % __version__
[8242]169
[8267]170 return output
[8257]171
172
[8267]173
174def generate_node(node):
[8257]175 """ Print overview of all files available for node """
[8267]176 return "\n".join(files)
[8242]177
[8257]178
179
[8242]180def generate_header(ctag="#"):
181 return """\
[9283]182%(ctag)s
[8242]183%(ctag)s DO NOT EDIT - Automatically generated by 'gformat'
184%(ctag)s Generated at %(date)s by %(host)s
[9283]185%(ctag)s
[8242]186""" % { 'ctag' : ctag, 'date' : time.ctime(), 'host' : socket.gethostname() }
187
[8257]188
189
[8242]190def parseaddr(s):
[8257]191 """ Process IPv4 CIDR notation addr to a (binary) number """
[8242]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
[8257]198
199
[8242]200def showaddr(a):
[8257]201 """ Display IPv4 addr in (dotted) CIDR notation """
[8242]202 return "%d.%d.%d.%d" % ((a >> 24) & 0xff, (a >> 16) & 0xff, (a >> 8) & 0xff, a & 0xff)
203
[8257]204
[8584]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
[8257]213
[8584]214
215
[9283]216
[8242]217def netmask2subnet(netmask):
[8257]218 """ Given a 'netmask' return corresponding CIDR """
[8242]219 return showaddr(0xffffffff & (0xffffffff << (32 - int(netmask))))
220
[8257]221
222
[8242]223def generate_dnsmasq_conf(datadump):
[8257]224 """ Generate configuration file '/usr/local/etc/dnsmasq.conf' """
[8242]225 output = generate_header()
226 output += """\
[9283]227# DHCP server options
[8242]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']:
[8262]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]
[8242]242
243 try:
[8257]244 (dhcp_start, dhcp_stop) = datadump[iface_key]['dhcp'].split('-')
[8242]245 (ip, netmask) = datadump[iface_key]['ip'].split('/')
246 datadump[iface_key]['subnet'] = netmask2subnet(netmask)
[8262]247 except (AttributeError, ValueError):
[8242]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]
[9283]255
[8242]256 return output
257
[8257]258
259
[8242]260def generate_rc_conf_local(datadump):
[8257]261 """ Generate configuration file '/etc/rc.conf.local' """
[8242]262 output = generate_header("#");
263 output += """\
264hostname='%(nodetype)s%(nodename)s.%(domain)s'
[9283]265location='%(location)s'
[8242]266""" % datadump
[9283]267
[8242]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"
[9283]278
[8242]279 output += '\n'
280 # lo0 configuration:
281 # - 172.32.255.1/32 is the proxy.wleiden.net deflector
[9283]282 # - masterip is special as it needs to be assigned to at
[8242]283 # least one interface, so if not used assign to lo0
[9808]284 addrs_list = { 'lo0' : [("127.0.0.1/8", "LocalHost"), ("172.31.255.1/32","Proxy IP")] }
[9283]285 iface_map = {'lo0' : 'lo0'}
[8242]286
[8297]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
[9283]292 if not masterip_used:
[8297]293 addrs_list['lo0'].append(datadump['masterip'] + "/32")
294
[8242]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
[9808]303 item = (ifacedump['ip'], ifacedump['desc'])
[8242]304 if addrs_list.has_key(interface):
[9808]305 addrs_list[interface].append(item)
[8242]306 else:
[9808]307 addrs_list[interface] = [item]
[8242]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
[9283]317 ifacedump['wlanif'] ="wlan%i" % wlan_count
[8242]318 iface_map[interface] = ifacedump['wlanif']
319 wlan_count += 1
320
321 # Default to station (client) mode
322 ifacedump['wlanmode'] = "sta"
[8274]323 if ifacedump['mode'] in ['master', 'master-wds']:
[8242]324 ifacedump['wlanmode'] = "ap"
325 # Default to 802.11b mode
326 ifacedump['mode'] = '11b'
327 if ifacedump['type'] in ['11a', '11b' '11g']:
[9283]328 ifacedump['mode'] = ifacedump['type']
[8242]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 " +\
[8274]342 "%(mode)s ssid %(ssid)s %(extra)s channel %(channel)s'\n") % ifacedump
[9283]343
[8242]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
[9283]350 # Print IP address which needs to be assigned over here
[8242]351 output += "\n"
352 for iface,addrs in sorted(addrs_list.iteritems()):
[9808]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]))
[8242]356
357 return output
358
[8257]359
360
[8242]361def get_yaml(item):
[8257]362 """ Get configuration yaml for 'item'"""
[9284]363 gfile = os.path.join(NODE_DIR,item,'wleiden.yaml')
[8242]364
365 f = open(gfile, 'r')
[8588]366 datadump = yaml.load(f,Loader=Loader)
[8242]367 f.close()
368
369 return datadump
370
[8588]371def store_yaml(datadump):
372 """ Store configuration yaml for 'item'"""
[9284]373 gfile = os.path.join(NODE_DIR,item,'wleiden.yaml')
[8257]374
[8588]375 f = open(gfile, 'w')
376 f.write(generate_wleiden_yaml(datadump))
377 f.close()
[8257]378
[8588]379
380
[8317]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
[8319]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)]
[8317]393
[8319]394
[8317]395def get_used_ips(configs):
396 """ Return array of all IPs used in config files"""
397 ip_list = []
[8319]398 for config in configs:
[8317]399 ip_list.append(config['masterip'])
[8319]400 for iface_key in get_interface_keys(config):
[8317]401 l = config[iface_key]['ip']
402 addr, mask = l.split('/')
403 # Special case do not process
[8332]404 if valid_addr(addr):
405 ip_list.append(addr)
406 else:
[9728]407 logger.error("## IP '%s' in '%s' not valid" % (addr, config['nodename']))
[8317]408 return sorted(ip_list)
409
410
411
[8267]412def write_yaml(item, datadump):
413 """ Write configuration yaml for 'item'"""
[9284]414 gfile = os.path.join(NODE_DIR,item,'wleiden.yaml')
[8267]415
416 f = open(gfile, 'w')
417 f.write(format_wleiden_yaml(datadump))
418 f.close()
419
420
421
[8242]422def generate_resolv_conf(datadump):
[8257]423 """ Generate configuration file '/etc/resolv.conf' """
[8242]424 output = generate_header("#");
425 output += """\
426search wleiden.net
427# Try local (cache) first
428nameserver 127.0.0.1
429
[9283]430# Proxies are recursive nameservers
[8242]431# needs to be in resolv.conf for dnsmasq as well
432""" % datadump
[9283]433
[8267]434 for proxy in get_proxylist():
[8242]435 proxy_ip = get_yaml(proxy)['masterip']
436 output += "nameserver %-15s # %s\n" % (proxy_ip, proxy)
437 return output
438
439
[8257]440
[8267]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
[9283]447 return output
[8267]448
449
450
451def format_wleiden_yaml(datadump):
[8242]452 """ Special formatting to ensure it is editable"""
[9283]453 output = "# Genesis config yaml style\n"
[8262]454 output += "# vim:ts=2:et:sw=2:ai\n"
[8242]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)):
[8267]458 output += "%-10s: %s\n" % (key, format_yaml_value(datadump[key]))
[9283]459
[8242]460 output += "\n\n"
[9283]461
[8272]462 key_order = [ 'comment', 'interface', 'ip', 'desc', 'sdesc', 'mode', 'type',
463 'extra_type', 'channel', 'ssid', 'dhcp' ]
464
[8242]465 for iface_key in sorted(iface_keys):
466 output += "%s:\n" % iface_key
[8272]467 for key in key_order + list(sorted(set(datadump[iface_key].keys()) - set(key_order))):
468 if datadump[iface_key].has_key(key):
[9283]469 output += " %-11s: %s\n" % (key, format_yaml_value(datadump[iface_key][key]))
[8242]470 output += "\n\n"
471
472 return output
473
474
[8257]475
[8267]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
[8588]483def generate_yaml(datadump):
484 return generate_config(datadump['nodename'], "wleiden.yaml", datadump)
[8267]485
[8588]486
[9283]487
[8298]488def generate_config(node, config, datadump=None):
[8257]489 """ Print configuration file 'config' of 'node' """
[8267]490 output = ""
[8242]491 try:
492 # Load config file
[8298]493 if datadump == None:
494 datadump = get_yaml(node)
[9283]495
[8267]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
[8242]503 if config == 'wleiden.yaml':
[8267]504 output += generate_wleiden_yaml(datadump)
505 elif config == 'authorized_keys':
[8242]506 f = open("global_keys", 'r')
[8267]507 output += f.read()
[8242]508 f.close()
509 elif config == 'dnsmasq.conf':
[8267]510 output += generate_dnsmasq_conf(datadump_extra)
[8242]511 elif config == 'rc.conf.local':
[8267]512 output += generate_rc_conf_local(datadump_extra)
[8242]513 elif config == 'resolv.conf':
[8267]514 output += generate_resolv_conf(datadump_extra)
[8242]515 else:
[9283]516 assert False, "Config not found!"
[8242]517 except IOError, e:
[8267]518 output += "[ERROR] Config file not found"
519 return output
[8242]520
521
[8257]522
[8258]523def process_cgi_request():
524 """ When calling from CGI """
525 # Update repository if requested
526 form = cgi.FieldStorage()
527 if form.getvalue("action") == "update":
[8259]528 print "Refresh: 5; url=."
[8258]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)
[9283]534
535
[8258]536 uri = os.environ['PATH_INFO'].strip('/').split('/')
[8267]537 output = ""
[8258]538 if not uri[0]:
[8267]539 output += "Content-type:text/html\r\n\r\n"
[8296]540 output += generate_title(get_hostlist())
[8258]541 elif len(uri) == 1:
[8267]542 output += "Content-type:text/plain\r\n\r\n"
543 output += generate_node(uri[0])
[8258]544 elif len(uri) == 2:
[8267]545 output += "Content-type:text/plain\r\n\r\n"
546 output += generate_config(uri[0], uri[1])
[8258]547 else:
548 assert False, "Invalid option"
[8267]549 print output
[8242]550
[8588]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)
[8259]559
[9283]560
561
[9284]562def make_dns(output_dir = 'dns'):
[8588]563 items = dict()
[8598]564
[8588]565 # hostname is key, IP is value
566 wleiden_zone = dict()
567 wleiden_cname = dict()
[8598]568
[8588]569 pool = dict()
570 for node in get_hostlist():
[9697]571 logger.info("Processing host %s", node)
[8588]572 datadump = get_yaml(node)
[9283]573
[8588]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
[8598]581 # Hacking to get proper DHCP IPs and hostnames
[8588]582 for iface_key in get_interface_keys(datadump):
[8598]583 iface_name = datadump[iface_key]['interface'].replace(':',"-alias-")
[8588]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)
[9283]598 if pool.has_key(addr):
[8588]599 pool[addr] += [(iface_name, fqdn, ip)]
[9283]600 else:
[8588]601 pool[addr] = [(iface_name, fqdn, ip)]
602 continue
603
[9286]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
[9957]620 # WL uses an /29 to configure an interface. IP's are ordered like this:
[9958]621 # MasterA (.1) -- DeviceA (.2) <<>> DeviceB (.3) --- SlaveB (.4)
[9957]622
623 sn = lambda x: re.sub(r'(?i)^cnode','',x)
624
[8598]625 # Automatic naming convention of interlinks namely 2 + remote.lower()
[8588]626 for (key,value) in pool.iteritems():
[9958]627 # Make sure they are sorted from low-ip to high-ip
628 value = sorted(value, key=lambda x: parseaddr(x[2]))
629
[8588]630 if len(value) == 1:
631 (iface_name, fqdn, ip) = value[0]
632 wleiden_zone["2unused-%s.%s" % (iface_name, fqdn)] = ip
[9957]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
[8588]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
[9957]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)
[9958]648 wleiden_zone["d-at-%s.%s" % (b_iface_name, b_fqdn)] = showaddr(parseaddr(b_ip) - 1)
[9957]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
[8588]654 else:
655 pool_members = [k[1] for k in value]
656 for item in value:
[9283]657 (iface_name, fqdn, ip) = item
[9286]658 pool_name = "2pool-" + showaddr(key).replace('.','-') + "-" + pool_to_name(fqdn,pool_members)
[8588]659 wleiden_zone["%s.%s" % (pool_name, fqdn)] = ip
[8598]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
[9284]668 dns = yaml.load(open(os.path.join(NODE_DIR,'../dns/staticDNS.yaml'),'r'))
[9938]669
670 # Hack to allow special entries, for development
671 wleiden_raw = dns['raw']
672 del dns['raw']
673
[8622]674 for comment, block in dns.iteritems():
675 for k,v in block.iteritems():
[8598]676 if valid_addr(v):
677 wleiden_zone[k] = v
678 else:
679 wleiden_cname[k] = v
[9283]680
[8598]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
[9283]693
[8598]694 if not os.path.isdir('dns'):
695 os.makedirs('dns')
696 details['zone'] = 'wleiden.net'
[9284]697 f = open(os.path.join(output_dir,"db." + details['zone']), "w")
[8598]698 f.write(dns_header % details)
699
[8588]700 for host,ip in wleiden_zone.iteritems():
[8598]701 if valid_addr(ip):
[9283]702 f.write("%s.wleiden.net. IN A %s \n" % (host.lower(), ip))
[8588]703 for source,dest in wleiden_cname.iteritems():
[8636]704 f.write("%s.wleiden.net. IN CNAME %s.wleiden.net.\n" % (source.lower(), dest.lower()))
[9938]705 for source, dest in wleiden_raw.iteritems():
706 f.write("%s.wleiden.net. %s\n" % (source, dest))
[8588]707 f.close()
[9283]708
[8598]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
[9284]712 f = open(os.path.join(output_dir,"db." + details['zone']), "w")
[8598]713 f.write(dns_header % details)
[8588]714
[8598]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('.')))
[9283]721 f.write("%s.in-addr.arpa. IN PTR %s.wleiden.net.\n" % (rev_ip.lower(), host.lower()))
[8598]722 f.close()
[8588]723
[8598]724
[8259]725def usage():
[8598]726 print """Usage: %s <standalone [port] |test [test arguments]|static|dns>
[8259]727Examples:
[9284]728\tdns [outputdir] = Generate BIND compliant zone files in dns.
[8259]729\tstandalone = Run configurator webserver [default port=8000]
[9589]730\twind-export = Generate SQL import scripts for WIND database
731\tfull-export = Generate yaml export script for heatmap.
[8296]732\tstatic = Generate all config files and store on disk
733\t with format ./static/%%NODE%%/%%FILE%%
[9283]734\ttest CNodeRick dnsmasq.conf = Receive output of CGI script
[8259]735\t for arguments CNodeRick/dnsmasq.conf
[9971]736\tlist <nodes|proxies> = List systems which marked up.
[8259]737"""
738 exit(0)
739
740
741
[8267]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()
[9283]748
[8267]749 if sys.argv[1] == "standalone":
750 import SocketServer
751 import CGIHTTPServer
[8867]752 # CGI does not go backward, little hack to get ourself in the right working directory.
753 os.chdir(os.path.dirname(__file__) + '/..')
[8267]754 try:
755 PORT = int(sys.argv[2])
756 except (IndexError,ValueError):
757 PORT = 8000
[9283]758
[8267]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
[9283]764
[8267]765 self.cgi_info = (__file__, self.path)
766 self.path = ''
767 return True
768 handler = MyCGIHTTPRequestHandler
[9807]769 SocketServer.TCPServer.allow_reuse_address = True
[8267]770 httpd = SocketServer.TCPServer(("", PORT), handler)
771 httpd.server_name = 'localhost'
772 httpd.server_port = PORT
[9283]773
[9728]774 logger.info("serving at port %s", PORT)
[8860]775 try:
776 httpd.serve_forever()
777 except KeyboardInterrupt:
778 httpd.shutdown()
[9728]779 logger.info("All done goodbye")
[8267]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()
[8296]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'])
[8298]791 datadump = get_yaml(node)
[8296]792 for config in files:
793 items['config'] = config
[9728]794 logger.info("## Generating %(node)s %(config)s" % items)
[8296]795 f = open("%(wdir)s/%(config)s" % items, "w")
[8298]796 f.write(generate_config(node, config, datadump))
[8296]797 f.close()
[9514]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
[9589]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
[8584]833 elif sys.argv[1] == "dns":
[9285]834 make_dns(sys.argv[2] if len(sys.argv) > 2 else 'dns')
[9283]835 elif sys.argv[1] == "cleanup":
[8588]836 # First generate all datadumps
837 datadumps = dict()
838 for host in get_hostlist():
[9728]839 logger.info("# Processing: %s", host)
[8588]840 datadump = get_yaml(host)
841 datadumps[get_fqdn(datadump)] = datadump
[9283]842
[8622]843 datadump['latitude'], datadump['longitude'] = rdnap.rd2etrs(datadump['rdnap_x'], datadump['rdnap_y'])
[8588]844 write_yaml(host, datadump)
[9971]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
[9283]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.