source: genesis/tools/gformat.py@ 10732

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

Als je goede interfaces opgeeft dan loopt die over de andere niet te klagen.

Related-To: nodefactory#156

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