source: genesis/tools/gformat.py@ 10702

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

Only set the defaults if the hybrid is set, else the cleanup does not work anymore.

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