source: genesis/tools/gformat.py@ 10584

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

Revamp of the hybrid config setup of rc.conf.local to support the issues with
default routing on hybrid machines which are NOT iLeiden proxies but normal
proxies instead.

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