source: genesis/tools/gformat.py@ 13167

Last change on this file since 13167 was 13079, checked in by huub, 10 years ago

cm9_mac vervangen door wlan_mac en dus hardware-onafhankelijke naam, geschikt voor gebruik met andere wifi-kaartjes

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