source: genesis/tools/gformat.py@ 10703

Last change on this file since 10703 was 10703, checked in by rick, 13 years ago

No false ap-wds flag for accespoints.

Fixes: nodefactory#117

  • Property svn:executable set to *
  • Property svn:keywords set to Id
File size: 44.6 KB
Line 
1#!/usr/bin/env python
2#
3# vim:ts=2:et:sw=2:ai
4# Wireless Leiden configuration generator, based on yaml files'
5#
6# XXX: This should be rewritten to make use of the ipaddr.py library.
7#
8# Sample apache configuration (mind the AcceptPathInfo!)
9# ScriptAlias /wleiden/config /usr/local/www/genesis/tools/gformat.py
10# <Directory /usr/local/www/genesis>
11# Allow from all
12# AcceptPathInfo On
13# </Directory>
14#
15# Rick van der Zwet <info@rickvanderzwet.nl>
16#
17
18# Hack to make the script directory is also threated as a module search path.
19import sys
20import os
21import re
22sys.path.append(os.path.dirname(__file__))
23
24import cgi
25import cgitb
26import copy
27import glob
28import socket
29import string
30import subprocess
31import time
32import rdnap
33import make_network_kml
34from pprint import pprint
35from collections import defaultdict
36try:
37 import yaml
38except ImportError, e:
39 print e
40 print "[ERROR] Please install the python-yaml or devel/py-yaml package"
41 exit(1)
42
43try:
44 from yaml import CLoader as Loader
45 from yaml import CDumper as Dumper
46except ImportError:
47 from yaml import Loader, Dumper
48
49from jinja2 import Environment, Template
50def yesorno(value):
51 return "YES" if bool(value) else "NO"
52env = Environment()
53env.filters['yesorno'] = yesorno
54def render_template(datadump, template):
55 result = env.from_string(template).render(datadump)
56 # Make it look pretty to the naked eye, as jinja templates are not so
57 # friendly when it comes to whitespace formatting
58 ## Remove extra whitespace at end of line lstrip() style.
59 result = re.sub(r'\n[\ ]+','\n', result)
60 ## Include only a single newline between an definition and a comment
61 result = re.sub(r'(["\'])\n+([a-z]|\n#\n)',r'\1\n\2', result)
62 ## Remove extra newlines after single comment
63 result = re.sub(r'(#\n)\n+([a-z])',r'\1\2', result)
64 return result
65
66import logging
67logging.basicConfig(format='# %(levelname)s: %(message)s' )
68logger = logging.getLogger()
69logger.setLevel(logging.DEBUG)
70
71
72if os.environ.has_key('CONFIGROOT'):
73 NODE_DIR = os.environ['CONFIGROOT']
74else:
75 NODE_DIR = os.path.abspath(os.path.dirname(__file__)) + '/../nodes'
76__version__ = '$Id: gformat.py 10703 2012-05-07 06:49:21Z rick $'
77
78
79files = [
80 'authorized_keys',
81 'dnsmasq.conf',
82 'dhcpd.conf',
83 'rc.conf.local',
84 'resolv.conf',
85 'motd',
86 'ntp.conf',
87 'wleiden.yaml',
88 ]
89
90# Global variables uses
91OK = 10
92DOWN = 20
93UNKNOWN = 90
94
95def get_yaml(item):
96 """ Get configuration yaml for 'item'"""
97 gfile = os.path.join(NODE_DIR,item,'wleiden.yaml')
98
99 # Use some boring defaults
100 datadump = {}
101 f = open(gfile, 'r')
102 datadump.update(yaml.load(f,Loader=Loader))
103 if datadump['nodetype'] == 'Hybrid':
104 defaults = { 'service_proxy_normal' : False, 'service_proxy_ileiden' : False, 'service_accesspoint' : True }
105 for (key,value) in defaults.iteritems():
106 if not datadump.has_key(key):
107 datadump[key] = value
108 f.close()
109
110 # Preformat certain needed variables for formatting and push those into special object
111 datadump['autogen_iface_keys'] = get_interface_keys(datadump)
112
113 wlan_count=0
114 for key in datadump['autogen_iface_keys']:
115 if datadump[key]['type'] in ['11a', '11b', '11g', 'wireless']:
116 datadump[key]['autogen_ifname'] = 'wlan%i' % wlan_count
117 wlan_count += 1
118 else:
119 datadump[key]['autogen_ifname'] = datadump[key]['interface'].split(':')[0]
120
121 dhcp_interfaces = [datadump[key]['autogen_ifname'] for key in datadump['autogen_iface_keys'] if datadump[key]['dhcp']]
122 datadump['autogen_dhcp_interfaces'] = ','.join(dhcp_interfaces)
123 datadump['autogen_item'] = item
124
125 datadump['autogen_realname'] = get_realname(datadump)
126 datadump['autogen_domain'] = datadump['domain'] if datadump.has_key('domain') else 'wleiden.net.'
127 datadump['autogen_fqdn'] = datadump['autogen_realname'] + '.' + datadump['autogen_domain']
128 return datadump
129
130
131def store_yaml(datadump, header=False):
132 """ Store configuration yaml for 'item'"""
133 item = datadump['autogen_item']
134 gfile = os.path.join(NODE_DIR,item,'wleiden.yaml')
135
136 f = open(gfile, 'w')
137 f.write(generate_wleiden_yaml(datadump, header))
138 f.close()
139
140
141
142def make_relations():
143 """ Process _ALL_ yaml files to get connection relations """
144 errors = ""
145 poel = defaultdict(list)
146 for host in get_hostlist():
147 try:
148 datadump = get_yaml(host)
149 for iface_key in datadump['autogen_iface_keys']:
150 l = datadump[iface_key]['ip']
151 addr, mask = l.split('/')
152
153 # Not parsing of these folks please
154 if not valid_addr(addr):
155 continue
156
157 addr = parseaddr(addr)
158 mask = int(mask)
159 network = addr & ~((1 << (32 - mask)) - 1)
160 poel[network] += [(host,datadump[iface_key])]
161 except (KeyError, ValueError), e:
162 errors += "[FOUT] in '%s' interface '%s'" % (host,iface_key)
163 errors += e
164 continue
165 return (poel, errors)
166
167
168
169def valid_addr(addr):
170 """ Show which address is valid in which are not """
171 return str(addr).startswith('172.')
172
173def get_system_list(prefix):
174 return sorted([os.path.basename(os.path.dirname(x)) for x in glob.glob("%s/%s*/wleiden.yaml" % (NODE_DIR, prefix))])
175
176get_hybridlist = lambda: get_system_list("Hybrid")
177get_nodelist = lambda: get_system_list("CNode")
178get_proxylist = lambda: get_system_list("Proxy")
179
180def get_hostlist():
181 """ Combined hosts and proxy list"""
182 return get_nodelist() + get_proxylist() + get_hybridlist()
183
184def angle_between_points(lat1,lat2,long1,long2):
185 """
186 Return Angle in radians between two GPS coordinates
187 See: http://stackoverflow.com/questions/3809179/angle-between-2-gps-coordinates
188 """
189 dy = lat2 - lat1
190 dx = math.cos(math.pi/180*lat1)*(long2 - long1)
191 angle = math.atan2(dy,dx)
192 return angle
193
194def angle_to_cd(angle):
195 """ Return Dutch Cardinal Direction estimation in 'one digit' of radian angle """
196
197 # For easy conversion get positive degree
198 degrees = math.degrees(angle)
199 if degrees < 0:
200 360 - abs(degrees)
201
202 # Numbers can be confusing calculate from the 4 main directions
203 p = 22.5
204 if degrees < p:
205 return "n"
206 elif degrees < (90 - p):
207 return "no"
208 elif degrees < (90 + p):
209 return "o"
210 elif degrees < (180 - p):
211 return "zo"
212 elif degrees < (180 + p):
213 return "z"
214 elif degrees < (270 - p):
215 return "zw"
216 elif degrees < (270 + p):
217 return "w"
218 elif degrees < (360 - p):
219 return "nw"
220 else:
221 return "n"
222
223
224def generate_title(nodelist):
225 """ Main overview page """
226 items = {'root' : "." }
227 def fl(spaces, line):
228 return (' ' * spaces) + line + '\n'
229
230 output = """
231<html>
232 <head>
233 <title>Wireless leiden Configurator - GFormat</title>
234 <style type="text/css">
235 th {background-color: #999999}
236 tr:nth-child(odd) {background-color: #cccccc}
237 tr:nth-child(even) {background-color: #ffffff}
238 th, td {padding: 0.1em 1em}
239 </style>
240 </head>
241 <body>
242 <center>
243 <form type="GET" action="%(root)s">
244 <input type="hidden" name="action" value="update">
245 <input type="submit" value="Update Configuration Database (SVN)">
246 </form>
247 <table>
248 <caption><h3>Wireless Leiden Configurator</h3></caption>
249 """ % items
250
251 for node in nodelist:
252 items['node'] = node
253 output += fl(5, '<tr>') + fl(7,'<td><a href="%(root)s/%(node)s">%(node)s</a></td>' % items)
254 for config in files:
255 items['config'] = config
256 output += fl(7,'<td><a href="%(root)s/%(node)s/%(config)s">%(config)s</a></td>' % items)
257 output += fl(5, "</tr>")
258 output += """
259 </table>
260 <hr />
261 <em>%s</em>
262 </center>
263 </body>
264</html>
265 """ % __version__
266
267 return output
268
269
270
271def generate_node(node):
272 """ Print overview of all files available for node """
273 return "\n".join(files)
274
275def generate_node_overview(host):
276 """ Print overview of all files available for node """
277 datadump = get_yaml(host)
278 params = { 'host' : host }
279 output = "<em><a href='..'>Back to overview</a></em><hr />"
280 output += "<h2>Available files:</h2><ul>"
281 for cf in files:
282 params['cf'] = cf
283 output += '<li><a href="%(host)s/%(cf)s">%(cf)s</a></li>\n' % params
284 output += "</ul>"
285
286 # Generate and connection listing
287 output += "<h2>Connected To:</h2><ul>"
288 (poel, errors) = make_relations()
289 for network, hosts in poel.iteritems():
290 if host in [x[0] for x in hosts]:
291 if len(hosts) == 1:
292 # Single not connected interface
293 continue
294 for remote,ifacedump in hosts:
295 if remote == host:
296 # This side of the interface
297 continue
298 params = { 'remote': remote, 'remote_ip' : ifacedump['ip'] }
299 output += '<li><a href="%(remote)s">%(remote)s</a> -- %(remote_ip)s</li>\n' % params
300 output += "</ul>"
301 output += "<h2>MOTD details:</h2><pre>" + generate_motd(datadump) + "</pre>"
302
303 output += "<hr /><em><a href='..'>Back to overview</a></em>"
304 return output
305
306
307def generate_header(ctag="#"):
308 return """\
309%(ctag)s
310%(ctag)s DO NOT EDIT - Automatically generated by 'gformat'
311%(ctag)s Generated at %(date)s by %(host)s
312%(ctag)s
313""" % { 'ctag' : ctag, 'date' : time.ctime(), 'host' : socket.gethostname() }
314
315
316
317def parseaddr(s):
318 """ Process IPv4 CIDR notation addr to a (binary) number """
319 f = s.split('.')
320 return (long(f[0]) << 24L) + \
321 (long(f[1]) << 16L) + \
322 (long(f[2]) << 8L) + \
323 long(f[3])
324
325
326
327def showaddr(a):
328 """ Display IPv4 addr in (dotted) CIDR notation """
329 return "%d.%d.%d.%d" % ((a >> 24) & 0xff, (a >> 16) & 0xff, (a >> 8) & 0xff, a & 0xff)
330
331
332def is_member(ip, mask, canidate):
333 """ Return True if canidate is part of ip/mask block"""
334 ip_addr = gformat.parseaddr(ip)
335 ip_canidate = gformat.parseaddr(canidate)
336 mask = int(mask)
337 ip_addr = ip_addr & ~((1 << (32 - mask)) - 1)
338 ip_canidate = ip_canidate & ~((1 << (32 - mask)) - 1)
339 return ip_addr == ip_canidate
340
341
342
343def cidr2netmask(netmask):
344 """ Given a 'netmask' return corresponding CIDR """
345 return showaddr(0xffffffff & (0xffffffff << (32 - int(netmask))))
346
347def get_network(addr, mask):
348 return showaddr(parseaddr(addr) & ~((1 << (32 - int(mask))) - 1))
349
350
351def generate_dhcpd_conf(datadump):
352 """ Generate config file '/usr/local/etc/dhcpd.conf """
353 output = generate_header()
354 output += Template("""\
355# option definitions common to all supported networks...
356option domain-name "dhcp.{{ autogen_fqdn }}";
357
358default-lease-time 600;
359max-lease-time 7200;
360
361# Use this to enble / disable dynamic dns updates globally.
362#ddns-update-style none;
363
364# If this DHCP server is the official DHCP server for the local
365# network, the authoritative directive should be uncommented.
366authoritative;
367
368# Use this to send dhcp log messages to a different log file (you also
369# have to hack syslog.conf to complete the redirection).
370log-facility local7;
371
372#
373# Interface definitions
374#
375\n""").render(datadump)
376
377 for iface_key in datadump['autogen_iface_keys']:
378 if not datadump[iface_key].has_key('comment'):
379 datadump[iface_key]['comment'] = None
380 output += "## %(interface)s - %(desc)s - %(comment)s\n" % datadump[iface_key]
381
382 (addr, mask) = datadump[iface_key]['ip'].split('/')
383 datadump[iface_key]['addr'] = addr
384 datadump[iface_key]['netmask'] = cidr2netmask(mask)
385 datadump[iface_key]['subnet'] = get_network(addr, mask)
386 try:
387 (dhcp_start, dhcp_stop) = datadump[iface_key]['dhcp'].split('-')
388 except (AttributeError, ValueError):
389 output += "subnet %(subnet)s netmask %(netmask)s {\n ### not autoritive\n}\n\n" % datadump[iface_key]
390 continue
391
392 dhcp_part = ".".join(addr.split('.')[0:3])
393 datadump[iface_key]['dhcp_start'] = dhcp_part + "." + dhcp_start
394 datadump[iface_key]['dhcp_stop'] = dhcp_part + "." + dhcp_stop
395 output += """\
396subnet %(subnet)s netmask %(netmask)s {
397 range %(dhcp_start)s %(dhcp_stop)s;
398 option routers %(addr)s;
399 option domain-name-servers %(addr)s;
400}
401\n""" % datadump[iface_key]
402
403 return output
404
405
406
407def generate_dnsmasq_conf(datadump):
408 """ Generate configuration file '/usr/local/etc/dnsmasq.conf' """
409 output = generate_header()
410 output += Template("""\
411# DHCP server options
412dhcp-authoritative
413dhcp-fqdn
414domain=dhcp.{{ autogen_fqdn }}
415domain-needed
416expand-hosts
417log-async=100
418
419# Low memory footprint
420cache-size=10000
421
422\n""").render(datadump)
423
424 for iface_key in datadump['autogen_iface_keys']:
425 if not datadump[iface_key].has_key('comment'):
426 datadump[iface_key]['comment'] = None
427 output += "## %(interface)s - %(desc)s - %(comment)s\n" % datadump[iface_key]
428
429 try:
430 (dhcp_start, dhcp_stop) = datadump[iface_key]['dhcp'].split('-')
431 (ip, cidr) = datadump[iface_key]['ip'].split('/')
432 datadump[iface_key]['netmask'] = cidr2netmask(cidr)
433 except (AttributeError, ValueError):
434 output += "# not autoritive\n\n"
435 continue
436
437 dhcp_part = ".".join(ip.split('.')[0:3])
438 datadump[iface_key]['dhcp_start'] = dhcp_part + "." + dhcp_start
439 datadump[iface_key]['dhcp_stop'] = dhcp_part + "." + dhcp_stop
440 output += "dhcp-range=%(interface)s,%(dhcp_start)s,%(dhcp_stop)s,%(netmask)s,24h\n\n" % datadump[iface_key]
441
442 return output
443
444
445
446def generate_rc_conf_local(datadump):
447 """ Generate configuration file '/etc/rc.conf.local' """
448 if not datadump.has_key('ileiden'):
449 datadump['autogen_ileiden_enable'] = False
450 else:
451 datadump['autogen_ileiden_enable'] = datadump['ileiden']
452
453 datadump['autogen_ileiden_enable'] = switchFormat(datadump['autogen_ileiden_enable'])
454
455 ileiden_proxies = []
456 normal_proxies = []
457 for proxy in get_proxylist():
458 proxydump = get_yaml(proxy)
459 if proxydump['ileiden']:
460 ileiden_proxies.append(proxydump)
461 else:
462 normal_proxies.append(proxydump)
463 for host in get_hybridlist():
464 hostdump = get_yaml(host)
465 if hostdump['service_proxy_ileiden']:
466 ileiden_proxies.append(hostdump)
467 if hostdump['service_proxy_normal']:
468 normal_proxies.append(hostdump)
469
470 datadump['autogen_ileiden_proxies'] = ileiden_proxies
471 datadump['autogen_normal_proxies'] = normal_proxies
472 datadump['autogen_ileiden_proxies_ips'] = ','.join([x['masterip'] for x in ileiden_proxies])
473 datadump['autogen_ileiden_proxies_names'] = ','.join([x['autogen_item'] for x in ileiden_proxies])
474 datadump['autogen_normal_proxies_ips'] = ','.join([x['masterip'] for x in normal_proxies])
475 datadump['autogen_normal_proxies_names'] = ','.join([x['autogen_item'] for x in normal_proxies])
476
477 output = generate_header("#");
478 output += render_template(datadump, """\
479hostname='{{ autogen_fqdn }}'
480location='{{ location }}'
481nodetype="{{ nodetype }}"
482
483#
484# Configured listings
485#
486captive_portal_whitelist=""
487{% if nodetype == "Proxy" %}
488#
489# Proxy Configuration
490#
491{% if gateway -%}
492defaultrouter="{{ gateway }}"
493{% else -%}
494#defaultrouter="NOTSET"
495{% endif -%}
496internalif="{{ internalif }}"
497ileiden_enable="{{ autogen_ileiden_enable }}"
498gateway_enable="{{ autogen_ileiden_enable }}"
499pf_enable="yes"
500pf_rules="/etc/pf.conf"
501{% if autogen_ileiden_enable -%}
502pf_flags="-D ext_if={{ externalif }} -D int_if={{ internalif }} -D publicnat={80,443}"
503lvrouted_enable="{{ autogen_ileiden_enable }}"
504lvrouted_flags="-u -s s00p3rs3kr3t -m 28"
505{% else -%}
506pf_flags="-D ext_if={{ externalif }} -D int_if={{ internalif }} -D publicnat={0}"
507{% endif -%}
508{% if internalroute -%}
509static_routes="wleiden"
510route_wleiden="-net 172.16.0.0/12 {{ internalroute }}"
511{% endif -%}
512
513{% elif nodetype == "Hybrid" %}
514 #
515 # Hybrid Configuration
516 #
517 list_ileiden_proxies="
518 {% for item in autogen_ileiden_proxies -%}
519 {{ "%-16s"|format(item.masterip) }} # {{ item.autogen_realname }}
520 {% endfor -%}
521 "
522 list_normal_proxies="
523 {% for item in autogen_normal_proxies -%}
524 {{ "%-16s"|format(item.masterip) }} # {{ item.autogen_realname }}
525 {% endfor -%}
526 "
527
528 captive_portal_interfaces="{{ autogen_dhcp_interfaces|default('none', true) }}"
529 externalif="{{ externalif|default('vr0', true) }}"
530 masterip="{{ masterip }}"
531
532 # Defined services
533 service_proxy_ileiden="{{ service_proxy_ileiden|yesorno }}"
534 service_proxy_normal="{{ service_proxy_normal|yesorno }}"
535 service_accesspoint="{{ service_accesspoint|yesorno }}"
536 #
537
538 {% if service_proxy_ileiden %}
539 pf_rules="/etc/pf.hybrid.conf"
540 pf_flags="-D ext_if=$externalif -D ext_if_net=$externalif:network -D masterip=$masterip"
541 pf_flags="$pf_flags -D publicnat=80,443"
542 {% elif service_proxy_normal %}
543 pf_rules="/etc/pf.hybrid.conf"
544 pf_flags="-D ext_if=$externalif -D ext_if_net=$externalif:network -D masterip=$masterip"
545 pf_flags="$pf_flags -D publicnat=0"
546 lvrouted_flags="$lvrouted_flags -z `make_list "$list_ileiden_proxies" ","`"
547 named_setfib="1"
548 tinyproxy_setfib="1"
549 dnsmasq_setfib="1"
550 sshd_setfib="1"
551 {% else %}
552 pf_rules="/etc/pf.node.conf"
553 pf_flags=""
554 {% endif %}
555
556 {% if service_proxy_normal %}
557 tinyproxy_enable="yes"
558 {% else %}
559 pen_wrapper_enable="yes"
560 {% endif %}
561
562 {% if service_accesspoint %}
563 pf_flags="$pf_flags -D captive_portal_interfaces=$captive_portal_interfaces"
564 {% endif %}
565
566 {% if board == "ALIX2" %}
567 #
568 # ''Fat'' configuration, board has 256MB RAM
569 #
570 dnsmasq_enable="NO"
571 named_enable="YES"
572 dhcpd_enable="YES"
573 {% endif -%}
574
575 {% if service_proxy_ileiden and gateway %}
576 defaultrouter="{{ gateway }}"
577 {% endif %}
578{% elif nodetype == "CNode" %}
579#
580# NODE iLeiden Configuration
581#
582
583# iLeiden Proxies {{ autogen_ileiden_proxies_names }}
584list_ileiden_proxies="{{ autogen_ileiden_proxies_ips }}"
585# normal Proxies {{ autogen_normal_proxies_names }}
586list_normal_proxies="{{ autogen_normal_proxies_ips }}"
587
588captive_portal_interfaces="{{ autogen_dhcp_interfaces }}"
589
590lvrouted_flags="-u -s s00p3rs3kr3t -m 28 -z $list_ileiden_proxies"
591{% endif %}
592
593#
594# Interface definitions
595#\n
596""")
597
598 # lo0 configuration:
599 # - 172.32.255.1/32 is the proxy.wleiden.net deflector
600 # - masterip is special as it needs to be assigned to at
601 # least one interface, so if not used assign to lo0
602 addrs_list = { 'lo0' : [("127.0.0.1/8", "LocalHost"), ("172.31.255.1/32","Proxy IP")] }
603 iface_map = {'lo0' : 'lo0'}
604 dhclient_if = {'lo0' : False}
605
606 masterip_used = False
607 for iface_key in datadump['autogen_iface_keys']:
608 if datadump[iface_key]['ip'].startswith(datadump['masterip']):
609 masterip_used = True
610 break
611 if not masterip_used:
612 addrs_list['lo0'].append((datadump['masterip'] + "/32", 'Master IP Not used in interface'))
613
614 for iface_key in datadump['autogen_iface_keys']:
615 ifacedump = datadump[iface_key]
616 ifname = ifacedump['autogen_ifname']
617
618 # Flag dhclient is possible
619 dhclient_if[ifname] = ifacedump.has_key('dhcpclient') and ifacedump['dhcpclient']
620
621 # Add interface IP to list
622 item = (ifacedump['ip'], ifacedump['desc'])
623 if addrs_list.has_key(ifname):
624 addrs_list[ifname].append(item)
625 else:
626 addrs_list[ifname] = [item]
627
628 # Alias only needs IP assignment for now, this might change if we
629 # are going to use virtual accesspoints
630 if "alias" in iface_key:
631 continue
632
633 # XXX: Might want to deduct type directly from interface name
634 if ifacedump['type'] in ['11a', '11b', '11g', 'wireless']:
635 # Default to station (client) mode
636 ifacedump['wlanmode'] = "sta"
637 if ifacedump['mode'] in ['master', 'master-wds', 'ap', 'ap-wds']:
638 ifacedump['wlanmode'] = "ap"
639 # Default to 802.11b mode
640 ifacedump['mode'] = '11b'
641 if ifacedump['type'] in ['11a', '11b' '11g']:
642 ifacedump['mode'] = ifacedump['type']
643
644 if not ifacedump.has_key('channel'):
645 if ifacedump['type'] == '11a':
646 ifacedump['channel'] = 36
647 else:
648 ifacedump['channel'] = 1
649
650 # Allow special hacks at the back like wds and stuff
651 if not ifacedump.has_key('extra'):
652 ifacedump['extra'] = 'regdomain ETSI country NL'
653
654 output += "wlans_%(interface)s='%(autogen_ifname)s'\n" % ifacedump
655 output += ("create_args_%(autogen_ifname)s='wlanmode %(wlanmode)s mode " +\
656 "%(mode)s ssid %(ssid)s %(extra)s channel %(channel)s'\n") % ifacedump
657
658 elif ifacedump['type'] in ['ethernet', 'eth']:
659 # No special config needed besides IP
660 pass
661 else:
662 assert False, "Unknown type " + ifacedump['type']
663
664 # Print IP address which needs to be assigned over here
665 output += "\n"
666 for iface,addrs in sorted(addrs_list.iteritems()):
667 for addr, comment in sorted(addrs,key=lambda x: parseaddr(x[0].split('/')[0])):
668 output += "# %s || %s || %s\n" % (iface, addr, comment)
669
670 # Write DHCLIENT entry
671 if dhclient_if[iface]:
672 output += "ifconfig_%s='SYNCDHCP'\n\n" % (iface)
673 else:
674 output += "ipv4_addrs_%s='%s'\n\n" % (iface, " ".join([x[0] for x in addrs]))
675
676 return output
677
678
679
680
681def get_all_configs():
682 """ Get dict with key 'host' with all configs present """
683 configs = dict()
684 for host in get_hostlist():
685 datadump = get_yaml(host)
686 configs[host] = datadump
687 return configs
688
689
690def get_interface_keys(config):
691 """ Quick hack to get all interface keys, later stage convert this to a iterator """
692 return sorted([elem for elem in config.keys() if (elem.startswith('iface_') and not "lo0" in elem)])
693
694
695def get_used_ips(configs):
696 """ Return array of all IPs used in config files"""
697 ip_list = []
698 for config in configs:
699 ip_list.append(config['masterip'])
700 for iface_key in get_interface_keys(config):
701 l = config[iface_key]['ip']
702 addr, mask = l.split('/')
703 # Special case do not process
704 if valid_addr(addr):
705 ip_list.append(addr)
706 else:
707 logger.error("## IP '%s' in '%s' not valid" % (addr, config['nodename']))
708 return sorted(ip_list)
709
710
711
712def generate_resolv_conf(datadump):
713 """ Generate configuration file '/etc/resolv.conf' """
714 # XXX: This should properly going to be an datastructure soon
715 datadump['autogen_header'] = generate_header("#")
716 datadump['autogen_edge_nameservers'] = ''
717 for host in get_proxylist():
718 hostdump = get_yaml(host)
719 datadump['autogen_edge_nameservers'] += "nameserver %(masterip)-15s # %(autogen_realname)s\n" % hostdump
720 for host in get_hybridlist():
721 hostdump = get_yaml(host)
722 if hostdump['service_proxy_ileiden'] or hostdump['service_proxy_normal']:
723 datadump['autogen_edge_nameservers'] += "nameserver %(masterip)-15s # %(autogen_realname)s\n" % hostdump
724
725 return Template("""\
726{{ autogen_header }}
727search wleiden.net
728
729# Try local (cache) first
730nameserver 127.0.0.1
731
732{% if service_proxy_normal or service_proxy_ileiden or nodetype == 'Proxy' -%}
733nameserver 8.8.8.8 # Google Public NameServer
734nameserver 8.8.4.4 # Google Public NameServer
735{% else -%}
736# START DYNAMIC LIST - updated by /tools/nameserver-shuffle
737{{ autogen_edge_nameservers }}
738{% endif -%}
739""").render(datadump)
740
741
742
743def generate_ntp_conf(datadump):
744 """ Generate configuration file '/etc/ntp.conf' """
745 # XXX: This should properly going to be an datastructure soon
746
747 datadump['autogen_header'] = generate_header("#")
748 datadump['autogen_ntp_servers'] = ''
749 for host in get_proxylist():
750 hostdump = get_yaml(host)
751 datadump['autogen_ntp_servers'] += "server %(masterip)-15s iburst maxpoll 9 # %(autogen_realname)s\n" % hostdump
752 for host in get_hybridlist():
753 hostdump = get_yaml(host)
754 if hostdump['service_proxy_ileiden'] or hostdump['service_proxy_normal']:
755 datadump['autogen_ntp_servers'] += "server %(masterip)-15s iburst maxpoll 9 # %(autogen_realname)s\n" % hostdump
756
757 return Template("""\
758{{ autogen_header }}
759
760{% if service_proxy_normal or service_proxy_ileiden or nodetype == 'Proxy' -%}
761# Machine hooked to internet.
762server 0.nl.pool.ntp.org iburst maxpoll 9
763server 1.nl.pool.ntp.org iburst maxpoll 9
764server 2.nl.pool.ntp.org iburst maxpoll 9
765server 3.nl.pool.ntp.org iburst maxpoll 9
766{% else -%}
767# Local Wireless Leiden NTP Servers.
768server 0.pool.ntp.wleiden.net iburst maxpoll 9
769server 1.pool.ntp.wleiden.net iburst maxpoll 9
770server 2.pool.ntp.wleiden.net iburst maxpoll 9
771server 3.pool.ntp.wleiden.net iburst maxpoll 9
772
773# All the configured NTP servers
774{{ autogen_ntp_servers }}
775{% endif %}
776
777# If a server loses sync with all upstream servers, NTP clients
778# no longer follow that server. The local clock can be configured
779# to provide a time source when this happens, but it should usually
780# be configured on just one server on a network. For more details see
781# http://support.ntp.org/bin/view/Support/UndisciplinedLocalClock
782# The use of Orphan Mode may be preferable.
783#
784server 127.127.1.0
785fudge 127.127.1.0 stratum 10
786""").render(datadump)
787
788
789
790def generate_motd(datadump):
791 """ Generate configuration file '/etc/motd' """
792 output = Template("""\
793FreeBSD run ``service motd onestart'' to make me look normal
794
795 WWW: {{ autogen_fqdn }} - http://www.wirelessleiden.nl
796 Loc: {{ location }}
797
798Services:
799{% if board == "ALIX2" -%}
800 - Core Node ({{ board }})
801{% else -%}
802 - Hulp Node ({{ board }})
803{% endif -%}
804{% if service_proxy_normal -%}
805 - Normal Proxy
806{% endif -%}
807{% if service_proxy_ileiden -%}
808 - iLeiden Proxy
809{% endif %}
810Interlinks:\n
811""").render(datadump)
812
813 # XXX: This is a hacky way to get the required data
814 for line in generate_rc_conf_local(datadump).split('\n'):
815 if '||' in line and not line[1:].split()[0] in ['lo0', 'ath0'] :
816 output += " - %s \n" % line[1:]
817 output += """\
818Attached bridges:
819"""
820 for iface_key in datadump['autogen_iface_keys']:
821 ifacedump = datadump[iface_key]
822 if ifacedump.has_key('ns_ip'):
823 output += " - %(interface)s || %(mode)s || %(ns_ip)s\n" % ifacedump
824
825 return output
826
827
828def format_yaml_value(value):
829 """ Get yaml value in right syntax for outputting """
830 if isinstance(value,str):
831 output = '"%s"' % value
832 else:
833 output = value
834 return output
835
836
837
838def format_wleiden_yaml(datadump):
839 """ Special formatting to ensure it is editable"""
840 output = "# Genesis config yaml style\n"
841 output += "# vim:ts=2:et:sw=2:ai\n"
842 output += "#\n"
843 iface_keys = [elem for elem in datadump.keys() if elem.startswith('iface_')]
844 for key in sorted(set(datadump.keys()) - set(iface_keys)):
845 output += "%-10s: %s\n" % (key, format_yaml_value(datadump[key]))
846
847 output += "\n\n"
848
849 key_order = [ 'comment', 'interface', 'ip', 'desc', 'sdesc', 'mode', 'type',
850 'extra_type', 'channel', 'ssid', 'dhcp' ]
851
852 for iface_key in sorted(iface_keys):
853 output += "%s:\n" % iface_key
854 for key in key_order + list(sorted(set(datadump[iface_key].keys()) - set(key_order))):
855 if datadump[iface_key].has_key(key):
856 output += " %-11s: %s\n" % (key, format_yaml_value(datadump[iface_key][key]))
857 output += "\n\n"
858
859 return output
860
861
862
863def generate_wleiden_yaml(datadump, header=True):
864 """ Generate (petty) version of wleiden.yaml"""
865 for key in datadump.keys():
866 if key.startswith('autogen_'):
867 del datadump[key]
868 # Interface autogen cleanups
869 elif type(datadump[key]) == dict:
870 for key2 in datadump[key].keys():
871 if key2.startswith('autogen_'):
872 del datadump[key][key2]
873
874 output = generate_header("#") if header else ''
875 output += format_wleiden_yaml(datadump)
876 return output
877
878
879def generate_yaml(datadump):
880 return generate_config(datadump['nodename'], "wleiden.yaml", datadump)
881
882
883
884def generate_config(node, config, datadump=None):
885 """ Print configuration file 'config' of 'node' """
886 output = ""
887 try:
888 # Load config file
889 if datadump == None:
890 datadump = get_yaml(node)
891
892 if config == 'wleiden.yaml':
893 output += generate_wleiden_yaml(datadump)
894 elif config == 'authorized_keys':
895 f = open(os.path.join(NODE_DIR,"global_keys"), 'r')
896 output += f.read()
897 f.close()
898 elif config == 'dnsmasq.conf':
899 output += generate_dnsmasq_conf(datadump)
900 elif config == 'dhcpd.conf':
901 output += generate_dhcpd_conf(datadump)
902 elif config == 'rc.conf.local':
903 output += generate_rc_conf_local(datadump)
904 elif config == 'resolv.conf':
905 output += generate_resolv_conf(datadump)
906 elif config == 'ntp.conf':
907 output += generate_ntp_conf(datadump)
908 elif config == 'motd':
909 output += generate_motd(datadump)
910 else:
911 assert False, "Config not found!"
912 except IOError, e:
913 output += "[ERROR] Config file not found"
914 return output
915
916
917
918def process_cgi_request():
919 """ When calling from CGI """
920 # Update repository if requested
921 form = cgi.FieldStorage()
922 if form.getvalue("action") == "update":
923 print "Refresh: 5; url=."
924 print "Content-type:text/plain\r\n\r\n",
925 print "[INFO] Updating subverion, please wait..."
926 print subprocess.Popen(['svn', 'cleanup', "%s/.." % NODE_DIR], stderr=subprocess.STDOUT, stdout=subprocess.PIPE).communicate()[0],
927 print subprocess.Popen(['svn', 'up', "%s/.." % NODE_DIR], stderr=subprocess.STDOUT, stdout=subprocess.PIPE).communicate()[0],
928 print "[INFO] All done, redirecting in 5 seconds"
929 sys.exit(0)
930
931
932 base_uri = os.environ['PATH_INFO']
933 uri = base_uri.strip('/').split('/')
934
935 output = "Template Holder"
936 content_type='text/plain'
937 if base_uri.endswith('/create/network.kml'):
938 content_type='application/vnd.google-earth.kml+xml'
939 output = make_network_kml.make_graph()
940 elif not uri[0]:
941 if is_text_request():
942 content_type = 'text/plain'
943 output = '\n'.join(get_hostlist())
944 else:
945 content_type = 'text/html'
946 output = generate_title(get_hostlist())
947 elif len(uri) == 1:
948 if is_text_request():
949 content_type = 'text/plain'
950 output = generate_node(uri[0])
951 else:
952 content_type = 'text/html'
953 output = generate_node_overview(uri[0])
954 elif len(uri) == 2:
955 content_type = 'text/plain'
956 output = generate_config(uri[0], uri[1])
957 else:
958 assert False, "Invalid option"
959
960 print "Content-Type: %s" % content_type
961 print "Content-Length: %s" % len(output)
962 print ""
963 print output
964
965def get_realname(datadump):
966 # Proxy naming convention is special, as the proxy name is also included in
967 # the nodename, when it comes to the numbered proxies.
968 if datadump['nodetype'] == 'Proxy':
969 realname = datadump['nodetype'] + datadump['nodename'].replace('proxy','')
970 else:
971 # By default the full name is listed and also a shortname CNAME for easy use.
972 realname = datadump['nodetype'] + datadump['nodename']
973 return(realname)
974
975
976
977def make_dns(output_dir = 'dns', external = False):
978 items = dict()
979
980 # hostname is key, IP is value
981 wleiden_zone = defaultdict(list)
982 wleiden_cname = dict()
983
984 pool = dict()
985 for node in get_hostlist():
986 datadump = get_yaml(node)
987
988 # Proxy naming convention is special
989 fqdn = datadump['autogen_realname']
990 if datadump['nodetype'] in ['CNode', 'Hybrid']:
991 wleiden_cname[datadump['nodename']] = fqdn
992
993 wleiden_zone[fqdn].append((datadump['masterip'], True))
994
995 # Hacking to get proper DHCP IPs and hostnames
996 for iface_key in get_interface_keys(datadump):
997 iface_name = datadump[iface_key]['interface'].replace(':',"-alias-")
998 (ip, cidr) = datadump[iface_key]['ip'].split('/')
999 try:
1000 (dhcp_start, dhcp_stop) = datadump[iface_key]['dhcp'].split('-')
1001 datadump[iface_key]['netmask'] = cidr2netmask(cidr)
1002 dhcp_part = ".".join(ip.split('.')[0:3])
1003 if ip != datadump['masterip']:
1004 wleiden_zone["dhcp-gateway-%s.%s" % (iface_name, fqdn)].append((ip, False))
1005 for i in range(int(dhcp_start), int(dhcp_stop) + 1):
1006 wleiden_zone["dhcp-%s-%s.%s" % (i, iface_name, fqdn)].append(("%s.%s" % (dhcp_part, i), True))
1007 except (AttributeError, ValueError):
1008 # First push it into a pool, to indentify the counter-part later on
1009 addr = parseaddr(ip)
1010 cidr = int(cidr)
1011 addr = addr & ~((1 << (32 - cidr)) - 1)
1012 if pool.has_key(addr):
1013 pool[addr] += [(iface_name, fqdn, ip)]
1014 else:
1015 pool[addr] = [(iface_name, fqdn, ip)]
1016 continue
1017
1018
1019 def pool_to_name(fqdn, pool_members):
1020 """Convert the joined name to a usable pool name"""
1021
1022 def isplit(item):
1023 (prefix, name, number) = re.match('^(cnode|hybrid|proxy)([a-z]+)([0-9]*)$',item.lower()).group(1,2,3)
1024 return (prefix, name, number)
1025
1026 my_name = isplit(fqdn.split('.')[0])[1]
1027
1028 short_names = defaultdict(list)
1029 for node in sorted(pool_members):
1030 (prefix, name, number) = isplit(node)
1031 short_names[name].append((prefix,number))
1032
1033 return '-'.join(sorted(short_names.keys()))
1034
1035
1036 # WL uses an /29 to configure an interface. IP's are ordered like this:
1037 # MasterA (.1) -- DeviceA (.2) <<>> DeviceB (.3) --- SlaveB (.4)
1038
1039 sn = lambda x: re.sub(r'(?i)^cnode','',x)
1040
1041 # Automatic naming convention of interlinks namely 2 + remote.lower()
1042 for (key,value) in pool.iteritems():
1043 # Make sure they are sorted from low-ip to high-ip
1044 value = sorted(value, key=lambda x: parseaddr(x[2]))
1045
1046 if len(value) == 1:
1047 (iface_name, fqdn, ip) = value[0]
1048 wleiden_zone["2unused-%s.%s" % (iface_name, fqdn)].append((ip, True))
1049
1050 # Device DNS names
1051 if 'cnode' in fqdn.lower():
1052 wleiden_zone["d-at-%s.%s" % (iface_name, fqdn)].append((showaddr(parseaddr(ip) + 1), False))
1053 wleiden_cname["d-at-%s.%s" % (iface_name,sn(fqdn))] = "d-at-%s.%s" % ((iface_name, fqdn))
1054
1055 elif len(value) == 2:
1056 (a_iface_name, a_fqdn, a_ip) = value[0]
1057 (b_iface_name, b_fqdn, b_ip) = value[1]
1058 wleiden_zone["2%s.%s" % (b_fqdn,a_fqdn)].append((a_ip, True))
1059 wleiden_zone["2%s.%s" % (a_fqdn,b_fqdn)].append((b_ip, True))
1060
1061 # Device DNS names
1062 if 'cnode' in a_fqdn.lower() and 'cnode' in b_fqdn.lower():
1063 wleiden_zone["d-at-%s.%s" % (a_iface_name, a_fqdn)].append((showaddr(parseaddr(a_ip) + 1), False))
1064 wleiden_zone["d-at-%s.%s" % (b_iface_name, b_fqdn)].append((showaddr(parseaddr(b_ip) - 1), False))
1065 wleiden_cname["d-at-%s.%s" % (a_iface_name,sn(a_fqdn))] = "d-at-%s.%s" % (a_iface_name, a_fqdn)
1066 wleiden_cname["d-at-%s.%s" % (b_iface_name,sn(b_fqdn))] = "d-at-%s.%s" % (b_iface_name, b_fqdn)
1067 wleiden_cname["d2%s.%s" % (sn(b_fqdn),sn(a_fqdn))] = "d-at-%s.%s" % (a_iface_name, a_fqdn)
1068 wleiden_cname["d2%s.%s" % (sn(a_fqdn),sn(b_fqdn))] = "d-at-%s.%s" % (b_iface_name, b_fqdn)
1069
1070 else:
1071 pool_members = [k[1] for k in value]
1072 for item in value:
1073 (iface_name, fqdn, ip) = item
1074 pool_name = "2pool-" + pool_to_name(fqdn,pool_members)
1075 wleiden_zone["%s.%s" % (pool_name, fqdn)].append((ip, True))
1076
1077 # Include static DNS entries
1078 # XXX: Should they override the autogenerated results?
1079 # XXX: Convert input to yaml more useable.
1080 # Format:
1081 ##; this is a comment
1082 ## roomburgh=CNodeRoomburgh1
1083 ## apkerk1.CNodeVosko=172.17.176.8 ;this as well
1084 dns_list = yaml.load(open(os.path.join(NODE_DIR,'../dns/staticDNS.yaml'),'r'))
1085
1086 # Hack to allow special entries, for development
1087 wleiden_raw = {}
1088
1089 for line in dns_list:
1090 reverse = False
1091 k, items = line.items()[0]
1092 if type(items) == dict:
1093 if items.has_key('reverse'):
1094 reverse = items['reverse']
1095 items = items['a']
1096 else:
1097 items = items['cname']
1098 items = [items] if type(items) != list else items
1099 for item in items:
1100 if item.startswith('IN '):
1101 wleiden_raw[k] = item
1102 elif valid_addr(item):
1103 wleiden_zone[k].append((item, reverse))
1104 else:
1105 wleiden_cname[k] = item
1106
1107 details = dict()
1108 # 24 updates a day allowed
1109 details['serial'] = time.strftime('%Y%m%d%H')
1110
1111 if external:
1112 dns_masters = ['siteview.wirelessleiden.nl', 'ns1.vanderzwet.net']
1113 else:
1114 dns_masters = ['sunny.wleiden.net']
1115
1116 details['master'] = dns_masters[0]
1117 details['ns_servers'] = '\n'.join(['\tNS\t%s.' % x for x in dns_masters])
1118
1119 dns_header = '''
1120$TTL 3h
1121%(zone)s. SOA %(master)s. beheer.lijst.wirelessleiden.nl. ( %(serial)s 1d 12h 1w 60s )
1122 ; Serial, Refresh, Retry, Expire, Neg. cache TTL
1123
1124%(ns_servers)s
1125 \n'''
1126
1127
1128 if not os.path.isdir(output_dir):
1129 os.makedirs(output_dir)
1130 details['zone'] = 'wleiden.net'
1131 f = open(os.path.join(output_dir,"db." + details['zone']), "w")
1132 f.write(dns_header % details)
1133
1134 for host,items in wleiden_zone.iteritems():
1135 for ip,reverse in items:
1136 if valid_addr(ip):
1137 f.write("%s.wleiden.net. IN A %s \n" % (host.lower(), ip))
1138 for source,dest in wleiden_cname.iteritems():
1139 f.write("%s.wleiden.net. IN CNAME %s.wleiden.net.\n" % (source.lower(), dest.lower()))
1140 for source, dest in wleiden_raw.iteritems():
1141 f.write("%s.wleiden.net. %s\n" % (source, dest))
1142 f.close()
1143
1144 # Create whole bunch of specific sub arpa zones. To keep it compliant
1145 for s in range(16,32):
1146 details['zone'] = '%i.172.in-addr.arpa' % s
1147 f = open(os.path.join(output_dir,"db." + details['zone']), "w")
1148 f.write(dns_header % details)
1149
1150 #XXX: Not effient, fix to proper data structure and do checks at other
1151 # stages
1152 for host,items in wleiden_zone.iteritems():
1153 for ip,reverse in items:
1154 if not reverse:
1155 continue
1156 if valid_addr(ip):
1157 if valid_addr(ip):
1158 if int(ip.split('.')[1]) == s:
1159 rev_ip = '.'.join(reversed(ip.split('.')))
1160 f.write("%s.in-addr.arpa. IN PTR %s.wleiden.net.\n" % (rev_ip.lower(), host.lower()))
1161 f.close()
1162
1163
1164def usage():
1165 print """Usage: %(prog)s <argument>
1166Argument:
1167\tstandalone [port] = Run configurator webserver [8000]
1168\tdns [outputdir] = Generate BIND compliant zone files in dns [./dns]
1169\tfull-export = Generate yaml export script for heatmap.
1170\tstatic [outputdir] = Generate all config files and store on disk
1171\t with format ./<outputdir>/%%NODE%%/%%FILE%% [./static]
1172\ttest <node> <file> = Receive output of CGI script.
1173\tlist <status> <items> = List systems which have certain status
1174
1175Arguments:
1176\t<node> = NodeName (example: HybridRick)
1177\t<file> = %(files)s
1178\t<status> = all|up|down|planned
1179\t<items> = systems|nodes|proxies
1180
1181NOTE FOR DEVELOPERS; you can test your changes like this:
1182 BEFORE any changes in this code:
1183 $ ./gformat.py static /tmp/pre
1184 AFTER the changes:
1185 $ ./gformat.py static /tmp/post
1186 VIEW differences and VERIFY all are OK:
1187 $ diff -urI 'Generated' -r /tmp/pre /tmp/post
1188""" % { 'prog' : sys.argv[0], 'files' : '|'.join(files) }
1189 exit(0)
1190
1191
1192def is_text_request():
1193 """ Find out whether we are calling from the CLI or any text based CLI utility """
1194 try:
1195 return os.environ['HTTP_USER_AGENT'].split()[0] in ['curl', 'fetch', 'wget']
1196 except KeyError:
1197 return True
1198
1199def switchFormat(setting):
1200 if setting:
1201 return "YES"
1202 else:
1203 return "NO"
1204
1205def main():
1206 """Hard working sub"""
1207 # Allow easy hacking using the CLI
1208 if not os.environ.has_key('PATH_INFO'):
1209 if len(sys.argv) < 2:
1210 usage()
1211
1212 if sys.argv[1] == "standalone":
1213 import SocketServer
1214 import CGIHTTPServer
1215 # Hop to the right working directory.
1216 os.chdir(os.path.dirname(__file__))
1217 try:
1218 PORT = int(sys.argv[2])
1219 except (IndexError,ValueError):
1220 PORT = 8000
1221
1222 class MyCGIHTTPRequestHandler(CGIHTTPServer.CGIHTTPRequestHandler):
1223 """ Serve this CGI from the root of the webserver """
1224 def is_cgi(self):
1225 if "favicon" in self.path:
1226 return False
1227
1228 self.cgi_info = (os.path.basename(__file__), self.path)
1229 self.path = ''
1230 return True
1231 handler = MyCGIHTTPRequestHandler
1232 SocketServer.TCPServer.allow_reuse_address = True
1233 httpd = SocketServer.TCPServer(("", PORT), handler)
1234 httpd.server_name = 'localhost'
1235 httpd.server_port = PORT
1236
1237 logger.info("serving at port %s", PORT)
1238 try:
1239 httpd.serve_forever()
1240 except KeyboardInterrupt:
1241 httpd.shutdown()
1242 logger.info("All done goodbye")
1243 elif sys.argv[1] == "test":
1244 os.environ['PATH_INFO'] = "/".join(sys.argv[2:])
1245 os.environ['SCRIPT_NAME'] = __file__
1246 process_cgi_request()
1247 elif sys.argv[1] == "static":
1248 items = dict()
1249 items['output_dir'] = sys.argv[2] if len(sys.argv) > 2 else "./static"
1250 for node in get_hostlist():
1251 items['node'] = node
1252 items['wdir'] = "%(output_dir)s/%(node)s" % items
1253 if not os.path.isdir(items['wdir']):
1254 os.makedirs(items['wdir'])
1255 datadump = get_yaml(node)
1256 for config in files:
1257 items['config'] = config
1258 logger.info("## Generating %(node)s %(config)s" % items)
1259 f = open("%(wdir)s/%(config)s" % items, "w")
1260 f.write(generate_config(node, config, datadump))
1261 f.close()
1262 elif sys.argv[1] == "wind-export":
1263 items = dict()
1264 for node in get_hostlist():
1265 datadump = get_yaml(node)
1266 sql = """INSERT IGNORE INTO nodes (name, name_ns, longitude, latitude)
1267 VALUES ('%(nodename)s', '%(nodename)s', %(latitude)s, %(longitude)s);""" % datadump;
1268 sql = """INSERT IGNORE INTO users_nodes (user_id, node_id, owner)
1269 VALUES (
1270 (SELECT id FROM users WHERE username = 'rvdzwet'),
1271 (SELECT id FROM nodes WHERE name = '%(nodename)s'),
1272 'Y');""" % datadump
1273 #for config in files:
1274 # items['config'] = config
1275 # print "## Generating %(node)s %(config)s" % items
1276 # f = open("%(wdir)s/%(config)s" % items, "w")
1277 # f.write(generate_config(node, config, datadump))
1278 # f.close()
1279 for node in get_hostlist():
1280 datadump = get_yaml(node)
1281 for iface_key in sorted([elem for elem in datadump.keys() if elem.startswith('iface_')]):
1282 ifacedump = datadump[iface_key]
1283 if ifacedump.has_key('mode') and ifacedump['mode'] == 'ap-wds':
1284 ifacedump['nodename'] = datadump['nodename']
1285 if not ifacedump.has_key('channel') or not ifacedump['channel']:
1286 ifacedump['channel'] = 0
1287 sql = """INSERT INTO links (node_id, type, ssid, protocol, channel, status)
1288 VALUES ((SELECT id FROM nodes WHERE name = '%(nodename)s'), 'ap',
1289 '%(ssid)s', 'IEEE 802.11b', %(channel)s, 'active');""" % ifacedump
1290 elif sys.argv[1] == "full-export":
1291 hosts = {}
1292 for node in get_hostlist():
1293 datadump = get_yaml(node)
1294 hosts[datadump['nodename']] = datadump
1295 print yaml.dump(hosts)
1296
1297 elif sys.argv[1] == "dns":
1298 make_dns(sys.argv[2] if len(sys.argv) > 2 else 'dns', 'external' in sys.argv)
1299 elif sys.argv[1] == "cleanup":
1300 # First generate all datadumps
1301 datadumps = dict()
1302 for host in get_hostlist():
1303 logger.info("# Processing: %s", host)
1304 # Set some boring default values
1305 datadump = { 'board' : 'UNKNOWN' }
1306 datadump.update(get_yaml(host))
1307 datadumps[datadump['autogen_realname']] = datadump
1308
1309
1310 for host,datadump in datadumps.iteritems():
1311 # Convert all yes and no to boolean values
1312 def fix_boolean(dump):
1313 for key in dump.keys():
1314 if type(dump[key]) == dict:
1315 dump[key] = fix_boolean(dump[key])
1316 elif str(dump[key]).lower() in ["yes", "true"]:
1317 dump[key] = True
1318 elif str(dump[key]).lower() in ["no", "false"]:
1319 # Compass richting no (Noord Oost) is valid input
1320 if key != "compass": dump[key] = False
1321 return dump
1322 datadump = fix_boolean(datadump)
1323
1324 if datadump['rdnap_x'] and datadump['rdnap_y']:
1325 datadump['latitude'], datadump['longitude'] = rdnap.rd2etrs(datadump['rdnap_x'], datadump['rdnap_y'])
1326 elif datadump['latitude'] and datadump['longitude']:
1327 datadump['rdnap_x'], datadump['rdnap_y'] = rdnap.etrs2rd(datadump['latitude'], datadump['longitude'])
1328
1329 if datadump['nodename'].startswith('Proxy'):
1330 datadump['nodename'] = datadump['nodename'].lower()
1331
1332 for iface_key in datadump['autogen_iface_keys']:
1333 # All our normal wireless cards are normal APs now
1334 if datadump[iface_key]['type'] in ['11a', '11b', '11g', 'wireless']:
1335 datadump[iface_key]['mode'] = 'ap'
1336 # Wireless Leiden SSID have an consistent lowercase/uppercase
1337 if datadump[iface_key].has_key('ssid'):
1338 ssid = datadump[iface_key]['ssid']
1339 prefix = 'ap-WirelessLeiden-'
1340 if ssid.lower().startswith(prefix.lower()):
1341 datadump[iface_key]['ssid'] = prefix + ssid[len(prefix)].upper() + ssid[len(prefix) + 1:]
1342 if datadump[iface_key].has_key('ns_ip') and not datadump[iface_key].has_key('mode'):
1343 datadump[iface_key]['mode'] = 'autogen-FIXME'
1344 if not datadump[iface_key].has_key('desc'):
1345 datadump[iface_key]['desc'] = 'autogen-FIXME'
1346 store_yaml(datadump)
1347 elif sys.argv[1] == "list":
1348 use_fqdn = False
1349 if len(sys.argv) < 4 or not sys.argv[2] in ["up", "down", "planned", "all"]:
1350 usage()
1351 if sys.argv[3] == "nodes":
1352 systems = get_nodelist()
1353 elif sys.argv[3] == "proxies":
1354 systems = get_proxylist()
1355 elif sys.argv[3] == "systems":
1356 systems = get_hostlist()
1357 else:
1358 usage()
1359 if len(sys.argv) > 4:
1360 if sys.argv[4] == "fqdn":
1361 use_fqdn = True
1362 else:
1363 usage()
1364
1365 for system in systems:
1366 datadump = get_yaml(system)
1367
1368 output = datadump['autogen_fqdn'] if use_fqdn else system
1369 if sys.argv[2] == "all":
1370 print output
1371 elif datadump['status'] == sys.argv[2]:
1372 print output
1373 elif sys.argv[1] == "create":
1374 if sys.argv[2] == "network.kml":
1375 print make_network_kml.make_graph()
1376 else:
1377 usage()
1378 usage()
1379 else:
1380 # Do not enable debugging for config requests as it highly clutters the output
1381 if not is_text_request():
1382 cgitb.enable()
1383 process_cgi_request()
1384
1385
1386if __name__ == "__main__":
1387 main()
Note: See TracBrowser for help on using the repository browser.