source: genesis/tools/gformat.py@ 13169

Last change on this file since 13169 was 13169, checked in by rick, 8 years ago

Quick to define bridge interfaces, example:

iface_bridge0:

comment : "Link naar RickRing"
ip : "172.16.6.114/28"
sdesc : "2ring"
type : "eth"
dhcp : False
status : "up"
members : "vr1 vr2"

iface_bridge0_alias0:

comment : "Link naar Ed"
ip : "172.17.2.250/29"
sdesc : "2ed"
type : "eth"
dhcp : False
status : "up"

  • Property svn:executable set to *
  • Property svn:keywords set to Id
File size: 68.6 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# Sample apache configuration (mind the AcceptPathInfo!)
9# ScriptAlias /wleiden/config /usr/local/www/genesis/tools/gformat.py
10# <Directory /usr/local/www/genesis>
11# Allow from all
12# AcceptPathInfo On
13# </Directory>
14#
15# MUCH FASTER WILL IT BE with mod_wsgi, due to caches and avoiding loading all
16# the heavy template lifting all the time.
17#
18# WSGIDaemonProcess gformat threads=25
19# WSGISocketPrefix run/wsgi
20#
21# <Directory /var/www/cgi-bin>
22# WSGIProcessGroup gformat
23# </Directory>
24# WSGIScriptAlias /hello /var/www/cgi-bin/genesis/tools/gformat.py
25#
26# Package dependencies list:
27# yum install python-yaml pyproj proj-epsg python-jinja2
28#
29# Rick van der Zwet <info@rickvanderzwet.nl>
30#
31
32# Hack to make the script directory is also threated as a module search path.
33import sys
34import os
35sys.path.append(os.path.dirname(__file__))
36
37SVN = filter(os.path.isfile, ('/usr/local/bin/svn', '/usr/bin/svn'))[0]
38
39import argparse
40import cgi
41import cgitb
42import copy
43import glob
44import make_network_kml
45import math
46import pyproj
47import random
48import re
49import socket
50import string
51import subprocess
52import textwrap
53import time
54import urlparse
55
56from pprint import pprint
57from collections import defaultdict
58try:
59 import yaml
60except ImportError, e:
61 print e
62 print "[ERROR] Please install the python-yaml or devel/py-yaml package"
63 exit(1)
64
65try:
66 from yaml import CLoader as Loader
67 from yaml import CDumper as Dumper
68except ImportError:
69 from yaml import Loader, Dumper
70
71from jinja2 import Environment, Template
72def yesorno(value):
73 return "YES" if bool(value) else "NO"
74env = Environment()
75env.filters['yesorno'] = yesorno
76def render_template(datadump, template):
77 result = env.from_string(template).render(datadump)
78 # Make it look pretty to the naked eye, as jinja templates are not so
79 # friendly when it comes to whitespace formatting
80 ## Remove extra whitespace at end of line lstrip() style.
81 result = re.sub(r'\n[\ ]+','\n', result)
82 ## Include only a single newline between an definition and a comment
83 result = re.sub(r'(["\'])\n+([a-z]|\n#\n)',r'\1\n\2', result)
84 ## Remove extra newlines after single comment
85 result = re.sub(r'(#\n)\n+([a-z])',r'\1\2', result)
86 return result
87
88import logging
89logging.basicConfig(format='# %(levelname)s: %(message)s' )
90logger = logging.getLogger()
91logger.setLevel(logging.DEBUG)
92
93
94if os.environ.has_key('CONFIGROOT'):
95 NODE_DIR = os.environ['CONFIGROOT']
96else:
97 NODE_DIR = os.path.abspath(os.path.dirname(__file__)) + '/../nodes'
98__version__ = '$Id: gformat.py 13169 2015-02-17 20:47:27Z rick $'
99
100files = [
101 'authorized_keys',
102 'dnsmasq.conf',
103 'dhcpd.conf',
104 'rc.conf.local',
105 'resolv.conf',
106 'motd',
107 'ntp.conf',
108 'pf.hybrid.conf.local',
109 'wleiden.yaml',
110 ]
111
112# Global variables uses
113OK = 10
114DOWN = 20
115UNKNOWN = 90
116
117
118ileiden_proxies = []
119normal_proxies = []
120datadump_cache = {}
121interface_list_cache = {}
122rc_conf_local_cache = {}
123nameservers_cache = []
124def clear_cache():
125 ''' Poor mans cache implementation '''
126 global datadump_cache, interface_list_cache, rc_conf_local_cache, ileiden_proxies, normal_proxies, nameservers_cache
127 datadump_cache = {}
128 interface_list_cache = {}
129 rc_conf_local_cache = {}
130 ileiden_proxies = []
131 normal_proxies = []
132 nameservers_cache = []
133
134NO_DHCP = 0
135DHCP_CLIENT = 10
136DHCP_SERVER = 20
137def dhcp_type(item):
138 if not item.has_key('dhcp'):
139 return NO_DHCP
140 elif not item['dhcp']:
141 return NO_DHCP
142 elif item['dhcp'].lower() == 'client':
143 return DHCP_CLIENT
144 else:
145 # Validation Checks
146 begin,end = map(int,item['dhcp'].split('-'))
147 if begin >= end:
148 raise ValueError("DHCP Start >= DHCP End")
149 return DHCP_SERVER
150
151def etrs2rd(lat, lon):
152 p1 = pyproj.Proj(proj='latlon',datum='WGS84')
153 p2 = pyproj.Proj(init='EPSG:28992')
154 RDx, RDy = pyproj.transform(p1,p2,lon, lat)
155 return (RDx, RDy)
156
157def rd2etrs(RDx, RDy):
158 p1 = pyproj.Proj(init='EPSG:28992')
159 p2 = pyproj.Proj(proj='latlon',datum='WGS84')
160 lon, lat = pyproj.transform(p1,p2, RDx, RDy)
161 return (lat, lon)
162
163def get_yaml(item,add_version_info=True):
164 try:
165 """ Get configuration yaml for 'item'"""
166 if datadump_cache.has_key(item):
167 return datadump_cache[item].copy()
168
169 gfile = os.path.join(NODE_DIR,item,'wleiden.yaml')
170
171 # Default values
172 datadump = {
173 'autogen_revision' : 'NOTFOUND',
174 'autogen_gfile' : gfile,
175 }
176 f = open(gfile, 'r')
177 datadump.update(yaml.load(f,Loader=Loader))
178 if datadump['nodetype'] == 'Hybrid':
179 # Some values are defined implicitly
180 if datadump.has_key('rdr_rules') and datadump['rdr_rules'] and not datadump.has_key('service_incoming_rdr'):
181 datadump['service_incoming_rdr'] = True
182 # Use some boring defaults
183 defaults = {
184 'service_proxy_normal' : False,
185 'service_proxy_ileiden' : False,
186 'service_accesspoint' : True,
187 'service_incoming_rdr' : False,
188 'service_concentrator' : False,
189 'monitoring_group' : 'wleiden',
190 }
191 for (key,value) in defaults.iteritems():
192 if not datadump.has_key(key):
193 datadump[key] = value
194 f.close()
195
196 # Sometimes getting version information is useless or harmfull, like in the pre-commit hooks
197 if add_version_info:
198 p = subprocess.Popen([SVN, 'info', datadump['autogen_gfile']], stderr=subprocess.STDOUT, stdout=subprocess.PIPE)
199 lines = p.communicate()[0].split('\n')
200 if p.returncode == 0:
201 for line in lines:
202 if line:
203 (key, value) = line.split(': ')
204 datadump["autogen_" + key.lower().replace(' ','_')] = value
205
206 # Preformat certain needed variables for formatting and push those into special object
207 datadump['autogen_iface_keys'] = get_interface_keys(datadump)
208
209 wlan_count=0
210 try:
211 for key in datadump['autogen_iface_keys']:
212 datadump[key]['autogen_ifbase'] = key.split('_')[1]
213 datadump[key]['autogen_gateway'] = datadump[key]['ip'].split('/')[0]
214 if datadump[key]['type'] in ['11a', '11b', '11g', 'wireless']:
215 datadump[key]['autogen_ifname'] = 'wlan%i' % wlan_count
216 wlan_count += 1
217 else:
218 datadump[key]['autogen_ifname'] = datadump[key]['autogen_ifbase']
219 datadump[key]['autogen_bridge'] = datadump[key]['autogen_ifbase'].startswith('bridge')
220 if datadump[key]['autogen_bridge'] and not 'alias' in key:
221 datadump[key]['autogen_bridge_interfaces'] = datadump[key]['members'].split()
222 except Exception as e:
223 print "# Error while processing interface %s" % key
224 raise
225
226 dhcp_interfaces = [datadump[key]['autogen_ifname'] for key in datadump['autogen_iface_keys'] \
227 if dhcp_type(datadump[key]) == DHCP_SERVER]
228
229 datadump['autogen_dhcp_interfaces'] = dhcp_interfaces
230 datadump['autogen_item'] = item
231
232 datadump['autogen_realname'] = get_realname(datadump)
233 datadump['autogen_domain'] = datadump['domain'] if datadump.has_key('domain') else 'wleiden.net.'
234 datadump['autogen_fqdn'] = datadump['autogen_realname'] + '.' + datadump['autogen_domain']
235 datadump_cache[item] = datadump.copy()
236 except Exception as e:
237 print "# Error while processing %s" % item
238 raise
239 return datadump
240
241
242def store_yaml(datadump, header=False):
243 """ Store configuration yaml for 'item'"""
244 item = datadump['autogen_item']
245 gfile = os.path.join(NODE_DIR,item,'wleiden.yaml')
246
247 output = generate_wleiden_yaml(datadump, header)
248
249 f = open(gfile, 'w')
250 f.write(output)
251 f.close()
252
253
254def network(ip):
255 addr, mask = ip.split('/')
256 # Not parsing of these folks please
257 addr = parseaddr(addr)
258 mask = int(mask)
259 network = addr & ~((1 << (32 - mask)) - 1)
260 return network
261
262
263
264def make_relations(datadumps=dict()):
265 """ Process _ALL_ yaml files to get connection relations """
266 errors = []
267 poel = defaultdict(list)
268
269 if not datadumps:
270 for host in get_hostlist():
271 datadumps[host] = get_yaml(host)
272
273 for host, datadump in datadumps.iteritems():
274 try:
275 for iface_key in datadump['autogen_iface_keys']:
276 net_addr = network(datadump[iface_key]['ip'])
277 poel[net_addr] += [(host,datadump[iface_key])]
278 except (KeyError, ValueError), e:
279 errors.append("[FOUT] in '%s' interface '%s' (%s)" % (host,iface_key, e))
280 continue
281 return (poel, errors)
282
283
284
285def valid_addr(addr):
286 """ Show which address is valid in which are not """
287 return str(addr).startswith('172.')
288
289def get_system_list(prefix):
290 return sorted([os.path.basename(os.path.dirname(x)) for x in glob.glob("%s/%s*/wleiden.yaml" % (NODE_DIR, prefix))])
291
292get_hybridlist = lambda: get_system_list("Hybrid")
293get_nodelist = lambda: get_system_list("CNode")
294get_proxylist = lambda: get_system_list("Proxy")
295
296def get_hostlist():
297 """ Combined hosts and proxy list"""
298 return get_nodelist() + get_proxylist() + get_hybridlist()
299
300def angle_between_points(lat1,lat2,long1,long2):
301 """
302 Return Angle in radians between two GPS coordinates
303 See: http://stackoverflow.com/questions/3809179/angle-between-2-gps-coordinates
304 """
305 dy = lat2 - lat1
306 dx = math.cos(lat1)*(long2 - long1)
307 angle = math.atan2(dy,dx)
308 return angle
309
310
311
312def angle_to_cd(angle):
313 """ Return Dutch Cardinal Direction estimation in 'one digit' of radian angle """
314
315 # For easy conversion get positive degree
316 degrees = math.degrees(angle)
317 abs_degrees = 360 + degrees if degrees < 0 else degrees
318
319 # Numbers can be confusing calculate from the 4 main directions
320 p = 22.5
321 if abs_degrees < p:
322 cd = "n"
323 elif abs_degrees < (90 - p):
324 cd = "no"
325 elif abs_degrees < (90 + p):
326 cd = "o"
327 elif abs_degrees < (180 - p):
328 cd = "zo"
329 elif abs_degrees < (180 + p):
330 cd = "z"
331 elif abs_degrees < (270 - p):
332 cd = "zw"
333 elif abs_degrees < (270 + p):
334 cd = "w"
335 elif abs_degrees < (360 - p):
336 cd = "nw"
337 else:
338 cd = "n"
339 return cd
340
341
342
343def cd_between_hosts(hostA, hostB, datadumps):
344 # Using RDNAP coordinates
345 dx = float(int(datadumps[hostA]['rdnap_x']) - int(datadumps[hostB]['rdnap_x'])) * -1
346 dy = float(int(datadumps[hostA]['rdnap_y']) - int(datadumps[hostB]['rdnap_y'])) * -1
347 return angle_to_cd(math.atan2(dx,dy))
348
349 # GPS coordinates seems to fail somehow
350 #latA = float(datadumps[hostA]['latitude'])
351 #latB = float(datadumps[hostB]['latitude'])
352 #lonA = float(datadumps[hostA]['longitude'])
353 #lonB = float(datadumps[hostB]['longitude'])
354 #return angle_to_cd(angle_between_points(latA, latB, lonA, lonB))
355
356
357def generate_title(nodelist):
358 """ Main overview page """
359 items = {'root' : "." }
360 def fl(spaces, line):
361 return (' ' * spaces) + line + '\n'
362
363 output = """
364<html>
365 <head>
366 <title>Wireless leiden Configurator - GFormat</title>
367 <style type="text/css">
368 th {background-color: #999999}
369 tr:nth-child(odd) {background-color: #cccccc}
370 tr:nth-child(even) {background-color: #ffffff}
371 th, td {padding: 0.1em 1em}
372 </style>
373 </head>
374 <body>
375 <center>
376 <form type="GET" action="%(root)s">
377 <input type="hidden" name="action" value="update">
378 <input type="submit" value="Update Configuration Database (SVN)">
379 </form>
380 <table>
381 <caption><h3>Wireless Leiden Configurator</h3></caption>
382 """ % items
383
384 for node in nodelist:
385 items['node'] = node
386 output += fl(5, '<tr>') + fl(7,'<td><a href="%(root)s/%(node)s">%(node)s</a></td>' % items)
387 for config in files:
388 items['config'] = config
389 output += fl(7,'<td><a href="%(root)s/%(node)s/%(config)s">%(config)s</a></td>' % items)
390 output += fl(5, "</tr>")
391 output += """
392 </table>
393 <hr />
394 <em>%s</em>
395 </center>
396 </body>
397</html>
398 """ % __version__
399
400 return output
401
402
403
404def generate_node(node):
405 """ Print overview of all files available for node """
406 return "\n".join(files)
407
408def generate_node_overview(host):
409 """ Print overview of all files available for node """
410 datadump = get_yaml(host)
411 params = { 'host' : host }
412 output = "<em><a href='..'>Back to overview</a></em><hr />"
413 output += "<h2>Available files:</h2><ul>"
414 for cf in files:
415 params['cf'] = cf
416 output += '<li><a href="%(host)s/%(cf)s">%(cf)s</a></li>\n' % params
417 output += "</ul>"
418
419 # Generate and connection listing
420 output += "<h2>Connected To:</h2><ul>"
421 (poel, errors) = make_relations()
422 for network, hosts in poel.iteritems():
423 if host in [x[0] for x in hosts]:
424 if len(hosts) == 1:
425 # Single not connected interface
426 continue
427 for remote,ifacedump in hosts:
428 if remote == host:
429 # This side of the interface
430 continue
431 params = { 'remote': remote, 'remote_ip' : ifacedump['ip'] }
432 output += '<li><a href="%(remote)s">%(remote)s</a> -- %(remote_ip)s</li>\n' % params
433 output += "</ul>"
434 output += "<h2>MOTD details:</h2><pre>" + generate_motd(datadump) + "</pre>"
435
436 output += "<hr /><em><a href='..'>Back to overview</a></em>"
437 return output
438
439
440def generate_header(datadump, ctag="#"):
441 return """\
442%(ctag)s
443%(ctag)s DO NOT EDIT - Automatically generated by 'gformat'
444%(ctag)s Generated at %(date)s by %(host)s from r%(revision)s
445%(ctag)s
446""" % { 'ctag' : ctag, 'date' : time.ctime(), 'host' : socket.gethostname(), 'revision' : datadump['autogen_revision'] }
447
448
449
450def parseaddr(s):
451 """ Process IPv4 CIDR notation addr to a (binary) number """
452 f = s.split('.')
453 return (long(f[0]) << 24L) + \
454 (long(f[1]) << 16L) + \
455 (long(f[2]) << 8L) + \
456 long(f[3])
457
458
459
460def showaddr(a):
461 """ Display IPv4 addr in (dotted) CIDR notation """
462 return "%d.%d.%d.%d" % ((a >> 24) & 0xff, (a >> 16) & 0xff, (a >> 8) & 0xff, a & 0xff)
463
464
465def is_member(ip, mask, canidate):
466 """ Return True if canidate is part of ip/mask block"""
467 ip_addr = parseaddr(ip)
468 ip_canidate = parseaddr(canidate)
469 mask = int(mask)
470 ip_addr = ip_addr & ~((1 << (32 - mask)) - 1)
471 ip_canidate = ip_canidate & ~((1 << (32 - mask)) - 1)
472 return ip_addr == ip_canidate
473
474
475
476def cidr2netmask(netmask):
477 """ Given a 'netmask' return corresponding CIDR """
478 return showaddr(0xffffffff & (0xffffffff << (32 - int(netmask))))
479
480def get_network(addr, mask):
481 return showaddr(parseaddr(addr) & ~((1 << (32 - int(mask))) - 1))
482
483
484def generate_dhcpd_conf(datadump):
485 """ Generate config file '/usr/local/etc/dhcpd.conf """
486 output = generate_header(datadump)
487 output += Template("""\
488# option definitions common to all supported networks...
489option domain-name "dhcp.{{ autogen_fqdn }}";
490
491default-lease-time 600;
492max-lease-time 7200;
493
494# Use this to enble / disable dynamic dns updates globally.
495#ddns-update-style none;
496
497# If this DHCP server is the official DHCP server for the local
498# network, the authoritative directive should be uncommented.
499authoritative;
500
501# Use this to send dhcp log messages to a different log file (you also
502# have to hack syslog.conf to complete the redirection).
503log-facility local7;
504
505#
506# Interface definitions
507#
508\n""").render(datadump)
509
510 dhcp_out = defaultdict(list)
511 for iface_key in datadump['autogen_iface_keys']:
512 ifname = datadump[iface_key]['autogen_ifname']
513 if not datadump[iface_key].has_key('comment'):
514 datadump[iface_key]['comment'] = None
515 dhcp_out[ifname].append(" ## %(autogen_ifname)s - %(comment)s\n" % datadump[iface_key])
516
517 (addr, mask) = datadump[iface_key]['ip'].split('/')
518 datadump[iface_key]['autogen_addr'] = addr
519 datadump[iface_key]['autogen_netmask'] = cidr2netmask(mask)
520 datadump[iface_key]['autogen_subnet'] = get_network(addr, mask)
521 if dhcp_type(datadump[iface_key]) != DHCP_SERVER:
522 dhcp_out[ifname].append(" subnet %(autogen_subnet)s netmask %(autogen_netmask)s {\n ### not autoritive\n }\n" % \
523 datadump[iface_key])
524 continue
525
526 (dhcp_start, dhcp_stop) = datadump[iface_key]['dhcp'].split('-')
527 dhcp_part = ".".join(addr.split('.')[0:3])
528 datadump[iface_key]['autogen_dhcp_start'] = dhcp_part + "." + dhcp_start
529 datadump[iface_key]['autogen_dhcp_stop'] = dhcp_part + "." + dhcp_stop
530
531 # Assume the first 10 IPs could be used for static entries
532 if 'no_portal' in datadump:
533 fixed = 5
534 for mac in datadump['no_portal']:
535 dhcp_out[ifname].append("""\
536 host fixed-%(ifname)s-%(fixed)s {
537 hardware ethernet %(mac)s;
538 fixed-address %(prefix)s.%(fixed)s;
539 }
540""" % { 'ifname' : ifname, 'mac' : mac, 'prefix': dhcp_part, 'fixed' : fixed })
541 fixed += 1
542
543 dhcp_out[ifname].append("""\
544 subnet %(autogen_subnet)s netmask %(autogen_netmask)s {
545 range %(autogen_dhcp_start)s %(autogen_dhcp_stop)s;
546 option routers %(autogen_addr)s;
547 option domain-name-servers %(autogen_addr)s;
548 }
549""" % datadump[iface_key])
550
551 for ifname,value in dhcp_out.iteritems():
552 output += ("shared-network %s {\n" % ifname) + ''.join(value) + '}\n\n'
553 return output
554
555
556
557def generate_dnsmasq_conf(datadump):
558 """ Generate configuration file '/usr/local/etc/dnsmasq.conf' """
559 output = generate_header(datadump)
560 output += Template("""\
561# DHCP server options
562dhcp-authoritative
563dhcp-fqdn
564domain=dhcp.{{ autogen_fqdn }}
565domain-needed
566expand-hosts
567log-async=100
568
569# Low memory footprint
570cache-size=10000
571
572\n""").render(datadump)
573
574 for iface_key in datadump['autogen_iface_keys']:
575 if not datadump[iface_key].has_key('comment'):
576 datadump[iface_key]['comment'] = None
577 output += "## %(autogen_ifname)s - %(comment)s\n" % datadump[iface_key]
578
579 if dhcp_type(datadump[iface_key]) != DHCP_SERVER:
580 output += "# not autoritive\n\n"
581 continue
582
583 (dhcp_start, dhcp_stop) = datadump[iface_key]['dhcp'].split('-')
584 (ip, cidr) = datadump[iface_key]['ip'].split('/')
585 datadump[iface_key]['autogen_netmask'] = cidr2netmask(cidr)
586
587 dhcp_part = ".".join(ip.split('.')[0:3])
588 datadump[iface_key]['autogen_dhcp_start'] = dhcp_part + "." + dhcp_start
589 datadump[iface_key]['autogen_dhcp_stop'] = dhcp_part + "." + dhcp_stop
590 output += "dhcp-range=%(autogen_ifname)s,%(autogen_dhcp_start)s,%(autogen_dhcp_stop)s,%(autogen_netmask)s,24h\n\n" % datadump[iface_key]
591
592 return output
593
594
595def make_interface_list(datadump):
596 if interface_list_cache.has_key(datadump['autogen_item']):
597 return (interface_list_cache[datadump['autogen_item']])
598 # lo0 configuration:
599 # - 172.32.255.1/32 is the proxy.wleiden.net deflector
600 # - masterip is special as it needs to be assigned to at
601 # least one interface, so if not used assign to lo0
602 addrs_list = { 'lo0' : [("127.0.0.1/8", "LocalHost"), ("172.31.255.1/32","Proxy IP")] }
603 dhclient_if = {'lo0' : False}
604
605 # XXX: Find some way of send this output nicely
606 output = ''
607
608 masterip_used = False
609 for iface_key in datadump['autogen_iface_keys']:
610 if datadump[iface_key]['ip'].startswith(datadump['masterip']):
611 masterip_used = True
612 break
613 if not masterip_used:
614 addrs_list['lo0'].append((datadump['masterip'] + "/32", 'Master IP Not used in interface'))
615
616 for iface_key in datadump['autogen_iface_keys']:
617 ifacedump = datadump[iface_key]
618 ifname = ifacedump['autogen_ifname']
619
620 # Flag dhclient is possible
621 if not dhclient_if.has_key(ifname) or dhclient_if[ifname] == False:
622 dhclient_if[ifname] = dhcp_type(ifacedump) == DHCP_CLIENT
623
624 # Add interface IP to list
625 item = (ifacedump['ip'], ifacedump['comment'])
626 if addrs_list.has_key(ifname):
627 addrs_list[ifname].append(item)
628 else:
629 addrs_list[ifname] = [item]
630
631 # Alias only needs IP assignment for now, this might change if we
632 # are going to use virtual accesspoints
633 if "alias" in iface_key:
634 continue
635
636 # XXX: Might want to deduct type directly from interface name
637 if ifacedump['type'] in ['11a', '11b', '11g', 'wireless']:
638 # Default to station (client) mode
639 ifacedump['autogen_wlanmode'] = "sta"
640 if ifacedump['mode'] in ['master', 'master-wds', 'ap', 'ap-wds']:
641 ifacedump['autogen_wlanmode'] = "ap"
642
643 if not ifacedump.has_key('channel'):
644 if ifacedump['type'] == '11a':
645 ifacedump['channel'] = 36
646 else:
647 ifacedump['channel'] = 1
648
649 # Allow special hacks at the back like wds and stuff
650 if not ifacedump.has_key('extra'):
651 ifacedump['autogen_extra'] = 'regdomain ETSI country NL'
652 else:
653 ifacedump['autogen_extra'] = ifacedump['extra']
654
655 output += "wlans_%(autogen_ifbase)s='%(autogen_ifname)s'\n" % ifacedump
656 output += ("create_args_%(autogen_ifname)s='wlanmode %(autogen_wlanmode)s mode " +\
657 "%(type)s ssid %(ssid)s %(autogen_extra)s channel %(channel)s'\n") % ifacedump
658 output += "\n"
659
660 elif ifacedump['type'] in ['ethernet', 'eth']:
661 # No special config needed besides IP
662 if ifacedump['autogen_bridge']:
663 output += "cloned_interfaces='%(autogen_ifname)s'\n" % ifacedump
664 output += "ifconfig_%s='addm %s up'\n" % (ifacedump['autogen_ifname'], ' addm '.join(ifacedump['autogen_bridge_interfaces']))
665 for member in ifacedump['autogen_bridge_interfaces']:
666 output += "ifconfig_%s='up'\n" % member
667 output += "\n"
668 else:
669 assert False, "Unknown type " + ifacedump['type']
670
671 store = (addrs_list, dhclient_if, output)
672 interface_list_cache[datadump['autogen_item']] = store
673 return(store)
674
675
676
677def generate_rc_conf_local(datadump):
678 """ Generate configuration file '/etc/rc.conf.local' """
679 item = datadump['autogen_item']
680 if rc_conf_local_cache.has_key(item):
681 return rc_conf_local_cache[item]
682
683 if not datadump.has_key('ileiden'):
684 datadump['autogen_ileiden_enable'] = False
685 else:
686 datadump['autogen_ileiden_enable'] = datadump['ileiden']
687
688 datadump['autogen_ileiden_enable'] = switchFormat(datadump['autogen_ileiden_enable'])
689
690 if not ileiden_proxies or not normal_proxies:
691 for proxy in get_proxylist():
692 proxydump = get_yaml(proxy)
693 if proxydump['ileiden']:
694 ileiden_proxies.append(proxydump)
695 else:
696 normal_proxies.append(proxydump)
697 for host in get_hybridlist():
698 hostdump = get_yaml(host)
699 if hostdump['service_proxy_ileiden']:
700 ileiden_proxies.append(hostdump)
701 if hostdump['service_proxy_normal']:
702 normal_proxies.append(hostdump)
703
704 datadump['autogen_ileiden_proxies'] = ileiden_proxies
705 datadump['autogen_normal_proxies'] = normal_proxies
706 datadump['autogen_ileiden_proxies_ips'] = ','.join([x['masterip'] for x in ileiden_proxies])
707 datadump['autogen_ileiden_proxies_names'] = ','.join([x['autogen_item'] for x in ileiden_proxies])
708 datadump['autogen_normal_proxies_ips'] = ','.join([x['masterip'] for x in normal_proxies])
709 datadump['autogen_normal_proxies_names'] = ','.join([x['autogen_item'] for x in normal_proxies])
710
711 output = generate_header(datadump, "#");
712 output += render_template(datadump, """\
713hostname='{{ autogen_fqdn }}'
714location='{{ location }}'
715nodetype="{{ nodetype }}"
716
717#
718# Configured listings
719#
720captive_portal_whitelist=""
721{% if nodetype == "Proxy" %}
722#
723# Proxy Configuration
724#
725{% if gateway -%}
726defaultrouter="{{ gateway }}"
727{% else -%}
728#defaultrouter="NOTSET"
729{% endif -%}
730internalif="{{ internalif }}"
731ileiden_enable="{{ autogen_ileiden_enable }}"
732gateway_enable="{{ autogen_ileiden_enable }}"
733pf_enable="yes"
734pf_rules="/etc/pf.conf"
735{% if autogen_ileiden_enable -%}
736pf_flags="-D ext_if={{ externalif }} -D int_if={{ internalif }} -D publicnat={80,443}"
737lvrouted_enable="{{ autogen_ileiden_enable }}"
738lvrouted_flags="-u -s s00p3rs3kr3t -m 28"
739{% else -%}
740pf_flags="-D ext_if={{ externalif }} -D int_if={{ internalif }} -D publicnat={0}"
741{% endif -%}
742{% if internalroute -%}
743static_routes="wleiden"
744route_wleiden="-net 172.16.0.0/12 {{ internalroute }}"
745{% endif -%}
746
747{% elif nodetype == "Hybrid" %}
748 #
749 # Hybrid Configuration
750 #
751 list_ileiden_proxies="
752 {% for item in autogen_ileiden_proxies -%}
753 {{ "%-16s"|format(item.masterip) }} # {{ item.autogen_realname }}
754 {% endfor -%}
755 "
756 list_normal_proxies="
757 {% for item in autogen_normal_proxies -%}
758 {{ "%-16s"|format(item.masterip) }} # {{ item.autogen_realname }}
759 {% endfor -%}
760 "
761
762 captive_portal_interfaces="{{ autogen_dhcp_interfaces|join(',')|default('none', true) }}"
763 externalif="{{ externalif|default('vr0', true) }}"
764 masterip="{{ masterip }}"
765
766 # Defined services
767 service_proxy_ileiden="{{ service_proxy_ileiden|yesorno }}"
768 service_proxy_normal="{{ service_proxy_normal|yesorno }}"
769 service_accesspoint="{{ service_accesspoint|yesorno }}"
770 service_incoming_rdr="{{ service_incoming_rdr|yesorno }}"
771 service_concentrator="{{ service_concentrator|yesorno }}"
772 #
773
774 {% if service_proxy_ileiden %}
775 pf_rules="/etc/pf.hybrid.conf"
776 {% if service_concentrator %}
777 pf_flags="-D ext_if=$externalif -D ext_if_net=$externalif:network -D inet_if=tun0 -D inet_ip='(tun0)' -D masterip=$masterip"
778 {% else %}
779 pf_flags="-D ext_if=$externalif -D ext_if_net=$externalif:network -D inet_if=$externalif -D inet_ip='($externalif:0)' -D masterip=$masterip"
780 {% endif %}
781 pf_flags="$pf_flags -D publicnat=80,443"
782 lvrouted_flags="$lvrouted_flags -g"
783 {% elif service_proxy_normal or service_incoming_rdr %}
784 pf_rules="/etc/pf.hybrid.conf"
785 pf_flags="-D ext_if=$externalif -D ext_if_net=$externalif:network -D masterip=$masterip"
786 pf_flags="$pf_flags -D publicnat=0"
787 lvrouted_flags="$lvrouted_flags -z `make_list "$list_ileiden_proxies" ","`"
788 named_setfib="1"
789 tinyproxy_setfib="1"
790 dnsmasq_setfib="1"
791 sshd_setfib="1"
792 {% else %}
793 named_auto_forward_only="YES"
794 pf_rules="/etc/pf.node.conf"
795 pf_flags=""
796 lvrouted_flags="$lvrouted_flags -z `make_list "$list_ileiden_proxies" ","`"
797 {% endif %}
798 {% if service_concentrator %}
799 # Do mind installing certificates is NOT done automatically for security reasons
800 openvpn_enable="YES"
801 openvpn_configfile="/usr/local/etc/openvpn/client.conf"
802 {% endif %}
803
804 {% if service_proxy_normal %}
805 tinyproxy_enable="yes"
806 {% else %}
807 pen_wrapper_enable="yes"
808 {% endif %}
809
810 {% if service_accesspoint %}
811 pf_flags="$pf_flags -D captive_portal_interfaces=$captive_portal_interfaces"
812 {% endif %}
813
814 {% if board == "ALIX2" %}
815 #
816 # ''Fat'' configuration, board has 256MB RAM
817 #
818 dnsmasq_enable="NO"
819 named_enable="YES"
820 {% if autogen_dhcp_interfaces -%}
821 dhcpd_enable="YES"
822 dhcpd_flags="$dhcpd_flags {{ autogen_dhcp_interfaces|join(' ') }}"
823 {% endif -%}
824 {% endif -%}
825
826 {% if gateway %}
827 defaultrouter="{{ gateway }}"
828 {% endif %}
829{% elif nodetype == "CNode" %}
830#
831# NODE iLeiden Configuration
832#
833
834# iLeiden Proxies {{ autogen_ileiden_proxies_names }}
835list_ileiden_proxies="{{ autogen_ileiden_proxies_ips }}"
836# normal Proxies {{ autogen_normal_proxies_names }}
837list_normal_proxies="{{ autogen_normal_proxies_ips }}"
838
839captive_portal_interfaces="{{ autogen_dhcp_interfaces|join(',') }}"
840
841lvrouted_flags="-u -s s00p3rs3kr3t -m 28 -z $list_ileiden_proxies"
842{% endif %}
843
844#
845# Interface definitions
846#\n
847""")
848
849 (addrs_list, dhclient_if, extra_ouput) = make_interface_list(datadump)
850 output += extra_ouput.strip() + "\n"
851
852 # Print IP address which needs to be assigned over here
853 output += "\n"
854 for iface,addrs in sorted(addrs_list.iteritems()):
855 for addr, comment in sorted(addrs,key=lambda x: parseaddr(x[0].split('/')[0])):
856 output += "# %s || %s || %s\n" % (iface, addr, comment)
857
858 # Write DHCLIENT entry
859 if dhclient_if[iface]:
860 output += "ifconfig_%s='SYNCDHCP'\n\n" % (iface)
861
862 # Make sure the external address is always first as this is needed in the
863 # firewall setup
864 addrs = sorted(
865 [x for x in addrs if not '0.0.0.0' in x[0]],
866 key=lambda x: x[0].split('.')[0],
867 cmp=lambda x,y: cmp(1 if x == '172' else 0, 1 if y == '172' else 0)
868 )
869 addr_str = " ".join([x[0] for x in addrs])
870 output += "ipv4_addrs_%s='%s'\n\n" % (iface, addr_str)
871
872 rc_conf_local_cache[datadump['autogen_item']] = output
873 return output
874
875
876
877
878def get_all_configs():
879 """ Get dict with key 'host' with all configs present """
880 configs = dict()
881 for host in get_hostlist():
882 datadump = get_yaml(host)
883 configs[host] = datadump
884 return configs
885
886
887def get_interface_keys(config):
888 """ Quick hack to get all interface keys, later stage convert this to a iterator """
889 return sorted([elem for elem in config.keys() if (elem.startswith('iface_') and not "lo0" in elem)])
890
891
892def get_used_ips(configs):
893 """ Return array of all IPs used in config files"""
894 ip_list = []
895 for config in configs:
896 ip_list.append(config['masterip'])
897 for iface_key in get_interface_keys(config):
898 l = config[iface_key]['ip']
899 addr, mask = l.split('/')
900 # Special case do not process
901 if valid_addr(addr):
902 ip_list.append(addr)
903 else:
904 logger.error("## IP '%s' in '%s' not valid" % (addr, config['nodename']))
905 return sorted(ip_list)
906
907
908
909def get_nameservers(max_servers=None):
910 if nameservers_cache:
911 return nameservers_cache[0:max_servers]
912
913 for host in get_hybridlist():
914 hostdump = get_yaml(host)
915 if hostdump['status'] == 'up' and (hostdump['service_proxy_ileiden'] or hostdump['service_proxy_normal']):
916 nameservers_cache.append((hostdump['masterip'], hostdump['autogen_realname']))
917 for host in get_proxylist():
918 hostdump = get_yaml(host)
919 if hostdump['status'] == 'up':
920 nameservers_cache.append((hostdump['masterip'], hostdump['autogen_realname']))
921
922 return nameservers_cache[0:max_servers]
923
924
925def generate_resolv_conf(datadump):
926 """ Generate configuration file '/etc/resolv.conf' """
927 # XXX: This should properly going to be an datastructure soon
928 datadump['autogen_header'] = generate_header(datadump, "#")
929 datadump['autogen_edge_nameservers'] = ''
930
931
932 for masterip,realname in get_nameservers():
933 datadump['autogen_edge_nameservers'] += "nameserver %-15s # %s\n" % (masterip, realname)
934
935 return Template("""\
936{{ autogen_header }}
937search wleiden.net
938
939# Try local (cache) first
940nameserver 127.0.0.1
941
942{% if service_proxy_normal or service_proxy_ileiden or nodetype == 'Proxy' -%}
943nameserver 8.8.8.8 # Google Public NameServer
944nameserver 8.8.4.4 # Google Public NameServer
945{% else -%}
946# START DYNAMIC LIST - updated by /tools/nameserver-shuffle
947{{ autogen_edge_nameservers }}
948{% endif -%}
949""").render(datadump)
950
951
952
953def generate_ntp_conf(datadump):
954 """ Generate configuration file '/etc/ntp.conf' """
955 # XXX: This should properly going to be an datastructure soon
956
957 datadump['autogen_header'] = generate_header(datadump, "#")
958 datadump['autogen_ntp_servers'] = ''
959 for host in get_proxylist():
960 hostdump = get_yaml(host)
961 datadump['autogen_ntp_servers'] += "server %(masterip)-15s iburst maxpoll 9 # %(autogen_realname)s\n" % hostdump
962 for host in get_hybridlist():
963 hostdump = get_yaml(host)
964 if hostdump['service_proxy_ileiden'] or hostdump['service_proxy_normal']:
965 datadump['autogen_ntp_servers'] += "server %(masterip)-15s iburst maxpoll 9 # %(autogen_realname)s\n" % hostdump
966
967 return Template("""\
968{{ autogen_header }}
969
970{% if service_proxy_normal or service_proxy_ileiden or nodetype == 'Proxy' -%}
971# Machine hooked to internet.
972server 0.nl.pool.ntp.org iburst maxpoll 9
973server 1.nl.pool.ntp.org iburst maxpoll 9
974server 2.nl.pool.ntp.org iburst maxpoll 9
975server 3.nl.pool.ntp.org iburst maxpoll 9
976{% else -%}
977# Local Wireless Leiden NTP Servers.
978server 0.pool.ntp.wleiden.net iburst maxpoll 9
979server 1.pool.ntp.wleiden.net iburst maxpoll 9
980server 2.pool.ntp.wleiden.net iburst maxpoll 9
981server 3.pool.ntp.wleiden.net iburst maxpoll 9
982
983# All the configured NTP servers
984{{ autogen_ntp_servers }}
985{% endif %}
986
987# If a server loses sync with all upstream servers, NTP clients
988# no longer follow that server. The local clock can be configured
989# to provide a time source when this happens, but it should usually
990# be configured on just one server on a network. For more details see
991# http://support.ntp.org/bin/view/Support/UndisciplinedLocalClock
992# The use of Orphan Mode may be preferable.
993#
994server 127.127.1.0
995fudge 127.127.1.0 stratum 10
996""").render(datadump)
997
998
999def generate_pf_hybrid_conf_local(datadump):
1000 """ Generate configuration file '/etc/pf.hybrid.conf.local' """
1001 datadump['autogen_header'] = generate_header(datadump, "#")
1002 return Template("""\
1003{{ autogen_header }}
1004
1005# Redirect some internal facing services outside (7)
1006# INFO: {{ rdr_rules|count }} rdr_rules (outside to internal redirect rules) defined.
1007{% for protocol, src_port,dest_ip,dest_port in rdr_rules -%}
1008rdr on $ext_if inet proto {{ protocol }} from any to $ext_if port {{ src_port }} tag SRV -> {{ dest_ip }} port {{ dest_port }}
1009{% endfor -%}
1010""").render(datadump)
1011
1012def generate_motd(datadump):
1013 """ Generate configuration file '/etc/motd' """
1014 output = Template("""\
1015FreeBSD run ``service motd onestart'' to make me look normal
1016
1017 WWW: {{ autogen_fqdn }} - http://www.wirelessleiden.nl
1018 Loc: {{ location }}
1019
1020Services:
1021{% if board == "ALIX2" -%}
1022{{" -"}} Core Node ({{ board }})
1023{% else -%}
1024{{" -"}} Hulp Node ({{ board }})
1025{% endif -%}
1026{% if service_proxy_normal -%}
1027{{" -"}} Normal Proxy
1028{% endif -%}
1029{% if service_proxy_ileiden -%}
1030{{" -"}} iLeiden Proxy
1031{% endif -%}
1032{% if service_incoming_rdr -%}
1033{{" -"}} Incoming port redirects
1034{% endif %}
1035Interlinks:\n
1036""").render(datadump)
1037
1038 (addrs_list, dhclient_if, extra_ouput) = make_interface_list(datadump)
1039 # Just nasty hack to make the formatting looks nice
1040 iface_len = max(map(len,addrs_list.keys()))
1041 addr_len = max(map(len,[x[0] for x in [x[0] for x in addrs_list.values()]]))
1042 for iface,addrs in sorted(addrs_list.iteritems()):
1043 if iface in ['lo0']:
1044 continue
1045 for addr, comment in sorted(addrs,key=lambda x: parseaddr(x[0].split('/')[0])):
1046 output += " - %s || %s || %s\n" % (iface.ljust(iface_len), addr.ljust(addr_len), comment)
1047
1048 output += '\n'
1049 output += """\
1050Attached bridges:
1051"""
1052 has_item = False
1053 for iface_key in datadump['autogen_iface_keys']:
1054 ifacedump = datadump[iface_key]
1055 if ifacedump.has_key('ns_ip'):
1056 ifacedump['autogen_ns_ip'] = ifacedump['ns_ip'].split('/')[0]
1057 has_item = True
1058 output += " - %(autogen_ifname)s || %(mode)s || https://%(autogen_ns_ip)s\n" % ifacedump
1059 if not has_item:
1060 output += " - none\n"
1061
1062 return output
1063
1064
1065def format_yaml_value(value):
1066 """ Get yaml value in right syntax for outputting """
1067 if isinstance(value,str):
1068 output = '"%s"' % value
1069 else:
1070 output = value
1071 return output
1072
1073
1074
1075def format_wleiden_yaml(datadump):
1076 """ Special formatting to ensure it is editable"""
1077 output = "# Genesis config yaml style\n"
1078 output += "# vim:ts=2:et:sw=2:ai\n"
1079 output += "#\n"
1080 iface_keys = [elem for elem in datadump.keys() if elem.startswith('iface_')]
1081 for key in sorted(set(datadump.keys()) - set(iface_keys)):
1082 if key == 'rdr_rules':
1083 output += '%-10s:\n' % 'rdr_rules'
1084 for rdr_rule in datadump[key]:
1085 output += '- %s\n' % rdr_rule
1086 else:
1087 output += "%-10s: %s\n" % (key, format_yaml_value(datadump[key]))
1088
1089 output += "\n\n"
1090
1091 # Format (key, required)
1092 key_order = (
1093 ('comment', True),
1094 ('ip', True),
1095 ('desc', True),
1096 ('sdesc', True),
1097 ('mode', True),
1098 ('type', True),
1099 ('extra_type', False),
1100 ('channel', False),
1101 ('ssid', False),
1102 ('wlan_mac', False),
1103 ('dhcp', True),
1104 ('compass', False),
1105 ('distance', False),
1106 ('ns_ip', False),
1107 ('bullet2_ip', False),
1108 ('ns_mac', False),
1109 ('bullet2_mac', False),
1110 ('ns_type', False),
1111 ('bridge_type', False),
1112 ('members', True),
1113 ('status', True),
1114 )
1115
1116 for iface_key in sorted(iface_keys):
1117 try:
1118 remainder = set(datadump[iface_key].keys()) - set([x[0] for x in key_order])
1119 if remainder:
1120 raise KeyError("invalid keys: %s" % remainder)
1121
1122 output += "%s:\n" % iface_key
1123 for key,required in key_order:
1124 if datadump[iface_key].has_key(key):
1125 output += " %-11s: %s\n" % (key, format_yaml_value(datadump[iface_key][key]))
1126 output += "\n\n"
1127 except Exception as e:
1128 print "# Error while processing interface %s" % iface_key
1129 raise
1130
1131 return output
1132
1133
1134
1135def generate_wleiden_yaml(datadump, header=True):
1136 """ Generate (petty) version of wleiden.yaml"""
1137 output = generate_header(datadump, "#") if header else ''
1138
1139 for key in datadump.keys():
1140 if key.startswith('autogen_'):
1141 del datadump[key]
1142 # Interface autogen cleanups
1143 elif type(datadump[key]) == dict:
1144 for key2 in datadump[key].keys():
1145 if key2.startswith('autogen_'):
1146 del datadump[key][key2]
1147
1148 output += format_wleiden_yaml(datadump)
1149 return output
1150
1151def generate_nanostation_config(datadump, iface, ns_type):
1152 #TODO(rvdz): Make sure the proper nanostation IP and subnet is set
1153 datadump['iface_%s' % iface]['ns_ip'] = datadump['iface_%s' % iface]['ns_ip'].split('/')[0]
1154
1155 datadump.update(datadump['iface_%s' % iface])
1156
1157 return open(os.path.join(os.path.dirname(__file__), 'ns5m.cfg.tmpl'),'r').read() % datadump
1158
1159def generate_yaml(datadump):
1160 return generate_config(datadump['nodename'], "wleiden.yaml", datadump)
1161
1162
1163
1164def generate_config(node, config, datadump=None):
1165 """ Print configuration file 'config' of 'node' """
1166 output = ""
1167 try:
1168 # Load config file
1169 if datadump == None:
1170 datadump = get_yaml(node)
1171
1172 if config == 'wleiden.yaml':
1173 output += generate_wleiden_yaml(datadump)
1174 elif config == 'authorized_keys':
1175 f = open(os.path.join(NODE_DIR,"global_keys"), 'r')
1176 output += f.read()
1177 node_keys = os.path.join(NODE_DIR,node,'authorized_keys')
1178 # Fetch local keys if existing
1179 if os.path.exists(node_keys):
1180 output += open(node_keys, 'r').read()
1181 f.close()
1182 elif config == 'dnsmasq.conf':
1183 output += generate_dnsmasq_conf(datadump)
1184 elif config == 'dhcpd.conf':
1185 output += generate_dhcpd_conf(datadump)
1186 elif config == 'rc.conf.local':
1187 output += generate_rc_conf_local(datadump)
1188 elif config == 'resolv.conf':
1189 output += generate_resolv_conf(datadump)
1190 elif config == 'ntp.conf':
1191 output += generate_ntp_conf(datadump)
1192 elif config == 'motd':
1193 output += generate_motd(datadump)
1194 elif config == 'pf.hybrid.conf.local':
1195 output += generate_pf_hybrid_conf_local(datadump)
1196 elif config.startswith('vr'):
1197 interface, ns_type = config.strip('.yaml').split('-')
1198 output += generate_nanostation_config(datadump, interface, ns_type)
1199 else:
1200 assert False, "Config not found!"
1201 except IOError, e:
1202 output += "[ERROR] Config file not found"
1203 return output
1204
1205
1206
1207def process_cgi_request(environ=os.environ):
1208 """ When calling from CGI """
1209 response_headers = []
1210 content_type = 'text/plain'
1211
1212 # Update repository if requested
1213 form = urlparse.parse_qs(environ['QUERY_STRING']) if environ.has_key('QUERY_STRING') else None
1214 if form and form.has_key("action") and "update" in form["action"]:
1215 output = "[INFO] Updating subverion, please wait...\n"
1216 output += subprocess.Popen([SVN, 'cleanup', "%s/.." % NODE_DIR], stderr=subprocess.STDOUT, stdout=subprocess.PIPE).communicate()[0]
1217 output += subprocess.Popen([SVN, 'up', "%s/.." % NODE_DIR], stderr=subprocess.STDOUT, stdout=subprocess.PIPE).communicate()[0]
1218 output += "[INFO] All done, redirecting in 5 seconds"
1219 response_headers += [
1220 ('Refresh', '5; url=.'),
1221 ]
1222 reload_cache()
1223 else:
1224 base_uri = environ['PATH_INFO']
1225 uri = base_uri.strip('/').split('/')
1226
1227 output = "Template Holder"
1228 if base_uri.endswith('/create/network.kml'):
1229 content_type='application/vnd.google-earth.kml+xml'
1230 output = make_network_kml.make_graph()
1231 elif base_uri.endswith('/api/get/nodeplanner.json'):
1232 content_type='application/json'
1233 output = make_network_kml.make_nodeplanner_json()
1234 elif not uri[0]:
1235 if is_text_request(environ):
1236 output = '\n'.join(get_hostlist())
1237 else:
1238 content_type = 'text/html'
1239 output = generate_title(get_hostlist())
1240 elif len(uri) == 1:
1241 if is_text_request(environ):
1242 output = generate_node(uri[0])
1243 else:
1244 content_type = 'text/html'
1245 output = generate_node_overview(uri[0])
1246 elif len(uri) == 2:
1247 output = generate_config(uri[0], uri[1])
1248 else:
1249 assert False, "Invalid option"
1250
1251 # Return response
1252 response_headers += [
1253 ('Content-type', content_type),
1254 ('Content-Length', str(len(output))),
1255 ]
1256 return(response_headers, str(output))
1257
1258
1259def get_realname(datadump):
1260 # Proxy naming convention is special, as the proxy name is also included in
1261 # the nodename, when it comes to the numbered proxies.
1262 if datadump['nodetype'] == 'Proxy':
1263 realname = datadump['nodetype'] + datadump['nodename'].replace('proxy','')
1264 else:
1265 # By default the full name is listed and also a shortname CNAME for easy use.
1266 realname = datadump['nodetype'] + datadump['nodename']
1267 return(realname)
1268
1269
1270
1271def make_dns(output_dir = 'dns', external = False):
1272 items = dict()
1273
1274 # hostname is key, IP is value
1275 wleiden_zone = defaultdict(list)
1276 wleiden_cname = dict()
1277
1278 pool = dict()
1279 for node in get_hostlist():
1280 datadump = get_yaml(node)
1281
1282 # Proxy naming convention is special
1283 fqdn = datadump['autogen_realname']
1284 if datadump['nodetype'] in ['CNode', 'Hybrid']:
1285 wleiden_cname[datadump['nodename']] = fqdn
1286
1287 if datadump.has_key('rdr_host'):
1288 remote_target = datadump['rdr_host']
1289 elif datadump.has_key('remote_access') and datadump['remote_access']:
1290 remote_target = datadump['remote_access'].split(':')[0]
1291 else:
1292 remote_target = None
1293
1294 if remote_target:
1295 try:
1296 parseaddr(remote_target)
1297 wleiden_zone[datadump['nodename'] + '.gw'].append((remote_target, False))
1298 except (IndexError, ValueError):
1299 wleiden_cname[datadump['nodename'] + '.gw'] = remote_target + '.'
1300
1301
1302 wleiden_zone[fqdn].append((datadump['masterip'], True))
1303
1304 # Hacking to get proper DHCP IPs and hostnames
1305 for iface_key in get_interface_keys(datadump):
1306 iface_name = iface_key.replace('_','-')
1307 (ip, cidr) = datadump[iface_key]['ip'].split('/')
1308 try:
1309 (dhcp_start, dhcp_stop) = datadump[iface_key]['dhcp'].split('-')
1310 datadump[iface_key]['autogen_netmask'] = cidr2netmask(cidr)
1311 dhcp_part = ".".join(ip.split('.')[0:3])
1312 if ip != datadump['masterip']:
1313 wleiden_zone["dhcp-gateway-%s.%s" % (iface_name, fqdn)].append((ip, False))
1314 for i in range(int(dhcp_start), int(dhcp_stop) + 1):
1315 wleiden_zone["dhcp-%s-%s.%s" % (i, iface_name, fqdn)].append(("%s.%s" % (dhcp_part, i), True))
1316 except (AttributeError, ValueError, KeyError):
1317 # First push it into a pool, to indentify the counter-part later on
1318 addr = parseaddr(ip)
1319 cidr = int(cidr)
1320 addr = addr & ~((1 << (32 - cidr)) - 1)
1321 if pool.has_key(addr):
1322 pool[addr] += [(iface_name, fqdn, ip)]
1323 else:
1324 pool[addr] = [(iface_name, fqdn, ip)]
1325 continue
1326
1327
1328
1329 # WL uses an /29 to configure an interface. IP's are ordered like this:
1330 # MasterA (.1) -- DeviceA (.2) <<>> DeviceB (.3) --- SlaveB (.4)
1331
1332 sn = lambda x: re.sub(r'(?i)^cnode','',x)
1333
1334 # Automatic naming convention of interlinks namely 2 + remote.lower()
1335 for (key,value) in pool.iteritems():
1336 # Make sure they are sorted from low-ip to high-ip
1337 value = sorted(value, key=lambda x: parseaddr(x[2]))
1338
1339 if len(value) == 1:
1340 (iface_name, fqdn, ip) = value[0]
1341 wleiden_zone["2unused-%s.%s" % (iface_name, fqdn)].append((ip, True))
1342
1343 # Device DNS names
1344 if 'cnode' in fqdn.lower():
1345 wleiden_zone["d-at-%s.%s" % (iface_name, fqdn)].append((showaddr(parseaddr(ip) + 1), False))
1346 wleiden_cname["d-at-%s.%s" % (iface_name,sn(fqdn))] = "d-at-%s.%s" % ((iface_name, fqdn))
1347
1348 elif len(value) == 2:
1349 (a_iface_name, a_fqdn, a_ip) = value[0]
1350 (b_iface_name, b_fqdn, b_ip) = value[1]
1351 wleiden_zone["2%s.%s" % (b_fqdn,a_fqdn)].append((a_ip, True))
1352 wleiden_zone["2%s.%s" % (a_fqdn,b_fqdn)].append((b_ip, True))
1353
1354 # Device DNS names
1355 if 'cnode' in a_fqdn.lower() and 'cnode' in b_fqdn.lower():
1356 wleiden_zone["d-at-%s.%s" % (a_iface_name, a_fqdn)].append((showaddr(parseaddr(a_ip) + 1), False))
1357 wleiden_zone["d-at-%s.%s" % (b_iface_name, b_fqdn)].append((showaddr(parseaddr(b_ip) - 1), False))
1358 wleiden_cname["d-at-%s.%s" % (a_iface_name,sn(a_fqdn))] = "d-at-%s.%s" % (a_iface_name, a_fqdn)
1359 wleiden_cname["d-at-%s.%s" % (b_iface_name,sn(b_fqdn))] = "d-at-%s.%s" % (b_iface_name, b_fqdn)
1360 wleiden_cname["d2%s.%s" % (sn(b_fqdn),sn(a_fqdn))] = "d-at-%s.%s" % (a_iface_name, a_fqdn)
1361 wleiden_cname["d2%s.%s" % (sn(a_fqdn),sn(b_fqdn))] = "d-at-%s.%s" % (b_iface_name, b_fqdn)
1362
1363 else:
1364 pool_members = [k[1] for k in value]
1365 for item in value:
1366 (iface_name, fqdn, ip) = item
1367 wleiden_zone["2ring.%s" % (fqdn)].append((ip, True))
1368
1369 # Include static DNS entries
1370 # XXX: Should they override the autogenerated results?
1371 # XXX: Convert input to yaml more useable.
1372 # Format:
1373 ##; this is a comment
1374 ## roomburgh=CNodeRoomburgh1
1375 ## apkerk1.CNodeVosko=172.17.176.8 ;this as well
1376 dns_list = yaml.load(open(os.path.join(NODE_DIR,'../dns/staticDNS.yaml'),'r'))
1377
1378 # Hack to allow special entries, for development
1379 wleiden_raw = {}
1380
1381 for line in dns_list:
1382 reverse = False
1383 k, items = line.items()[0]
1384 if type(items) == dict:
1385 if items.has_key('reverse'):
1386 reverse = items['reverse']
1387 items = items['a']
1388 else:
1389 items = items['cname']
1390 items = [items] if type(items) != list else items
1391 for item in items:
1392 if item.startswith('IN '):
1393 wleiden_raw[k] = item
1394 elif valid_addr(item):
1395 wleiden_zone[k].append((item, reverse))
1396 else:
1397 wleiden_cname[k] = item
1398
1399 # Hack to get dynamic pool listing
1400 def chunks(l, n):
1401 return [l[i:i+n] for i in range(0, len(l), n)]
1402
1403 ntp_servers = [x[0] for x in get_nameservers()]
1404 for id, chunk in enumerate(chunks(ntp_servers,(len(ntp_servers)/4))):
1405 for ntp_server in chunk:
1406 wleiden_zone['%i.pool.ntp' % id].append((ntp_server, False))
1407
1408 details = dict()
1409 # 24 updates a day allowed
1410 details['serial'] = time.strftime('%Y%m%d%H')
1411
1412 if external:
1413 dns_masters = ['siteview.wirelessleiden.nl', 'ns1.vanderzwet.net']
1414 else:
1415 dns_masters = ['sunny.wleiden.net'] + ["%s.wleiden.net" % x[1] for x in get_nameservers(max_servers=3)]
1416
1417 details['master'] = dns_masters[0]
1418 details['ns_servers'] = '\n'.join(['\tNS\t%s.' % x for x in dns_masters])
1419
1420 dns_header = '''
1421$TTL 3h
1422%(zone)s. SOA %(master)s. beheer.lijst.wirelessleiden.nl. ( %(serial)s 15m 15m 1w 60s )
1423 ; Serial, Refresh, Retry, Expire, Neg. cache TTL
1424
1425%(ns_servers)s
1426 \n'''
1427
1428
1429 if not os.path.isdir(output_dir):
1430 os.makedirs(output_dir)
1431 details['zone'] = 'wleiden.net'
1432 f = open(os.path.join(output_dir,"db." + details['zone']), "w")
1433 f.write(dns_header % details)
1434
1435 for host,items in wleiden_zone.iteritems():
1436 for ip,reverse in items:
1437 if ip not in ['0.0.0.0']:
1438 f.write("%s.wleiden.net. IN A %s \n" % (host.lower(), ip))
1439 for source,dest in wleiden_cname.iteritems():
1440 dest = dest if dest.endswith('.') else dest + ".wleiden.net."
1441 f.write("%s.wleiden.net. IN CNAME %s\n" % (source.lower(), dest.lower()))
1442 for source, dest in wleiden_raw.iteritems():
1443 f.write("%s.wleiden.net. %s\n" % (source, dest))
1444 f.close()
1445
1446 # Create whole bunch of specific sub arpa zones. To keep it compliant
1447 for s in range(16,32):
1448 details['zone'] = '%i.172.in-addr.arpa' % s
1449 f = open(os.path.join(output_dir,"db." + details['zone']), "w")
1450 f.write(dns_header % details)
1451
1452 #XXX: Not effient, fix to proper data structure and do checks at other
1453 # stages
1454 for host,items in wleiden_zone.iteritems():
1455 for ip,reverse in items:
1456 if not reverse:
1457 continue
1458 if valid_addr(ip):
1459 if valid_addr(ip):
1460 if int(ip.split('.')[1]) == s:
1461 rev_ip = '.'.join(reversed(ip.split('.')))
1462 f.write("%s.in-addr.arpa. IN PTR %s.wleiden.net.\n" % (rev_ip.lower(), host.lower()))
1463 f.close()
1464
1465
1466def usage():
1467 print """Usage: %(prog)s <argument>
1468Argument:
1469\tstandalone [port] = Run configurator webserver [8000]
1470\tdns [outputdir] = Generate BIND compliant zone files in dns [./dns]
1471\tnagios-export [--heavy-load] = Generate basic nagios configuration file.
1472\tfull-export = Generate yaml export script for heatmap.
1473\tstatic [outputdir] = Generate all config files and store on disk
1474\t with format ./<outputdir>/%%NODE%%/%%FILE%% [./static]
1475\ttest <node> [<file>] = Receive output for certain node [all files].
1476\ttest-cgi <node> <file> = Receive output of CGI script [all files].
1477\tlist <status> <items> = List systems which have certain status
1478
1479Arguments:
1480\t<node> = NodeName (example: HybridRick)
1481\t<file> = %(files)s
1482\t<status> = all|up|down|planned
1483\t<items> = systems|nodes|proxies
1484
1485NOTE FOR DEVELOPERS; you can test your changes like this:
1486 BEFORE any changes in this code:
1487 $ ./gformat.py static /tmp/pre
1488 AFTER the changes:
1489 $ ./gformat.py static /tmp/post
1490 VIEW differences and VERIFY all are OK:
1491 $ diff -urI 'Generated' -r /tmp/pre /tmp/post
1492""" % { 'prog' : sys.argv[0], 'files' : '|'.join(files) }
1493 exit(0)
1494
1495
1496def is_text_request(environ=os.environ):
1497 """ Find out whether we are calling from the CLI or any text based CLI utility """
1498 try:
1499 return environ['HTTP_USER_AGENT'].split()[0] in ['curl', 'fetch', 'wget']
1500 except KeyError:
1501 return True
1502
1503def switchFormat(setting):
1504 if setting:
1505 return "YES"
1506 else:
1507 return "NO"
1508
1509def rlinput(prompt, prefill=''):
1510 import readline
1511 readline.set_startup_hook(lambda: readline.insert_text(prefill))
1512 try:
1513 return raw_input(prompt)
1514 finally:
1515 readline.set_startup_hook()
1516
1517def fix_conflict(left, right, default='i'):
1518 while True:
1519 print "## %-30s | %-30s" % (left, right)
1520 c = raw_input("## Solve Conflict (h for help) <l|r|e|i|> [%s]: " % default)
1521 if not c:
1522 c = default
1523
1524 if c in ['l','1']:
1525 return left
1526 elif c in ['r','2']:
1527 return right
1528 elif c in ['e', '3']:
1529 return rlinput("Edit: ", "%30s | %30s" % (left, right))
1530 elif c in ['i', '4']:
1531 return None
1532 else:
1533 print "#ERROR: '%s' is invalid input (left, right, edit or ignore)!" % c
1534
1535
1536
1537def print_cgi_response(response_headers, output):
1538 """Could we not use some kind of wsgi wrapper to make this output?"""
1539 for header in response_headers:
1540 print "%s: %s" % header
1541 print
1542 print output
1543
1544
1545def fill_cache():
1546 ''' Poor man re-loading of few cache items (the slow ones) '''
1547 for host in get_hostlist():
1548 get_yaml(host)
1549
1550
1551def reload_cache():
1552 clear_cache()
1553 fill_cache()
1554
1555
1556def main():
1557 """Hard working sub"""
1558 # Allow easy hacking using the CLI
1559 if not os.environ.has_key('PATH_INFO'):
1560 if len(sys.argv) < 2:
1561 usage()
1562
1563 if sys.argv[1] == "standalone":
1564 import SocketServer
1565 import CGIHTTPServer
1566 # Hop to the right working directory.
1567 os.chdir(os.path.dirname(__file__))
1568 try:
1569 PORT = int(sys.argv[2])
1570 except (IndexError,ValueError):
1571 PORT = 8000
1572
1573 class MyCGIHTTPRequestHandler(CGIHTTPServer.CGIHTTPRequestHandler):
1574 """ Serve this CGI from the root of the webserver """
1575 def is_cgi(self):
1576 if "favicon" in self.path:
1577 return False
1578
1579 self.cgi_info = (os.path.basename(__file__), self.path)
1580 self.path = ''
1581 return True
1582 handler = MyCGIHTTPRequestHandler
1583 SocketServer.TCPServer.allow_reuse_address = True
1584 httpd = SocketServer.TCPServer(("", PORT), handler)
1585 httpd.server_name = 'localhost'
1586 httpd.server_port = PORT
1587
1588 logger.info("serving at port %s", PORT)
1589 try:
1590 httpd.serve_forever()
1591 except KeyboardInterrupt:
1592 httpd.shutdown()
1593 logger.info("All done goodbye")
1594 elif sys.argv[1] == "test":
1595 # Basic argument validation
1596 try:
1597 node = sys.argv[2]
1598 datadump = get_yaml(node)
1599 except IndexError:
1600 print "Invalid argument"
1601 exit(1)
1602 except IOError as e:
1603 print e
1604 exit(1)
1605
1606
1607 # Get files to generate
1608 gen_files = sys.argv[3:] if len(sys.argv) > 3 else files
1609
1610 # Actual config generation
1611 for config in gen_files:
1612 logger.info("## Generating %s %s", node, config)
1613 print generate_config(node, config, datadump)
1614 elif sys.argv[1] == "test-cgi":
1615 os.environ['PATH_INFO'] = "/".join(sys.argv[2:])
1616 os.environ['SCRIPT_NAME'] = __file__
1617 response_headers, output = process_cgi_request()
1618 print_cgi_response(response_headers, output)
1619 elif sys.argv[1] == "static":
1620 items = dict()
1621 items['output_dir'] = sys.argv[2] if len(sys.argv) > 2 else "./static"
1622 for node in get_hostlist():
1623 items['node'] = node
1624 items['wdir'] = "%(output_dir)s/%(node)s" % items
1625 if not os.path.isdir(items['wdir']):
1626 os.makedirs(items['wdir'])
1627 datadump = get_yaml(node)
1628 for config in files:
1629 items['config'] = config
1630 logger.info("## Generating %(node)s %(config)s" % items)
1631 f = open("%(wdir)s/%(config)s" % items, "w")
1632 f.write(generate_config(node, config, datadump))
1633 f.close()
1634 elif sys.argv[1] == "wind-export":
1635 items = dict()
1636 for node in get_hostlist():
1637 datadump = get_yaml(node)
1638 sql = """INSERT IGNORE INTO nodes (name, name_ns, longitude, latitude)
1639 VALUES ('%(nodename)s', '%(nodename)s', %(latitude)s, %(longitude)s);""" % datadump;
1640 sql = """INSERT IGNORE INTO users_nodes (user_id, node_id, owner)
1641 VALUES (
1642 (SELECT id FROM users WHERE username = 'rvdzwet'),
1643 (SELECT id FROM nodes WHERE name = '%(nodename)s'),
1644 'Y');""" % datadump
1645 #for config in files:
1646 # items['config'] = config
1647 # print "## Generating %(node)s %(config)s" % items
1648 # f = open("%(wdir)s/%(config)s" % items, "w")
1649 # f.write(generate_config(node, config, datadump))
1650 # f.close()
1651 for node in get_hostlist():
1652 datadump = get_yaml(node)
1653 for iface_key in sorted([elem for elem in datadump.keys() if elem.startswith('iface_')]):
1654 ifacedump = datadump[iface_key]
1655 if ifacedump.has_key('mode') and ifacedump['mode'] == 'ap-wds':
1656 ifacedump['nodename'] = datadump['nodename']
1657 if not ifacedump.has_key('channel') or not ifacedump['channel']:
1658 ifacedump['channel'] = 0
1659 sql = """INSERT INTO links (node_id, type, ssid, protocol, channel, status)
1660 VALUES ((SELECT id FROM nodes WHERE name = '%(nodename)s'), 'ap',
1661 '%(ssid)s', 'IEEE 802.11b', %(channel)s, 'active');""" % ifacedump
1662 elif sys.argv[1] == "nagios-export":
1663 try:
1664 heavy_load = (sys.argv[2] == "--heavy-load")
1665 except IndexError:
1666 heavy_load = False
1667
1668 hostgroup_details = {
1669 'wleiden' : 'Stichting Wireless Leiden - FreeBSD Nodes',
1670 'wzoeterwoude' : 'Stichting Wireless Leiden - Afdeling Zoeterwoude - Free-WiFi Project',
1671 'walphen' : 'Stichting Wireless Alphen',
1672 'westeinder' : 'WestEinder Plassen',
1673 }
1674
1675 params = {
1676 'check_interval' : 5 if heavy_load else 120,
1677 'retry_interval' : 1 if heavy_load else 10,
1678 'max_check_attempts' : 10 if heavy_load else 6,
1679 }
1680
1681 print '''\
1682define host {
1683 name wleiden-node ; Default Node Template
1684 use generic-host ; Use the standard template as initial starting point
1685 check_period 24x7 ; By default, FreeBSD hosts are checked round the clock
1686 check_interval %(check_interval)s ; Actively check the host every 5 minutes
1687 retry_interval %(retry_interval)s ; Schedule host check retries at 1 minute intervals
1688 max_check_attempts %(max_check_attempts)s ; Check each FreeBSD host 10 times (max)
1689 check_command check-host-alive ; Default command to check FreeBSD hosts
1690 register 0 ; DONT REGISTER THIS DEFINITION - ITS NOT A REAL HOST, JUST A TEMPLATE!
1691}
1692
1693define service {
1694 name wleiden-service ; Default Service Template
1695 use generic-service ; Use the standard template as initial starting point
1696 check_period 24x7 ; By default, FreeBSD hosts are checked round the clock
1697 check_interval %(check_interval)s ; Actively check the host every 5 minutes
1698 retry_interval %(retry_interval)s ; Schedule host check retries at 1 minute intervals
1699 max_check_attempts %(max_check_attempts)s ; Check each FreeBSD host 10 times (max)
1700 register 0 ; DONT REGISTER THIS DEFINITION - ITS NOT A REAL HOST, JUST A TEMPLATE!
1701}
1702
1703# Please make sure to install:
1704# make -C /usr/ports/net-mgmt/nagios-check_netsnmp install clean
1705#
1706define command{
1707 command_name check_netsnmp_disk
1708 command_line $USER1$/check_netsnmp -H $HOSTADDRESS$ -o disk
1709}
1710
1711define command{
1712 command_name check_netsnmp_load
1713 command_line $USER1$/check_netsnmp -H $HOSTADDRESS$ -o load
1714}
1715
1716define command{
1717 command_name check_netsnmp_proc
1718 command_line $USER1$/check_netsnmp -H $HOSTADDRESS$ -o proc
1719}
1720
1721define command{
1722 command_name check_disk_snmp
1723 command_line /usr/local/libexec/nagios/check_disk_snmp.pl -H $HOSTADDRESS$ -d $ARG1$
1724}
1725
1726define command{
1727 command_name check_by_ssh
1728 command_line $USER1$/check_by_ssh -H $HOSTADDRESS$ -p $ARG1$ -C "$ARG2$ $ARG3$ $ARG4$ $ARG5$ $ARG6$"
1729}
1730
1731define command{
1732 command_name check_dns_wl
1733 command_line $USER1$/v2/check_dns_wl $HOSTADDRESS$ $ARG1$
1734}
1735
1736
1737# TDB: dhcp leases
1738# /usr/local/libexec/nagios/check_netsnmp -H 192.168.178.47 --oid 1 exec
1739
1740# TDB: internet status
1741# /usr/local/libexec/nagios/check_netsnmp -H 192.168.178.47 --oid 1 file
1742
1743# TDB: Advanced local passive checks
1744# /usr/local/libexec/nagios/check_by_ssh
1745''' % params
1746
1747 print '''\
1748# Service Group, not displayed by default
1749define hostgroup {
1750 hostgroup_name srv_hybrid
1751 alias All Hybrid Nodes
1752 register 0
1753}
1754
1755define service {
1756 use wleiden-service
1757 hostgroup_name srv_hybrid
1758 service_description SSH
1759 check_command check_ssh
1760}
1761
1762define service {
1763 use wleiden-service
1764 hostgroup_name srv_hybrid
1765 service_description HTTP
1766 check_command check_http
1767}
1768
1769define service {
1770 use wleiden-service
1771 hostgroup_name srv_hybrid
1772 service_description DNS
1773 check_command check_dns_wl!"www.wirelessleiden.nl"
1774}
1775
1776
1777define service {
1778 use wleiden-service
1779 hostgroup_name srv_hybrid
1780 service_description VAR space
1781 check_command check_disk_snmp!/var
1782}
1783'''
1784
1785 if heavy_load:
1786 print '''\
1787define service {
1788 use wleiden-service
1789 hostgroup_name srv_hybrid
1790 service_description SNMP
1791 check_command check_snmp
1792}
1793
1794define service {
1795 use wleiden-service
1796 hostgroup_name srv_hybrid
1797 service_description NTP
1798 check_command check_ntp_peer
1799}
1800
1801define service {
1802 use wleiden-service
1803 hostgroup_name srv_hybrid
1804 service_description LOAD
1805 check_command check_netsnmp_load
1806}
1807
1808define service {
1809 use wleiden-service
1810 hostgroup_name srv_hybrid
1811 service_description PROC
1812 check_command check_netsnmp_proc
1813}
1814
1815define service {
1816 use wleiden-service
1817 hostgroup_name srv_hybrid
1818 service_description DISK
1819 check_command check_netsnmp_disk
1820}
1821'''
1822 for node in get_hostlist():
1823 datadump = get_yaml(node)
1824 if not datadump['status'] == 'up':
1825 continue
1826 if not hostgroup_details.has_key(datadump['monitoring_group']):
1827 hostgroup_details[datadump['monitoring_group']] = datadump['monitoring_group']
1828 print '''\
1829define host {
1830 use wleiden-node
1831 host_name %(autogen_fqdn)s
1832 address %(masterip)s
1833 hostgroups srv_hybrid,%(monitoring_group)s
1834}
1835''' % datadump
1836
1837 for name,alias in hostgroup_details.iteritems():
1838 print '''\
1839define hostgroup {
1840 hostgroup_name %s
1841 alias %s
1842} ''' % (name, alias)
1843
1844
1845 elif sys.argv[1] == "full-export":
1846 hosts = {}
1847 for node in get_hostlist():
1848 datadump = get_yaml(node)
1849 hosts[datadump['nodename']] = datadump
1850 print yaml.dump(hosts)
1851
1852 elif sys.argv[1] == "dns":
1853 make_dns(sys.argv[2] if len(sys.argv) > 2 else 'dns', 'external' in sys.argv)
1854 elif sys.argv[1] == "cleanup":
1855 # First generate all datadumps
1856 datadumps = dict()
1857 ssid_to_node = dict()
1858 for host in get_hostlist():
1859 logger.info("# Processing: %s", host)
1860 # Set some boring default values
1861 datadump = { 'board' : 'UNKNOWN' }
1862 datadump.update(get_yaml(host))
1863 datadumps[datadump['autogen_realname']] = datadump
1864
1865 (poel, errors) = make_relations(datadumps)
1866 print "\n".join(["# WARNING: %s" % x for x in errors])
1867
1868 for host,datadump in datadumps.iteritems():
1869 try:
1870 # Convert all yes and no to boolean values
1871 def fix_boolean(dump):
1872 for key in dump.keys():
1873 if type(dump[key]) == dict:
1874 dump[key] = fix_boolean(dump[key])
1875 elif str(dump[key]).lower() in ["yes", "true"]:
1876 dump[key] = True
1877 elif str(dump[key]).lower() in ["no", "false"]:
1878 # Compass richting no (Noord Oost) is valid input
1879 if key != "compass": dump[key] = False
1880 return dump
1881 datadump = fix_boolean(datadump)
1882
1883 if datadump['rdnap_x'] and datadump['rdnap_y']:
1884 datadump['latitude'], datadump['longitude'] = map(lambda x: "%.5f" % x, rd2etrs(datadump['rdnap_x'], datadump['rdnap_y']))
1885 elif datadump['latitude'] and datadump['longitude']:
1886 datadump['rdnap_x'], datadump['rdnap_y'] = etrs2rd(datadump['latitude'], datadump['longitude'])
1887
1888 if datadump['nodename'].startswith('Proxy'):
1889 datadump['nodename'] = datadump['nodename'].lower()
1890
1891 for iface_key in datadump['autogen_iface_keys']:
1892 try:
1893 # All our normal wireless cards are normal APs now
1894 if datadump[iface_key]['type'] in ['11a', '11b', '11g', 'wireless']:
1895 datadump[iface_key]['mode'] = 'ap'
1896 # Wireless Leiden SSID have an consistent lowercase/uppercase
1897 if datadump[iface_key].has_key('ssid'):
1898 ssid = datadump[iface_key]['ssid']
1899 prefix = 'ap-WirelessLeiden-'
1900 if ssid.lower().startswith(prefix.lower()):
1901 datadump[iface_key]['ssid'] = prefix + ssid[len(prefix)].upper() + ssid[len(prefix) + 1:]
1902 if datadump[iface_key].has_key('ns_ip') and not datadump[iface_key].has_key('mode'):
1903 datadump[iface_key]['mode'] = 'autogen-FIXME'
1904 if not datadump[iface_key].has_key('comment'):
1905 datadump[iface_key]['comment'] = 'autogen-FIXME'
1906
1907 if datadump[iface_key].has_key('ns_mac'):
1908 datadump[iface_key]['ns_mac'] = datadump[iface_key]['ns_mac'].lower()
1909
1910 if datadump[iface_key]['comment'].startswith('autogen-') and datadump[iface_key].has_key('comment'):
1911 datadump[iface_key] = datadump[iface_key]['desc']
1912
1913 # We are not using 802.11b anymore. OFDM is preferred over DSSS
1914 # due to better collision avoidance.
1915 if datadump[iface_key]['type'] == '11b':
1916 datadump[iface_key]['type'] = '11g'
1917
1918 # Setting 802.11g channels to de-facto standards, to avoid
1919 # un-detected sharing with other overlapping channels
1920 #
1921 # Technically we could also use channel 13 in NL, but this is not
1922 # recommended as foreign devices might not be able to select this
1923 # channel. Secondly using 1,5,9,13 instead is going to clash with
1924 # the de-facto usage of 1,6,11.
1925 #
1926 # See: https://en.wikipedia.org/wiki/List_of_WLAN_channels
1927 channels_at_2400Mhz = (1,6,11)
1928 if datadump[iface_key]['type'] == '11g' and datadump[iface_key].has_key('channel'):
1929 datadump[iface_key]['channel'] = int(datadump[iface_key]['channel'])
1930 if datadump[iface_key]['channel'] not in channels_at_2400Mhz:
1931 datadump[iface_key]['channel'] = random.choice(channels_at_2400Mhz)
1932
1933 # Mandatory interface keys
1934 if not datadump[iface_key].has_key('status'):
1935 datadump[iface_key]['status'] = 'planned'
1936
1937 x = datadump[iface_key]['comment']
1938 datadump[iface_key]['comment'] = x[0].upper() + x[1:]
1939
1940 # Fixing bridge_type if none is found
1941 if datadump[iface_key].get('extra_type', '') == 'eth2wifibridge':
1942 if not 'bridge_type' in datadump[iface_key]:
1943 datadump[iface_key]['bridge_type'] = 'NanoStation M5'
1944
1945 # Making sure description works
1946 if datadump[iface_key].has_key('desc'):
1947 if datadump[iface_key]['comment'].lower() == datadump[iface_key]['desc'].lower():
1948 del datadump[iface_key]['desc']
1949 else:
1950 print "# ERROR: At %s - %s" % (datadump['nodename'], iface_key)
1951 response = fix_conflict(datadump[iface_key]['comment'], datadump[iface_key]['desc'])
1952 if response:
1953 datadump[iface_key]['comment'] = response
1954 del datadump[iface_key]['desc']
1955
1956 # Check DHCP configuration
1957 dhcp_type(datadump[iface_key])
1958
1959 # Set the compass value based on the angle between the poels
1960 if datadump[iface_key].has_key('ns_ip'):
1961 my_pool = poel[network(datadump[iface_key]['ip'])]
1962 remote_hosts = list(set([x[0] for x in my_pool]) - set([host]))
1963 if remote_hosts:
1964 compass_target = remote_hosts[0]
1965 datadump[iface_key]['compass'] = cd_between_hosts(host, compass_target, datadumps)
1966
1967 # Monitoring Group default
1968 if not 'monitoring_group' in datadump:
1969 datadump['monitoring_group'] = 'wleiden'
1970
1971 except Exception as e:
1972 print "# Error while processing interface %s" % iface_key
1973 raise
1974 store_yaml(datadump)
1975 except Exception as e:
1976 print "# Error while processing %s" % host
1977 raise
1978 elif sys.argv[1] == "list":
1979 use_fqdn = False
1980 if len(sys.argv) < 4 or not sys.argv[2] in ["up", "down", "planned", "all"]:
1981 usage()
1982 if sys.argv[3] == "nodes":
1983 systems = get_nodelist()
1984 elif sys.argv[3] == "proxies":
1985 systems = get_proxylist()
1986 elif sys.argv[3] == "systems":
1987 systems = get_hostlist()
1988 else:
1989 usage()
1990 if len(sys.argv) > 4:
1991 if sys.argv[4] == "fqdn":
1992 use_fqdn = True
1993 else:
1994 usage()
1995
1996 for system in systems:
1997 datadump = get_yaml(system)
1998
1999 output = datadump['autogen_fqdn'] if use_fqdn else system
2000 if sys.argv[2] == "all":
2001 print output
2002 elif datadump['status'] == sys.argv[2]:
2003 print output
2004 elif sys.argv[1] == "create":
2005 if sys.argv[2] == "network.kml":
2006 print make_network_kml.make_graph()
2007 elif sys.argv[2] == "host-ips.txt":
2008 for system in get_hostlist():
2009 datadump = get_yaml(system)
2010 ips = [datadump['masterip']]
2011 for ifkey in datadump['autogen_iface_keys']:
2012 ips.append(datadump[ifkey]['ip'].split('/')[0])
2013 print system, ' '.join(ips)
2014 elif sys.argv[2] == "host-pos.txt":
2015 for system in get_hostlist():
2016 datadump = get_yaml(system)
2017 print system, datadump['rdnap_x'], datadump['rdnap_y']
2018 elif sys.argv[2] == 'ssh_config':
2019 print '''
2020Host *.wleiden.net
2021 User root
2022
2023Host 172.16.*.*
2024 User root
2025'''
2026 for system in get_hostlist():
2027 datadump = get_yaml(system)
2028 print '''\
2029Host %s
2030 User root
2031
2032Host %s
2033 User root
2034
2035Host %s
2036 User root
2037
2038Host %s
2039 User root
2040''' % (system, system.lower(), datadump['nodename'], datadump['nodename'].lower())
2041 else:
2042 usage()
2043 else:
2044 usage()
2045 else:
2046 # Do not enable debugging for config requests as it highly clutters the output
2047 if not is_text_request():
2048 cgitb.enable()
2049 response_headers, output = process_cgi_request()
2050 print_cgi_response(response_headers, output)
2051
2052def application(environ, start_response):
2053 status = '200 OK'
2054 response_headers, output = process_cgi_request(environ)
2055 start_response(status, response_headers)
2056
2057 # Debugging only
2058 # output = 'wsgi.multithread = %s' % repr(environ['wsgi.multithread'])
2059 # soutput += '\nwsgi.multiprocess = %s' % repr(environ['wsgi.multiprocess'])
2060 return [output]
2061
2062if __name__ == "__main__":
2063 main()
Note: See TracBrowser for help on using the repository browser.