source: genesis/tools/gformat.py@ 13860

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

Fix generation & cleanup of config files

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