source: genesis/tools/gformat.py@ 10656

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

Make those very long pool names more understandable.

  • Property svn:executable set to *
  • Property svn:keywords set to Id
File size: 44.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 10656 2012-05-02 23:06:50Z 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(fqdn, pool_members):
1024 """Convert the joined name to a usable pool name"""
1025
1026 def isplit(item):
1027 (prefix, name, number) = re.match('^(cnode|hybrid|proxy)([a-z]+)([0-9]*)$',item.lower()).group(1,2,3)
1028 return (prefix, name, number)
1029
1030 my_name = isplit(fqdn.split('.')[0])[1]
1031
1032 short_names = defaultdict(list)
1033 for node in sorted(pool_members):
1034 (prefix, name, number) = isplit(node)
1035 short_names[name].append((prefix,number))
1036
1037 return '-'.join(sorted(short_names.keys()))
1038
1039
1040 # WL uses an /29 to configure an interface. IP's are ordered like this:
1041 # MasterA (.1) -- DeviceA (.2) <<>> DeviceB (.3) --- SlaveB (.4)
1042
1043 sn = lambda x: re.sub(r'(?i)^cnode','',x)
1044
1045 # Automatic naming convention of interlinks namely 2 + remote.lower()
1046 for (key,value) in pool.iteritems():
1047 # Make sure they are sorted from low-ip to high-ip
1048 value = sorted(value, key=lambda x: parseaddr(x[2]))
1049
1050 if len(value) == 1:
1051 (iface_name, fqdn, ip) = value[0]
1052 wleiden_zone["2unused-%s.%s" % (iface_name, fqdn)].append((ip, True))
1053
1054 # Device DNS names
1055 if 'cnode' in fqdn.lower():
1056 wleiden_zone["d-at-%s.%s" % (iface_name, fqdn)].append((showaddr(parseaddr(ip) + 1), False))
1057 wleiden_cname["d-at-%s.%s" % (iface_name,sn(fqdn))] = "d-at-%s.%s" % ((iface_name, fqdn))
1058
1059 elif len(value) == 2:
1060 (a_iface_name, a_fqdn, a_ip) = value[0]
1061 (b_iface_name, b_fqdn, b_ip) = value[1]
1062 wleiden_zone["2%s.%s" % (b_fqdn,a_fqdn)].append((a_ip, True))
1063 wleiden_zone["2%s.%s" % (a_fqdn,b_fqdn)].append((b_ip, True))
1064
1065 # Device DNS names
1066 if 'cnode' in a_fqdn.lower() and 'cnode' in b_fqdn.lower():
1067 wleiden_zone["d-at-%s.%s" % (a_iface_name, a_fqdn)].append((showaddr(parseaddr(a_ip) + 1), False))
1068 wleiden_zone["d-at-%s.%s" % (b_iface_name, b_fqdn)].append((showaddr(parseaddr(b_ip) - 1), False))
1069 wleiden_cname["d-at-%s.%s" % (a_iface_name,sn(a_fqdn))] = "d-at-%s.%s" % (a_iface_name, a_fqdn)
1070 wleiden_cname["d-at-%s.%s" % (b_iface_name,sn(b_fqdn))] = "d-at-%s.%s" % (b_iface_name, b_fqdn)
1071 wleiden_cname["d2%s.%s" % (sn(b_fqdn),sn(a_fqdn))] = "d-at-%s.%s" % (a_iface_name, a_fqdn)
1072 wleiden_cname["d2%s.%s" % (sn(a_fqdn),sn(b_fqdn))] = "d-at-%s.%s" % (b_iface_name, b_fqdn)
1073
1074 else:
1075 pool_members = [k[1] for k in value]
1076 for item in value:
1077 (iface_name, fqdn, ip) = item
1078 pool_name = "2pool-" + pool_to_name(fqdn,pool_members)
1079 wleiden_zone["%s.%s" % (pool_name, fqdn)].append((ip, True))
1080
1081 # Include static DNS entries
1082 # XXX: Should they override the autogenerated results?
1083 # XXX: Convert input to yaml more useable.
1084 # Format:
1085 ##; this is a comment
1086 ## roomburgh=CNodeRoomburgh1
1087 ## apkerk1.CNodeVosko=172.17.176.8 ;this as well
1088 dns_list = yaml.load(open(os.path.join(NODE_DIR,'../dns/staticDNS.yaml'),'r'))
1089
1090 # Hack to allow special entries, for development
1091 wleiden_raw = {}
1092
1093 for line in dns_list:
1094 k, items = line.items()[0]
1095 items = [items] if type(items) == str else items
1096 for item in items:
1097 if item.startswith('IN '):
1098 wleiden_raw[k] = item
1099 elif valid_addr(item):
1100 wleiden_zone[k].append((item, False))
1101 else:
1102 wleiden_cname[k] = item
1103
1104 details = dict()
1105 # 24 updates a day allowed
1106 details['serial'] = time.strftime('%Y%m%d%H')
1107
1108 if external:
1109 dns_masters = ['siteview.wirelessleiden.nl', 'ns1.vanderzwet.net']
1110 else:
1111 dns_masters = ['sunny.wleiden.net']
1112
1113 details['master'] = dns_masters[0]
1114 details['ns_servers'] = '\n'.join(['\tNS\t%s.' % x for x in dns_masters])
1115
1116 dns_header = '''
1117$TTL 3h
1118%(zone)s. SOA %(master)s. beheer.lijst.wirelessleiden.nl. ( %(serial)s 1d 12h 1w 3h )
1119 ; Serial, Refresh, Retry, Expire, Neg. cache TTL
1120
1121%(ns_servers)s
1122 \n'''
1123
1124
1125 if not os.path.isdir(output_dir):
1126 os.makedirs(output_dir)
1127 details['zone'] = 'wleiden.net'
1128 f = open(os.path.join(output_dir,"db." + details['zone']), "w")
1129 f.write(dns_header % details)
1130
1131 for host,items in wleiden_zone.iteritems():
1132 for ip,reverse in items:
1133 if valid_addr(ip):
1134 f.write("%s.wleiden.net. IN A %s \n" % (host.lower(), ip))
1135 for source,dest in wleiden_cname.iteritems():
1136 f.write("%s.wleiden.net. IN CNAME %s.wleiden.net.\n" % (source.lower(), dest.lower()))
1137 for source, dest in wleiden_raw.iteritems():
1138 f.write("%s.wleiden.net. %s\n" % (source, dest))
1139 f.close()
1140
1141 # Create whole bunch of specific sub arpa zones. To keep it compliant
1142 for s in range(16,32):
1143 details['zone'] = '%i.172.in-addr.arpa' % s
1144 f = open(os.path.join(output_dir,"db." + details['zone']), "w")
1145 f.write(dns_header % details)
1146
1147 #XXX: Not effient, fix to proper data structure and do checks at other
1148 # stages
1149 for host,items in wleiden_zone.iteritems():
1150 for ip,reverse in items:
1151 if not reverse:
1152 continue
1153 if valid_addr(ip):
1154 if valid_addr(ip):
1155 if int(ip.split('.')[1]) == s:
1156 rev_ip = '.'.join(reversed(ip.split('.')))
1157 f.write("%s.in-addr.arpa. IN PTR %s.wleiden.net.\n" % (rev_ip.lower(), host.lower()))
1158 f.close()
1159
1160
1161def usage():
1162 print """Usage: %(prog)s <argument>
1163Argument:
1164\tstandalone [port] = Run configurator webserver [8000]
1165\tdns [outputdir] = Generate BIND compliant zone files in dns [./dns]
1166\tfull-export = Generate yaml export script for heatmap.
1167\tstatic [outputdir] = Generate all config files and store on disk
1168\t with format ./<outputdir>/%%NODE%%/%%FILE%% [./static]
1169\ttest <node> <file> = Receive output of CGI script.
1170\tlist <status> <items> = List systems which have certain status
1171
1172Arguments:
1173\t<node> = NodeName (example: HybridRick)
1174\t<file> = %(files)s
1175\t<status> = all|up|down|planned
1176\t<items> = systems|nodes|proxies
1177
1178NOTE FOR DEVELOPERS; you can test your changes like this:
1179 BEFORE any changes in this code:
1180 $ ./gformat.py static /tmp/pre
1181 AFTER the changes:
1182 $ ./gformat.py static /tmp/post
1183 VIEW differences and VERIFY all are OK:
1184 $ diff -urI 'Generated' -r /tmp/pre /tmp/post
1185""" % { 'prog' : sys.argv[0], 'files' : '|'.join(files) }
1186 exit(0)
1187
1188
1189def is_text_request():
1190 """ Find out whether we are calling from the CLI or any text based CLI utility """
1191 try:
1192 return os.environ['HTTP_USER_AGENT'].split()[0] in ['curl', 'fetch', 'wget']
1193 except KeyError:
1194 return True
1195
1196def switchFormat(setting):
1197 if setting:
1198 return "YES"
1199 else:
1200 return "NO"
1201
1202def main():
1203 """Hard working sub"""
1204 # Allow easy hacking using the CLI
1205 if not os.environ.has_key('PATH_INFO'):
1206 if len(sys.argv) < 2:
1207 usage()
1208
1209 if sys.argv[1] == "standalone":
1210 import SocketServer
1211 import CGIHTTPServer
1212 # Hop to the right working directory.
1213 os.chdir(os.path.dirname(__file__))
1214 try:
1215 PORT = int(sys.argv[2])
1216 except (IndexError,ValueError):
1217 PORT = 8000
1218
1219 class MyCGIHTTPRequestHandler(CGIHTTPServer.CGIHTTPRequestHandler):
1220 """ Serve this CGI from the root of the webserver """
1221 def is_cgi(self):
1222 if "favicon" in self.path:
1223 return False
1224
1225 self.cgi_info = (os.path.basename(__file__), self.path)
1226 self.path = ''
1227 return True
1228 handler = MyCGIHTTPRequestHandler
1229 SocketServer.TCPServer.allow_reuse_address = True
1230 httpd = SocketServer.TCPServer(("", PORT), handler)
1231 httpd.server_name = 'localhost'
1232 httpd.server_port = PORT
1233
1234 logger.info("serving at port %s", PORT)
1235 try:
1236 httpd.serve_forever()
1237 except KeyboardInterrupt:
1238 httpd.shutdown()
1239 logger.info("All done goodbye")
1240 elif sys.argv[1] == "test":
1241 os.environ['PATH_INFO'] = "/".join(sys.argv[2:])
1242 os.environ['SCRIPT_NAME'] = __file__
1243 process_cgi_request()
1244 elif sys.argv[1] == "static":
1245 items = dict()
1246 items['output_dir'] = sys.argv[2] if len(sys.argv) > 2 else "./static"
1247 for node in get_hostlist():
1248 items['node'] = node
1249 items['wdir'] = "%(output_dir)s/%(node)s" % items
1250 if not os.path.isdir(items['wdir']):
1251 os.makedirs(items['wdir'])
1252 datadump = get_yaml(node)
1253 for config in files:
1254 items['config'] = config
1255 logger.info("## Generating %(node)s %(config)s" % items)
1256 f = open("%(wdir)s/%(config)s" % items, "w")
1257 f.write(generate_config(node, config, datadump))
1258 f.close()
1259 elif sys.argv[1] == "wind-export":
1260 items = dict()
1261 for node in get_hostlist():
1262 datadump = get_yaml(node)
1263 sql = """INSERT IGNORE INTO nodes (name, name_ns, longitude, latitude)
1264 VALUES ('%(nodename)s', '%(nodename)s', %(latitude)s, %(longitude)s);""" % datadump;
1265 sql = """INSERT IGNORE INTO users_nodes (user_id, node_id, owner)
1266 VALUES (
1267 (SELECT id FROM users WHERE username = 'rvdzwet'),
1268 (SELECT id FROM nodes WHERE name = '%(nodename)s'),
1269 'Y');""" % datadump
1270 #for config in files:
1271 # items['config'] = config
1272 # print "## Generating %(node)s %(config)s" % items
1273 # f = open("%(wdir)s/%(config)s" % items, "w")
1274 # f.write(generate_config(node, config, datadump))
1275 # f.close()
1276 for node in get_hostlist():
1277 datadump = get_yaml(node)
1278 for iface_key in sorted([elem for elem in datadump.keys() if elem.startswith('iface_')]):
1279 ifacedump = datadump[iface_key]
1280 if ifacedump.has_key('mode') and ifacedump['mode'] == 'ap-wds':
1281 ifacedump['nodename'] = datadump['nodename']
1282 if not ifacedump.has_key('channel') or not ifacedump['channel']:
1283 ifacedump['channel'] = 0
1284 sql = """INSERT INTO links (node_id, type, ssid, protocol, channel, status)
1285 VALUES ((SELECT id FROM nodes WHERE name = '%(nodename)s'), 'ap',
1286 '%(ssid)s', 'IEEE 802.11b', %(channel)s, 'active');""" % ifacedump
1287 elif sys.argv[1] == "full-export":
1288 hosts = {}
1289 for node in get_hostlist():
1290 datadump = get_yaml(node)
1291 hosts[datadump['nodename']] = datadump
1292 print yaml.dump(hosts)
1293
1294 elif sys.argv[1] == "dns":
1295 make_dns(sys.argv[2] if len(sys.argv) > 2 else 'dns', 'external' in sys.argv)
1296 elif sys.argv[1] == "cleanup":
1297 # First generate all datadumps
1298 datadumps = dict()
1299 for host in get_hostlist():
1300 logger.info("# Processing: %s", host)
1301 # Set some boring default values
1302 datadump = { 'board' : 'UNKNOWN' }
1303 datadump.update(get_yaml(host))
1304 datadumps[datadump['autogen_realname']] = datadump
1305
1306
1307 for host,datadump in datadumps.iteritems():
1308 # Convert all yes and no to boolean values
1309 def fix_boolean(dump):
1310 for key in dump.keys():
1311 if type(dump[key]) == dict:
1312 dump[key] = fix_boolean(dump[key])
1313 elif str(dump[key]).lower() in ["yes", "true"]:
1314 dump[key] = True
1315 elif str(dump[key]).lower() in ["no", "false"]:
1316 # Compass richting no (Noord Oost) is valid input
1317 if key != "compass": dump[key] = False
1318 return dump
1319 datadump = fix_boolean(datadump)
1320
1321 if datadump['rdnap_x'] and datadump['rdnap_y']:
1322 datadump['latitude'], datadump['longitude'] = rdnap.rd2etrs(datadump['rdnap_x'], datadump['rdnap_y'])
1323 elif datadump['latitude'] and datadump['longitude']:
1324 datadump['rdnap_x'], datadump['rdnap_y'] = rdnap.etrs2rd(datadump['latitude'], datadump['longitude'])
1325
1326 if datadump['nodename'].startswith('Proxy'):
1327 datadump['nodename'] = datadump['nodename'].lower()
1328
1329 for iface_key in datadump['autogen_iface_keys']:
1330 # Wireless Leiden SSID have an consistent lowercase/uppercase
1331 if datadump[iface_key].has_key('ssid'):
1332 ssid = datadump[iface_key]['ssid']
1333 prefix = 'ap-WirelessLeiden-'
1334 if ssid.lower().startswith(prefix.lower()):
1335 datadump[iface_key]['ssid'] = prefix + ssid[len(prefix)].upper() + ssid[len(prefix) + 1:]
1336 if datadump[iface_key].has_key('ns_ip') and not datadump[iface_key].has_key('mode'):
1337 datadump[iface_key]['mode'] = 'autogen-FIXME'
1338 if not datadump[iface_key].has_key('desc'):
1339 datadump[iface_key]['desc'] = 'autogen-FIXME'
1340 store_yaml(datadump)
1341 elif sys.argv[1] == "list":
1342 use_fqdn = False
1343 if len(sys.argv) < 4 or not sys.argv[2] in ["up", "down", "planned", "all"]:
1344 usage()
1345 if sys.argv[3] == "nodes":
1346 systems = get_nodelist()
1347 elif sys.argv[3] == "proxies":
1348 systems = get_proxylist()
1349 elif sys.argv[3] == "systems":
1350 systems = get_hostlist()
1351 else:
1352 usage()
1353 if len(sys.argv) > 4:
1354 if sys.argv[4] == "fqdn":
1355 use_fqdn = True
1356 else:
1357 usage()
1358
1359 for system in systems:
1360 datadump = get_yaml(system)
1361
1362 output = datadump['autogen_fqdn'] if use_fqdn else system
1363 if sys.argv[2] == "all":
1364 print output
1365 elif datadump['status'] == sys.argv[2]:
1366 print output
1367 elif sys.argv[1] == "create":
1368 if sys.argv[2] == "network.kml":
1369 print make_network_kml.make_graph()
1370 else:
1371 usage()
1372 usage()
1373 else:
1374 # Do not enable debugging for config requests as it highly clutters the output
1375 if not is_text_request():
1376 cgitb.enable()
1377 process_cgi_request()
1378
1379
1380if __name__ == "__main__":
1381 main()
Note: See TracBrowser for help on using the repository browser.