source: genesis/tools/gformat.py@ 10699

Last change on this file since 10699 was 10698, checked in by rick, 14 years ago

Running an ssh backdoor, needs us to have the ssh daemon know about the non-standard default route routing table.

Related-To: nodefactory#148

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