source: genesis/tools/gformat.py@ 10655

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

Allow some reverse entries, not always all of them, this highly is sometimes
highly confusing, as the reverse PTR is shown in output from traceroute and
such. Multiple names makes it look a different host all the time.

  • Property svn:executable set to *
  • Property svn:keywords set to Id
File size: 44.4 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 10655 2012-05-02 22:49:02Z 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
163def get_proxylist():
164 """Get all available proxies proxyX sorting based on X number"""
165 proxylist = sorted([os.path.basename(x) for x in glob.glob("%s/proxy*" % NODE_DIR)],
166 key=lambda name: int(''.join([c for c in name if c in string.digits])),
167 cmp=lambda x,y: x - y) + sorted([os.path.basename(x) for x in glob.glob("%s/Proxy*" % NODE_DIR)])
168 return proxylist
169
170def get_hybridlist():
171 """Get all available hybrid nodes/proxies"""
172 hybridlist = sorted([os.path.basename(x) for x in glob.glob("%s/Hybrid*" % NODE_DIR)])
173 return hybridlist
174
175
176def valid_addr(addr):
177 """ Show which address is valid in which are not """
178 return str(addr).startswith('172.')
179
180
181def get_nodelist():
182 """ Get all available nodes - sorted """
183 nodelist = sorted([os.path.basename(x) for x in glob.glob("%s/CNode*" % NODE_DIR)])
184 return nodelist
185
186def get_hostlist():
187 """ Combined hosts and proxy list"""
188 return get_nodelist() + get_proxylist() + get_hybridlist()
189
190def angle_between_points(lat1,lat2,long1,long2):
191 """
192 Return Angle in radians between two GPS coordinates
193 See: http://stackoverflow.com/questions/3809179/angle-between-2-gps-coordinates
194 """
195 dy = lat2 - lat1
196 dx = math.cos(math.pi/180*lat1)*(long2 - long1)
197 angle = math.atan2(dy,dx)
198 return angle
199
200def angle_to_cd(angle):
201 """ Return Dutch Cardinal Direction estimation in 'one digit' of radian angle """
202
203 # For easy conversion get positive degree
204 degrees = math.degrees(angle)
205 if degrees < 0:
206 360 - abs(degrees)
207
208 # Numbers can be confusing calculate from the 4 main directions
209 p = 22.5
210 if degrees < p:
211 return "n"
212 elif degrees < (90 - p):
213 return "no"
214 elif degrees < (90 + p):
215 return "o"
216 elif degrees < (180 - p):
217 return "zo"
218 elif degrees < (180 + p):
219 return "z"
220 elif degrees < (270 - p):
221 return "zw"
222 elif degrees < (270 + p):
223 return "w"
224 elif degrees < (360 - p):
225 return "nw"
226 else:
227 return "n"
228
229
230def generate_title(nodelist):
231 """ Main overview page """
232 items = {'root' : "." }
233 output = """
234<html>
235 <head>
236 <title>Wireless leiden Configurator - GFormat</title>
237 <style type="text/css">
238 th {background-color: #999999}
239 tr:nth-child(odd) {background-color: #cccccc}
240 tr:nth-child(even) {background-color: #ffffff}
241 th, td {padding: 0.1em 1em}
242 </style>
243 </head>
244 <body>
245 <center>
246 <form type="GET" action="%(root)s">
247 <input type="hidden" name="action" value="update">
248 <input type="submit" value="Update Configuration Database (SVN)">
249 </form>
250 <table>
251 <caption><h3>Wireless Leiden Configurator</h3></caption>
252 """ % items
253
254 for node in nodelist:
255 items['node'] = node
256 output += '<tr><td><a href="%(root)s/%(node)s">%(node)s</a></td>' % items
257 for config in files:
258 items['config'] = config
259 output += '<td><a href="%(root)s/%(node)s/%(config)s">%(config)s</a></td>' % items
260 output += "</tr>"
261 output += """
262 </table>
263 <hr />
264 <em>%s</em>
265 </center>
266 </body>
267</html>
268 """ % __version__
269
270 return output
271
272
273
274def generate_node(node):
275 """ Print overview of all files available for node """
276 return "\n".join(files)
277
278def generate_node_overview(host):
279 """ Print overview of all files available for node """
280 datadump = get_yaml(host)
281 params = { 'host' : host }
282 output = "<em><a href='..'>Back to overview</a></em><hr />"
283 output += "<h2>Available files:</h2><ul>"
284 for cf in files:
285 params['cf'] = cf
286 output += '<li><a href="%(host)s/%(cf)s">%(cf)s</a></li>\n' % params
287 output += "</ul>"
288
289 # Generate and connection listing
290 output += "<h2>Connected To:</h2><ul>"
291 (poel, errors) = make_relations()
292 for network, hosts in poel.iteritems():
293 if host in [x[0] for x in hosts]:
294 if len(hosts) == 1:
295 # Single not connected interface
296 continue
297 for remote,ifacedump in hosts:
298 if remote == host:
299 # This side of the interface
300 continue
301 params = { 'remote': remote, 'remote_ip' : ifacedump['ip'] }
302 output += '<li><a href="%(remote)s">%(remote)s</a> -- %(remote_ip)s</li>\n' % params
303 output += "</ul>"
304 output += "<h2>MOTD details:</h2><pre>" + generate_motd(datadump) + "</pre>"
305
306 output += "<hr /><em><a href='..'>Back to overview</a></em>"
307 return output
308
309
310def generate_header(ctag="#"):
311 return """\
312%(ctag)s
313%(ctag)s DO NOT EDIT - Automatically generated by 'gformat'
314%(ctag)s Generated at %(date)s by %(host)s
315%(ctag)s
316""" % { 'ctag' : ctag, 'date' : time.ctime(), 'host' : socket.gethostname() }
317
318
319
320def parseaddr(s):
321 """ Process IPv4 CIDR notation addr to a (binary) number """
322 f = s.split('.')
323 return (long(f[0]) << 24L) + \
324 (long(f[1]) << 16L) + \
325 (long(f[2]) << 8L) + \
326 long(f[3])
327
328
329
330def showaddr(a):
331 """ Display IPv4 addr in (dotted) CIDR notation """
332 return "%d.%d.%d.%d" % ((a >> 24) & 0xff, (a >> 16) & 0xff, (a >> 8) & 0xff, a & 0xff)
333
334
335def is_member(ip, mask, canidate):
336 """ Return True if canidate is part of ip/mask block"""
337 ip_addr = gformat.parseaddr(ip)
338 ip_canidate = gformat.parseaddr(canidate)
339 mask = int(mask)
340 ip_addr = ip_addr & ~((1 << (32 - mask)) - 1)
341 ip_canidate = ip_canidate & ~((1 << (32 - mask)) - 1)
342 return ip_addr == ip_canidate
343
344
345
346def cidr2netmask(netmask):
347 """ Given a 'netmask' return corresponding CIDR """
348 return showaddr(0xffffffff & (0xffffffff << (32 - int(netmask))))
349
350def get_network(addr, mask):
351 return showaddr(parseaddr(addr) & ~((1 << (32 - int(mask))) - 1))
352
353
354def generate_dhcpd_conf(datadump):
355 """ Generate config file '/usr/local/etc/dhcpd.conf """
356 output = generate_header()
357 output += Template("""\
358# option definitions common to all supported networks...
359option domain-name "dhcp.{{ autogen_fqdn }}";
360
361default-lease-time 600;
362max-lease-time 7200;
363
364# Use this to enble / disable dynamic dns updates globally.
365#ddns-update-style none;
366
367# If this DHCP server is the official DHCP server for the local
368# network, the authoritative directive should be uncommented.
369authoritative;
370
371# Use this to send dhcp log messages to a different log file (you also
372# have to hack syslog.conf to complete the redirection).
373log-facility local7;
374
375#
376# Interface definitions
377#
378\n""").render(datadump)
379
380 for iface_key in datadump['autogen_iface_keys']:
381 if not datadump[iface_key].has_key('comment'):
382 datadump[iface_key]['comment'] = None
383 output += "## %(interface)s - %(desc)s - %(comment)s\n" % datadump[iface_key]
384
385 (addr, mask) = datadump[iface_key]['ip'].split('/')
386 datadump[iface_key]['addr'] = addr
387 datadump[iface_key]['netmask'] = cidr2netmask(mask)
388 datadump[iface_key]['subnet'] = get_network(addr, mask)
389 try:
390 (dhcp_start, dhcp_stop) = datadump[iface_key]['dhcp'].split('-')
391 except (AttributeError, ValueError):
392 output += "subnet %(subnet)s netmask %(netmask)s {\n ### not autoritive\n}\n\n" % datadump[iface_key]
393 continue
394
395 dhcp_part = ".".join(addr.split('.')[0:3])
396 datadump[iface_key]['dhcp_start'] = dhcp_part + "." + dhcp_start
397 datadump[iface_key]['dhcp_stop'] = dhcp_part + "." + dhcp_stop
398 output += """\
399subnet %(subnet)s netmask %(netmask)s {
400 range %(dhcp_start)s %(dhcp_stop)s;
401 option routers %(addr)s;
402 option domain-name-servers %(addr)s;
403}
404\n""" % datadump[iface_key]
405
406 return output
407
408
409
410def generate_dnsmasq_conf(datadump):
411 """ Generate configuration file '/usr/local/etc/dnsmasq.conf' """
412 output = generate_header()
413 output += Template("""\
414# DHCP server options
415dhcp-authoritative
416dhcp-fqdn
417domain=dhcp.{{ autogen_fqdn }}
418domain-needed
419expand-hosts
420log-async=100
421
422# Low memory footprint
423cache-size=10000
424
425\n""").render(datadump)
426
427 for iface_key in datadump['autogen_iface_keys']:
428 if not datadump[iface_key].has_key('comment'):
429 datadump[iface_key]['comment'] = None
430 output += "## %(interface)s - %(desc)s - %(comment)s\n" % datadump[iface_key]
431
432 try:
433 (dhcp_start, dhcp_stop) = datadump[iface_key]['dhcp'].split('-')
434 (ip, cidr) = datadump[iface_key]['ip'].split('/')
435 datadump[iface_key]['netmask'] = cidr2netmask(cidr)
436 except (AttributeError, ValueError):
437 output += "# not autoritive\n\n"
438 continue
439
440 dhcp_part = ".".join(ip.split('.')[0:3])
441 datadump[iface_key]['dhcp_start'] = dhcp_part + "." + dhcp_start
442 datadump[iface_key]['dhcp_stop'] = dhcp_part + "." + dhcp_stop
443 output += "dhcp-range=%(interface)s,%(dhcp_start)s,%(dhcp_stop)s,%(netmask)s,24h\n\n" % datadump[iface_key]
444
445 return output
446
447
448
449def generate_rc_conf_local(datadump):
450 """ Generate configuration file '/etc/rc.conf.local' """
451 if not datadump.has_key('ileiden'):
452 datadump['autogen_ileiden_enable'] = False
453 else:
454 datadump['autogen_ileiden_enable'] = datadump['ileiden']
455
456 datadump['autogen_ileiden_enable'] = switchFormat(datadump['autogen_ileiden_enable'])
457
458 ileiden_proxies = []
459 normal_proxies = []
460 for proxy in get_proxylist():
461 proxydump = get_yaml(proxy)
462 if proxydump['ileiden']:
463 ileiden_proxies.append(proxydump)
464 else:
465 normal_proxies.append(proxydump)
466 for host in get_hybridlist():
467 hostdump = get_yaml(host)
468 if hostdump['service_proxy_ileiden']:
469 ileiden_proxies.append(hostdump)
470 if hostdump['service_proxy_normal']:
471 normal_proxies.append(hostdump)
472
473 datadump['autogen_ileiden_proxies'] = ileiden_proxies
474 datadump['autogen_normal_proxies'] = normal_proxies
475 datadump['autogen_ileiden_proxies_ips'] = ','.join([x['masterip'] for x in ileiden_proxies])
476 datadump['autogen_ileiden_proxies_names'] = ','.join([x['autogen_item'] for x in ileiden_proxies])
477 datadump['autogen_normal_proxies_ips'] = ','.join([x['masterip'] for x in normal_proxies])
478 datadump['autogen_normal_proxies_names'] = ','.join([x['autogen_item'] for x in normal_proxies])
479
480 output = generate_header("#");
481 output += render_template(datadump, """\
482hostname='{{ autogen_fqdn }}'
483location='{{ location }}'
484nodetype="{{ nodetype }}"
485
486#
487# Configured listings
488#
489captive_portal_whitelist=""
490{% if nodetype == "Proxy" %}
491#
492# Proxy Configuration
493#
494{% if gateway -%}
495defaultrouter="{{ gateway }}"
496{% else -%}
497#defaultrouter="NOTSET"
498{% endif -%}
499internalif="{{ internalif }}"
500ileiden_enable="{{ autogen_ileiden_enable }}"
501gateway_enable="{{ autogen_ileiden_enable }}"
502pf_enable="yes"
503pf_rules="/etc/pf.conf"
504{% if autogen_ileiden_enable -%}
505pf_flags="-D ext_if={{ externalif }} -D int_if={{ internalif }} -D publicnat={80,443}"
506lvrouted_enable="{{ autogen_ileiden_enable }}"
507lvrouted_flags="-u -s s00p3rs3kr3t -m 28"
508{% else -%}
509pf_flags="-D ext_if={{ externalif }} -D int_if={{ internalif }} -D publicnat={0}"
510{% endif -%}
511{% if internalroute -%}
512static_routes="wleiden"
513route_wleiden="-net 172.16.0.0/12 {{ internalroute }}"
514{% endif -%}
515
516{% elif nodetype == "Hybrid" %}
517 #
518 # Hybrid Configuration
519 #
520 list_ileiden_proxies="
521 {% for item in autogen_ileiden_proxies -%}
522 {{ "%-16s"|format(item.masterip) }} # {{ item.autogen_realname }}
523 {% endfor -%}
524 "
525 list_normal_proxies="
526 {% for item in autogen_normal_proxies -%}
527 {{ "%-16s"|format(item.masterip) }} # {{ item.autogen_realname }}
528 {% endfor -%}
529 "
530
531 captive_portal_interfaces="{{ autogen_dhcp_interfaces|default('none', true) }}"
532 externalif="{{ externalif|default('vr0', true) }}"
533 masterip="{{ masterip }}"
534
535 # Defined services
536 service_proxy_ileiden="{{ service_proxy_ileiden|yesorno }}"
537 service_proxy_normal="{{ service_proxy_normal|yesorno }}"
538 service_accesspoint="{{ service_accesspoint|yesorno }}"
539 #
540
541 {% if service_proxy_ileiden %}
542 pf_rules="/etc/pf.hybrid.conf"
543 pf_flags="-D ext_if=$externalif -D ext_if_net=$externalif:network -D masterip=$masterip"
544 pf_flags="$pf_flags -D publicnat=80,443"
545 {% elif service_proxy_normal %}
546 pf_rules="/etc/pf.hybrid.conf"
547 pf_flags="-D ext_if=$externalif -D ext_if_net=$externalif:network -D masterip=$masterip"
548 pf_flags="$pf_flags -D publicnat=0"
549 lvrouted_flags="$lvrouted_flags -z `make_list "$list_ileiden_proxies" ","`"
550 named_setfib="1"
551 tinyproxy_setfib="1"
552 dnsmasq_setfib="1"
553 {% else %}
554 pf_rules="/etc/pf.node.conf"
555 pf_flags=""
556 {% endif %}
557
558 {% if service_proxy_normal %}
559 tinyproxy_enable="yes"
560 {% else %}
561 pen_wrapper_enable="yes"
562 {% endif %}
563
564 {% if service_accesspoint %}
565 pf_flags="$pf_flags -D captive_portal_interfaces=$captive_portal_interfaces"
566 {% endif %}
567
568 {% if board == "ALIX2" %}
569 #
570 # ''Fat'' configuration, board has 256MB RAM
571 #
572 dnsmasq_enable="NO"
573 named_enable="YES"
574 dhcpd_enable="YES"
575 {% endif -%}
576
577 {% if service_proxy_ileiden and gateway %}
578 defaultrouter="{{ gateway }}"
579 {% endif %}
580{% elif nodetype == "CNode" %}
581#
582# NODE iLeiden Configuration
583#
584
585# iLeiden Proxies {{ autogen_ileiden_proxies_names }}
586list_ileiden_proxies="{{ autogen_ileiden_proxies_ips }}"
587# normal Proxies {{ autogen_normal_proxies_names }}
588list_normal_proxies="{{ autogen_normal_proxies_ips }}"
589
590captive_portal_interfaces="{{ autogen_dhcp_interfaces }}"
591
592{% if tproxy -%}
593tproxy_enable='YES'
594tproxy_range='{{ tproxy }}'
595{% else -%}
596tproxy_enable='NO'
597{% endif -%}
598
599lvrouted_flags="-u -s s00p3rs3kr3t -m 28 -z $list_ileiden_proxies"
600{% endif %}
601
602#
603# Interface definitions
604#\n
605""")
606
607 # lo0 configuration:
608 # - 172.32.255.1/32 is the proxy.wleiden.net deflector
609 # - masterip is special as it needs to be assigned to at
610 # least one interface, so if not used assign to lo0
611 addrs_list = { 'lo0' : [("127.0.0.1/8", "LocalHost"), ("172.31.255.1/32","Proxy IP")] }
612 iface_map = {'lo0' : 'lo0'}
613 dhclient_if = {'lo0' : False}
614
615 masterip_used = False
616 for iface_key in datadump['autogen_iface_keys']:
617 if datadump[iface_key]['ip'].startswith(datadump['masterip']):
618 masterip_used = True
619 break
620 if not masterip_used:
621 addrs_list['lo0'].append((datadump['masterip'] + "/32", 'Master IP Not used in interface'))
622
623 for iface_key in datadump['autogen_iface_keys']:
624 ifacedump = datadump[iface_key]
625 ifname = ifacedump['autogen_ifname']
626
627 # Flag dhclient is possible
628 dhclient_if[ifname] = ifacedump.has_key('dhcpclient') and ifacedump['dhcpclient']
629
630 # Add interface IP to list
631 item = (ifacedump['ip'], ifacedump['desc'])
632 if addrs_list.has_key(ifname):
633 addrs_list[ifname].append(item)
634 else:
635 addrs_list[ifname] = [item]
636
637 # Alias only needs IP assignment for now, this might change if we
638 # are going to use virtual accesspoints
639 if "alias" in iface_key:
640 continue
641
642 # XXX: Might want to deduct type directly from interface name
643 if ifacedump['type'] in ['11a', '11b', '11g', 'wireless']:
644 # Default to station (client) mode
645 ifacedump['wlanmode'] = "sta"
646 if ifacedump['mode'] in ['master', 'master-wds', 'ap', 'ap-wds']:
647 ifacedump['wlanmode'] = "ap"
648 # Default to 802.11b mode
649 ifacedump['mode'] = '11b'
650 if ifacedump['type'] in ['11a', '11b' '11g']:
651 ifacedump['mode'] = ifacedump['type']
652
653 if not ifacedump.has_key('channel'):
654 if ifacedump['type'] == '11a':
655 ifacedump['channel'] = 36
656 else:
657 ifacedump['channel'] = 1
658
659 # Allow special hacks at the back like wds and stuff
660 if not ifacedump.has_key('extra'):
661 ifacedump['extra'] = 'regdomain ETSI country NL'
662
663 output += "wlans_%(interface)s='%(autogen_ifname)s'\n" % ifacedump
664 output += ("create_args_%(autogen_ifname)s='wlanmode %(wlanmode)s mode " +\
665 "%(mode)s ssid %(ssid)s %(extra)s channel %(channel)s'\n") % ifacedump
666
667 elif ifacedump['type'] in ['ethernet', 'eth']:
668 # No special config needed besides IP
669 pass
670 else:
671 assert False, "Unknown type " + ifacedump['type']
672
673 # Print IP address which needs to be assigned over here
674 output += "\n"
675 for iface,addrs in sorted(addrs_list.iteritems()):
676 for addr, comment in sorted(addrs,key=lambda x: parseaddr(x[0].split('/')[0])):
677 output += "# %s || %s || %s\n" % (iface, addr, comment)
678
679 # Write DHCLIENT entry
680 if dhclient_if[iface]:
681 output += "ifconfig_%s='SYNCDHCP'\n\n" % (iface)
682 else:
683 output += "ipv4_addrs_%s='%s'\n\n" % (iface, " ".join([x[0] for x in addrs]))
684
685 return output
686
687
688
689
690def get_all_configs():
691 """ Get dict with key 'host' with all configs present """
692 configs = dict()
693 for host in get_hostlist():
694 datadump = get_yaml(host)
695 configs[host] = datadump
696 return configs
697
698
699def get_interface_keys(config):
700 """ Quick hack to get all interface keys, later stage convert this to a iterator """
701 return sorted([elem for elem in config.keys() if (elem.startswith('iface_') and not "lo0" in elem)])
702
703
704def get_used_ips(configs):
705 """ Return array of all IPs used in config files"""
706 ip_list = []
707 for config in configs:
708 ip_list.append(config['masterip'])
709 for iface_key in get_interface_keys(config):
710 l = config[iface_key]['ip']
711 addr, mask = l.split('/')
712 # Special case do not process
713 if valid_addr(addr):
714 ip_list.append(addr)
715 else:
716 logger.error("## IP '%s' in '%s' not valid" % (addr, config['nodename']))
717 return sorted(ip_list)
718
719
720
721def generate_resolv_conf(datadump):
722 """ Generate configuration file '/etc/resolv.conf' """
723 # XXX: This should properly going to be an datastructure soon
724 datadump['autogen_header'] = generate_header("#")
725 datadump['autogen_edge_nameservers'] = ''
726 for host in get_proxylist():
727 hostdump = get_yaml(host)
728 datadump['autogen_edge_nameservers'] += "nameserver %(masterip)-15s # %(autogen_realname)s\n" % hostdump
729 for host in get_hybridlist():
730 hostdump = get_yaml(host)
731 if hostdump['service_proxy_ileiden'] or hostdump['service_proxy_normal']:
732 datadump['autogen_edge_nameservers'] += "nameserver %(masterip)-15s # %(autogen_realname)s\n" % hostdump
733
734 return Template("""\
735{{ autogen_header }}
736search wleiden.net
737
738# Try local (cache) first
739nameserver 127.0.0.1
740
741{% if service_proxy_normal or service_proxy_ileiden or nodetype == 'Proxy' -%}
742nameserver 8.8.8.8 # Google Public NameServer
743nameserver 8.8.4.4 # Google Public NameServer
744{% else -%}
745# START DYNAMIC LIST - updated by /tools/nameserver-shuffle
746{{ autogen_edge_nameservers }}
747{% endif -%}
748""").render(datadump)
749
750
751
752def generate_ntp_conf(datadump):
753 """ Generate configuration file '/etc/ntp.conf' """
754 # XXX: This should properly going to be an datastructure soon
755
756 datadump['autogen_header'] = generate_header("#")
757 datadump['autogen_ntp_servers'] = ''
758 for host in get_proxylist():
759 hostdump = get_yaml(host)
760 datadump['autogen_ntp_servers'] += "server %(masterip)-15s iburst maxpoll 9 # %(autogen_realname)s\n" % hostdump
761 for host in get_hybridlist():
762 hostdump = get_yaml(host)
763 if hostdump['service_proxy_ileiden'] or hostdump['service_proxy_normal']:
764 datadump['autogen_ntp_servers'] += "server %(masterip)-15s iburst maxpoll 9 # %(autogen_realname)s\n" % hostdump
765
766 return Template("""\
767{{ autogen_header }}
768
769{% if service_proxy_normal or service_proxy_ileiden or nodetype == 'Proxy' -%}
770# Machine hooked to internet.
771server 0.nl.pool.ntp.org iburst maxpoll 9
772server 1.nl.pool.ntp.org iburst maxpoll 9
773server 2.nl.pool.ntp.org iburst maxpoll 9
774server 3.nl.pool.ntp.org iburst maxpoll 9
775{% else -%}
776# Local Wireless Leiden NTP Servers.
777server 0.pool.ntp.wleiden.net iburst maxpoll 9
778server 1.pool.ntp.wleiden.net iburst maxpoll 9
779server 2.pool.ntp.wleiden.net iburst maxpoll 9
780server 3.pool.ntp.wleiden.net iburst maxpoll 9
781
782# All the configured NTP servers
783{{ autogen_ntp_servers }}
784{% endif %}
785
786# If a server loses sync with all upstream servers, NTP clients
787# no longer follow that server. The local clock can be configured
788# to provide a time source when this happens, but it should usually
789# be configured on just one server on a network. For more details see
790# http://support.ntp.org/bin/view/Support/UndisciplinedLocalClock
791# The use of Orphan Mode may be preferable.
792#
793server 127.127.1.0
794fudge 127.127.1.0 stratum 10
795""").render(datadump)
796
797
798
799def generate_motd(datadump):
800 """ Generate configuration file '/etc/motd' """
801 output = Template("""\
802FreeBSD run ``service motd onestart'' to make me look normal
803
804 WWW: {{ autogen_fqdn }} - http://www.wirelessleiden.nl
805 Loc: {{ location }}
806
807Services:
808{% if board == "ALIX2" -%}
809 - Core Node
810{% else -%}
811 - Hulp Node
812{% endif -%}
813{% if service_proxy_normal -%}
814 - Normal Proxy
815{% endif -%}
816{% if service_proxy_ileiden -%}
817 - iLeiden Proxy
818{% endif %}
819Interlinks:\n
820""").render(datadump)
821
822 # XXX: This is a hacky way to get the required data
823 for line in generate_rc_conf_local(datadump).split('\n'):
824 if '||' in line and not line[1:].split()[0] in ['lo0', 'ath0'] :
825 output += " - %s \n" % line[1:]
826 output += """\
827Attached bridges:
828"""
829 for iface_key in datadump['autogen_iface_keys']:
830 ifacedump = datadump[iface_key]
831 if ifacedump.has_key('ns_ip'):
832 output += " - %(interface)s || %(mode)s || %(ns_ip)s\n" % ifacedump
833
834 return output
835
836
837def format_yaml_value(value):
838 """ Get yaml value in right syntax for outputting """
839 if isinstance(value,str):
840 output = '"%s"' % value
841 else:
842 output = value
843 return output
844
845
846
847def format_wleiden_yaml(datadump):
848 """ Special formatting to ensure it is editable"""
849 output = "# Genesis config yaml style\n"
850 output += "# vim:ts=2:et:sw=2:ai\n"
851 output += "#\n"
852 iface_keys = [elem for elem in datadump.keys() if elem.startswith('iface_')]
853 for key in sorted(set(datadump.keys()) - set(iface_keys)):
854 output += "%-10s: %s\n" % (key, format_yaml_value(datadump[key]))
855
856 output += "\n\n"
857
858 key_order = [ 'comment', 'interface', 'ip', 'desc', 'sdesc', 'mode', 'type',
859 'extra_type', 'channel', 'ssid', 'dhcp' ]
860
861 for iface_key in sorted(iface_keys):
862 output += "%s:\n" % iface_key
863 for key in key_order + list(sorted(set(datadump[iface_key].keys()) - set(key_order))):
864 if datadump[iface_key].has_key(key):
865 output += " %-11s: %s\n" % (key, format_yaml_value(datadump[iface_key][key]))
866 output += "\n\n"
867
868 return output
869
870
871
872def generate_wleiden_yaml(datadump, header=True):
873 """ Generate (petty) version of wleiden.yaml"""
874 for key in datadump.keys():
875 if key.startswith('autogen_'):
876 del datadump[key]
877 # Interface autogen cleanups
878 elif type(datadump[key]) == dict:
879 for key2 in datadump[key].keys():
880 if key2.startswith('autogen_'):
881 del datadump[key][key2]
882
883 output = generate_header("#") if header else ''
884 output += format_wleiden_yaml(datadump)
885 return output
886
887
888def generate_yaml(datadump):
889 return generate_config(datadump['nodename'], "wleiden.yaml", datadump)
890
891
892
893def generate_config(node, config, datadump=None):
894 """ Print configuration file 'config' of 'node' """
895 output = ""
896 try:
897 # Load config file
898 if datadump == None:
899 datadump = get_yaml(node)
900
901 if config == 'wleiden.yaml':
902 output += generate_wleiden_yaml(datadump)
903 elif config == 'authorized_keys':
904 f = open(os.path.join(NODE_DIR,"global_keys"), 'r')
905 output += f.read()
906 f.close()
907 elif config == 'dnsmasq.conf':
908 output += generate_dnsmasq_conf(datadump)
909 elif config == 'dhcpd.conf':
910 output += generate_dhcpd_conf(datadump)
911 elif config == 'rc.conf.local':
912 output += generate_rc_conf_local(datadump)
913 elif config == 'resolv.conf':
914 output += generate_resolv_conf(datadump)
915 elif config == 'ntp.conf':
916 output += generate_ntp_conf(datadump)
917 elif config == 'motd':
918 output += generate_motd(datadump)
919 else:
920 assert False, "Config not found!"
921 except IOError, e:
922 output += "[ERROR] Config file not found"
923 return output
924
925
926
927def process_cgi_request():
928 """ When calling from CGI """
929 # Update repository if requested
930 form = cgi.FieldStorage()
931 if form.getvalue("action") == "update":
932 print "Refresh: 5; url=."
933 print "Content-type:text/plain\r\n\r\n",
934 print "[INFO] Updating subverion, please wait..."
935 print subprocess.Popen(['svn', 'cleanup', "%s/.." % NODE_DIR], stderr=subprocess.STDOUT, stdout=subprocess.PIPE).communicate()[0],
936 print subprocess.Popen(['svn', 'up', "%s/.." % NODE_DIR], stderr=subprocess.STDOUT, stdout=subprocess.PIPE).communicate()[0],
937 print "[INFO] All done, redirecting in 5 seconds"
938 sys.exit(0)
939
940
941 base_uri = os.environ['PATH_INFO']
942 uri = base_uri.strip('/').split('/')
943
944 output = ""
945 if base_uri.endswith('/create/network.kml'):
946 output += "Content-type:application/vnd.google-earth.kml+xml\r\n\r\n"
947 output += make_network_kml.make_graph()
948 elif not uri[0]:
949 if is_text_request():
950 output += "Content-type:text/plain\r\n\r\n"
951 output += '\n'.join(get_hostlist())
952 else:
953 output += "Content-type:text/html\r\n\r\n"
954 output += generate_title(get_hostlist())
955 elif len(uri) == 1:
956 if is_text_request():
957 output += "Content-type:text/plain\r\n\r\n"
958 output += generate_node(uri[0])
959 else:
960 output += "Content-type:text/html\r\n\r\n"
961 output += generate_node_overview(uri[0])
962 elif len(uri) == 2:
963 output += "Content-type:text/plain\r\n\r\n"
964 output += generate_config(uri[0], uri[1])
965 else:
966 assert False, "Invalid option"
967 print output
968
969def get_realname(datadump):
970 # Proxy naming convention is special, as the proxy name is also included in
971 # the nodename, when it comes to the numbered proxies.
972 if datadump['nodetype'] == 'Proxy':
973 realname = datadump['nodetype'] + datadump['nodename'].replace('proxy','')
974 else:
975 # By default the full name is listed and also a shortname CNAME for easy use.
976 realname = datadump['nodetype'] + datadump['nodename']
977 return(realname)
978
979
980
981def make_dns(output_dir = 'dns', external = False):
982 items = dict()
983
984 # hostname is key, IP is value
985 wleiden_zone = defaultdict(list)
986 wleiden_cname = dict()
987
988 pool = dict()
989 for node in get_hostlist():
990 datadump = get_yaml(node)
991
992 # Proxy naming convention is special
993 fqdn = datadump['autogen_realname']
994 if datadump['nodetype'] in ['CNode', 'Hybrid']:
995 wleiden_cname[datadump['nodename']] = fqdn
996
997 wleiden_zone[fqdn].append((datadump['masterip'], True))
998
999 # Hacking to get proper DHCP IPs and hostnames
1000 for iface_key in get_interface_keys(datadump):
1001 iface_name = datadump[iface_key]['interface'].replace(':',"-alias-")
1002 (ip, cidr) = datadump[iface_key]['ip'].split('/')
1003 try:
1004 (dhcp_start, dhcp_stop) = datadump[iface_key]['dhcp'].split('-')
1005 datadump[iface_key]['netmask'] = cidr2netmask(cidr)
1006 dhcp_part = ".".join(ip.split('.')[0:3])
1007 if ip != datadump['masterip']:
1008 wleiden_zone["dhcp-gateway-%s.%s" % (iface_name, fqdn)].append((ip, False))
1009 for i in range(int(dhcp_start), int(dhcp_stop) + 1):
1010 wleiden_zone["dhcp-%s-%s.%s" % (i, iface_name, fqdn)].append(("%s.%s" % (dhcp_part, i), True))
1011 except (AttributeError, ValueError):
1012 # First push it into a pool, to indentify the counter-part later on
1013 addr = parseaddr(ip)
1014 cidr = int(cidr)
1015 addr = addr & ~((1 << (32 - cidr)) - 1)
1016 if pool.has_key(addr):
1017 pool[addr] += [(iface_name, fqdn, ip)]
1018 else:
1019 pool[addr] = [(iface_name, fqdn, ip)]
1020 continue
1021
1022
1023 def pool_to_name(node, pool_members):
1024 """Convert the joined name to a usable pool name"""
1025
1026 # Get rid of the own entry
1027 pool_members = list(set(pool_members) - set([fqdn]))
1028
1029 target = oldname = ''
1030 for node in sorted(pool_members):
1031 (name, number) = re.match('^([A-Za-z]+)([0-9]*)$',node).group(1,2)
1032 target += "-" + number if name == oldname else "-" + node if target else node
1033 oldname = name
1034
1035 return target
1036
1037
1038 # WL uses an /29 to configure an interface. IP's are ordered like this:
1039 # MasterA (.1) -- DeviceA (.2) <<>> DeviceB (.3) --- SlaveB (.4)
1040
1041 sn = lambda x: re.sub(r'(?i)^cnode','',x)
1042
1043 # Automatic naming convention of interlinks namely 2 + remote.lower()
1044 for (key,value) in pool.iteritems():
1045 # Make sure they are sorted from low-ip to high-ip
1046 value = sorted(value, key=lambda x: parseaddr(x[2]))
1047
1048 if len(value) == 1:
1049 (iface_name, fqdn, ip) = value[0]
1050 wleiden_zone["2unused-%s.%s" % (iface_name, fqdn)].append((ip, True))
1051
1052 # Device DNS names
1053 if 'cnode' in fqdn.lower():
1054 wleiden_zone["d-at-%s.%s" % (iface_name, fqdn)].append((showaddr(parseaddr(ip) + 1), False))
1055 wleiden_cname["d-at-%s.%s" % (iface_name,sn(fqdn))] = "d-at-%s.%s" % ((iface_name, fqdn))
1056
1057 elif len(value) == 2:
1058 (a_iface_name, a_fqdn, a_ip) = value[0]
1059 (b_iface_name, b_fqdn, b_ip) = value[1]
1060 wleiden_zone["2%s.%s" % (b_fqdn,a_fqdn)].append((a_ip, True))
1061 wleiden_zone["2%s.%s" % (a_fqdn,b_fqdn)].append((b_ip, True))
1062
1063 # Device DNS names
1064 if 'cnode' in a_fqdn.lower() and 'cnode' in b_fqdn.lower():
1065 wleiden_zone["d-at-%s.%s" % (a_iface_name, a_fqdn)].append((showaddr(parseaddr(a_ip) + 1), False))
1066 wleiden_zone["d-at-%s.%s" % (b_iface_name, b_fqdn)].append((showaddr(parseaddr(b_ip) - 1), False))
1067 wleiden_cname["d-at-%s.%s" % (a_iface_name,sn(a_fqdn))] = "d-at-%s.%s" % (a_iface_name, a_fqdn)
1068 wleiden_cname["d-at-%s.%s" % (b_iface_name,sn(b_fqdn))] = "d-at-%s.%s" % (b_iface_name, b_fqdn)
1069 wleiden_cname["d2%s.%s" % (sn(b_fqdn),sn(a_fqdn))] = "d-at-%s.%s" % (a_iface_name, a_fqdn)
1070 wleiden_cname["d2%s.%s" % (sn(a_fqdn),sn(b_fqdn))] = "d-at-%s.%s" % (b_iface_name, b_fqdn)
1071
1072 else:
1073 pool_members = [k[1] for k in value]
1074 for item in value:
1075 (iface_name, fqdn, ip) = item
1076 pool_name = "2pool-" + showaddr(key).replace('.','-') + "-" + pool_to_name(fqdn,pool_members)
1077 wleiden_zone["%s.%s" % (pool_name, fqdn)].append((ip, True))
1078
1079 # Include static DNS entries
1080 # XXX: Should they override the autogenerated results?
1081 # XXX: Convert input to yaml more useable.
1082 # Format:
1083 ##; this is a comment
1084 ## roomburgh=CNodeRoomburgh1
1085 ## apkerk1.CNodeVosko=172.17.176.8 ;this as well
1086 dns_list = yaml.load(open(os.path.join(NODE_DIR,'../dns/staticDNS.yaml'),'r'))
1087
1088 # Hack to allow special entries, for development
1089 wleiden_raw = {}
1090
1091 for line in dns_list:
1092 k, items = line.items()[0]
1093 items = [items] if type(items) == str 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, False))
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 3h )
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.