source: genesis/tools/gformat.py@ 10714

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

Fill the content of the pf.hybrid.conf.local

rdr_rules is een array van de vorm:

  • [src_port, dest_ip, dest_port]

rdr_ip: is de external facing IP waar de rdr_rules (normaal op gelden). Dit is
voornamelijk administratief en moet dus het externe IP zijn voor het geval er

Let wel op dat een (eventuele) router die ertussen zit natuurlijk ook port
forwarding moet instellen.

Related-To: nodefactory#152

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