source: genesis/tools/gformat.py@ 13985

Last change on this file since 13985 was 13985, checked in by www, 7 years ago

Fix error print statements handled as headers when updating with an error.

This quirck is to make the code somehow more object consistent, we also could have moved the header statement, yet that is no fun...

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