source: genesis/tools/gformat.py@ 13335

Last change on this file since 13335 was 13328, checked in by rick, 9 years ago

Adding support for devices like repeaters which are specified in _extraX_ blocks

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