source: genesis/tools/gformat.py@ 13792

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

Instead of 32 spaces, use 2 spares, since unbound does not seems to like this setup

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