aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorficus <ficus@robocracy.org>2012-09-15 19:46:49 +0200
committerficus <ficus@robocracy.org>2012-09-15 19:46:49 +0200
commit42725426ec02d359b67cd8d665afe17882d38958 (patch)
tree148c4082998f7d0b82ec4f22515d7626fca6a0e7
parenta304d7a56563b16ca98d9b45ebed521c52f6347c (diff)
downloadtui-42725426ec02d359b67cd8d665afe17882d38958.tar.gz
tui-42725426ec02d359b67cd8d665afe17882d38958.zip
more WIP
-rw-r--r--TODO22
-rw-r--r--helpers/netif.py188
-rw-r--r--helpers/sysstatus.py26
-rw-r--r--helpers/tor.py11
-rw-r--r--helpers/util.py2
-rw-r--r--templates/base.html12
-rw-r--r--templates/home.html96
-rw-r--r--templates/lan.html32
-rw-r--r--templates/lib.html65
-rw-r--r--templates/processes.html6
-rw-r--r--templates/tor.html19
-rw-r--r--templates/wan.html35
-rw-r--r--templates/wifi.html38
-rwxr-xr-xtorouterui.py155
14 files changed, 625 insertions, 82 deletions
diff --git a/TODO b/TODO
index d9d76ed..012684a 100644
--- a/TODO
+++ b/TODO
@@ -3,7 +3,7 @@
- imitate Tomato firmware style/layout
- flask python web framework
-- bootstrap with non-standard color scheme
+- bootstrap with a non-standard color scheme
- modularize configuration "nuggets"; compare and apply changes all or none
- write nugget getters/setters
@@ -12,16 +12,20 @@
- in some cases execute changes without restart?
- monitoring
- enable/disable
- bandwidth and system status charts via... mrtg? nagios?
- dmesg, syslog, auth, ???
- top
+ dmesg, syslog, auth
+ ps aux
- front page status
uptime
current cpu, disk, ram utilization
upstream: dhcp
ip equivalent
+ last login (?)
+- about
+ uname
+ software versions
+ project links
- administration
+ hostname (?)
ssh access, keys
autocron updates
- WAN network
@@ -35,11 +39,19 @@
torification
- tor
generic status
+ enable relay
+ enable bridge
common configuration
# Later
+- dhcp configuration
+- enable/disable hotplug in interfaces when method changes
+- flesh out README
+- installation script
+- debian packaging
- admin authentication scheme
+- not-root permissions scheme for: interfaces, tor, wireless, ssh keys
- firewall configuration
port forwarding
enable remote login
diff --git a/helpers/netif.py b/helpers/netif.py
index 6d7a194..77f710c 100644
--- a/helpers/netif.py
+++ b/helpers/netif.py
@@ -1,5 +1,6 @@
import os
+import augeas
from util import *
@@ -20,6 +21,10 @@ def parse_ip(ifname):
for k, v in val_indexes.iteritems():
if len(iplinkl) > v and iplinkl[v] == k:
d[k] = iplinkl[v+1]
+ if 'LOWER_UP' in iplinkl[2][1:-1].split(","):
+ d['state'] = "RUNNING"
+ elif 'NO-CARRIER' in iplinkl[2][1:-1].split(","):
+ d['state'] = "DISCONNECTED"
d['ipv4addrs'] = list()
d['ipv6addrs'] = list()
for l in ipinfo[1:]:
@@ -38,15 +43,121 @@ def parse_ip(ifname):
addr=l[1].split('/')[0],
prefix=int(l[1].split('/')[1]),
scope=l[3]))
- print d['ipv6addrs']
return d
-def get_wan_status(ifname='eth1'):
+def parse_iw(ifname):
+ """
+ Example `iw dev wlan0 link` string (sic):
+
+ Connected to c0:25:06:51:22:9b (on wlan0)
+ SSID: fleischfressendepflanze
+ freq: 2427
+ RX: 73744193 bytes (456838 packets)
+ TX: 3269174 bytes (19709 packets)
+ signal: -44 dBm
+ tx bitrate: 72.2 MBit/s MCS 7 short GI
+
+ bss flags:
+ dtim period: 0
+ beacon int: 100
+ """
+ d = dict()
+ iwinfo = cli_read_lines('iw dev %s link' % ifname)
+ if 0 == len(iwinfo):
+ raise KeyError('No such interface: ' + ifname)
+ if iwinfo[0].strip() == "Not connected.":
+ d['radio_state'] = "disabled"
+ return d
+ else:
+ d['radio_state'] = "enabled"
+ for l in iwinfo:
+ l = l.strip()
+ if l.startswith("SSID:"):
+ d['ssid'] = l[6:].strip()
+ elif l.startswith("freq:"):
+ d['freq'] = "%sMHz" % l.split()[-1].strip()
+ elif l.startswith("signal:"):
+ d['signal_dbm'] = l.split()[1]
+ elif l.startswith("tx bitrate:"):
+ d['signal_throughput'] = ' '.join(l.split()[2:4])
+ return d
+
+def read_augeas_ifinfo(ifname):
+ d = dict()
+ aug = augeas.Augeas(flags=augeas.Augeas.NO_MODL_AUTOLOAD)
+ aug.set("/augeas/load/Interfaces/lens", "Interfaces.lns")
+ aug.set("/augeas/load/Interfaces/incl", "/etc/network/interfaces")
+ aug.load()
+ for iface in aug.match("/files/etc/network/interfaces/iface"):
+ if aug.get(iface) == ifname:
+ if aug.get(iface + "/family") == 'inet':
+ d['ipv4method'] = aug.get(iface + "/method")
+ if d['ipv4method'] == 'manual':
+ d['ipv4method'] = 'disabled'
+ d['ipv4addr'] = aug.get(iface + "/address")
+ d['ipv4netmask'] = aug.get(iface + "/netmask")
+ d['ipv4gateway'] = aug.get(iface + "/gateway")
+ d['ipv4mtu'] = aug.get(iface + "/mtu")
+ d['ipv4mac'] = aug.get(iface + "/hwaddress")
+ elif aug.get(iface + "/family") == 'inet6':
+ # handle ipv6 stuff
+ pass
+ aug.close()
+ return d
+ aug.close()
+ return None
+
+def write_augeas_ifinfo(ifname, settings, method='disabled'):
+ d = dict()
+ aug = augeas.Augeas(flags=augeas.Augeas.NO_MODL_AUTOLOAD)
+ aug.set("/augeas/load/Interfaces/lens", "Interfaces.lns")
+ aug.set("/augeas/load/Interfaces/incl", "/etc/network/interfaces")
+ aug.load()
+ path = None
+ for iface in aug.match("/files/etc/network/interfaces/iface"):
+ if aug.get(iface) == ifname and aug.get(iface + "/family") == 'inet':
+ path = iface
+ if not path:
+ # insert iface
+ if len(aug.match("/files/etc/network/interfaces/iface")) == 0:
+ # no interfaces at all, insert wherever
+ path = "/files/etc/network/interfaces/iface"
+ aug.set(path, ifname)
+ else:
+ aug.insert("/files/etc/network/interfaces/iface", "iface",
+ before=False)
+ path = aug.match("/files/etc/network/interfaces/iface")[-1]
+ aug.set(path, ifname)
+ assert path, "require path to be set"
+ aug.set(path + "/family", 'inet')
+ if method == 'disabled':
+ aug.set(path + "/method", 'manual')
+ aug.remove(path + "/address")
+ aug.remove(path + "/netmask")
+ aug.remove(path + "/gateway")
+ elif method == 'dhcp':
+ aug.set(path + "/method", 'dhcp')
+ aug.remove(path + "/address")
+ aug.remove(path + "/netmask")
+ aug.remove(path + "/gateway")
+ elif method == 'static':
+ aug.set(path + "/method", 'static')
+ aug.set(path + "/address", str(settings['ipv4addr']))
+ aug.set(path + "/netmask", str(settings['ipv4netmask']))
+ aug.set(path + "/gateway", str(settings['ipv4gateway']))
+ else:
+ raise ValueError("unrecognized network interface method: " + method)
+ print "committing with augeas..."
+ aug.save()
+ print "augeas errors: %s" % aug.get("/augeas/error")
+ aug.close()
+
+def get_wan_status(ifname='eth0'):
d = dict()
try:
d.update(parse_ip(ifname))
except KeyError:
- None
+ return None
return d
def get_lan_status(ifname='eth0'):
@@ -54,15 +165,80 @@ def get_lan_status(ifname='eth0'):
try:
d.update(parse_ip(ifname))
except KeyError:
- None
+ return None
return d
-def get_wireless_status(ifname='wlan0'):
+def get_wifi_status(ifname='wlan0'):
d = dict()
try:
d.update(parse_ip(ifname))
except KeyError, ke:
- raise ke
return None
+ d.update(parse_iw(ifname))
return d
+def get_wan_settings(ifname='eth0'):
+ return read_augeas_ifinfo(ifname)
+
+def save_wan_settings(form, ifname='eth0'):
+ write_augeas_ifinfo(ifname, method=form['ipv4method'], settings=form)
+ if form['ipv4method'] == 'disabled':
+ print "ifdown..."
+ os.system("ifdown %s" % ifname)
+ else:
+ print "ifup..."
+ os.system("ifdown %s" % ifname)
+ os.system("ifup %s &" % ifname)
+
+def get_lan_settings(ifname='eth0'):
+ d = read_augeas_ifinfo(ifname)
+ return d
+
+def save_lan_settings(ifname='eth0'):
+ write_augeas_ifinfo(ifname, method=form['ipv4method'], settings=form)
+ if form['ipv4method'] == 'disabled':
+ print "ifdown..."
+ os.system("ifdown %s" % ifname)
+ else:
+ print "ifup..."
+ os.system("ifdown %s" % ifname)
+ os.system("ifup %s &" % ifname)
+
+def get_wifi_settings(ifname='wlan0'):
+ #d = read_augeas_ifinfo(ifname)
+ d = dict()
+ if not d:
+ return d
+ d.update(dict()) # extra wireless settings
+ return d
+
+def save_wifi_settings(ifname='eth0'):
+ pass
+
+def is_valid_ipv4(s):
+ # TODO: this is a hack
+ l = s.split('.')
+ if not len(l) == 4:
+ return False
+ try:
+ l = map(int, l)
+ except ValueError:
+ return False
+ if l[0] > 255 or l[1] > 255 or l[2] > 255 or l[3] > 255:
+ return False
+ if l[0] == 0 or l[3] == 0:
+ return False
+ return True
+
+def is_valid_ipv4mask(s):
+ # TODO: this is a hack
+ l = s.split('.')
+ if not len(l) == 4:
+ return False
+ try:
+ l = map(int, l)
+ except ValueError:
+ return False
+ if l[0] > 255 or l[1] > 255 or l[2] > 255 or l[3] > 255:
+ return False
+ return True
diff --git a/helpers/sysstatus.py b/helpers/sysstatus.py
index b2eec6f..933f625 100644
--- a/helpers/sysstatus.py
+++ b/helpers/sysstatus.py
@@ -12,6 +12,28 @@ def get_system_status():
d['uptime'] = cli_read('uptime')
return d
+def get_resources_status():
+ d = dict()
+ disk_info = cli_read_lines('df -h /home')[1].split()
+ d['disk_used'] = disk_info[2]
+ d['disk_avail'] = disk_info[1]
+ d['disk_percent'] = int(disk_info[4][:-1])
+
+ ram_info = cli_read_lines('free -m')[1].split()
+ d['ram_used'] = "%sMB" % ram_info[2]
+ d['ram_avail'] = "%sMB" % ram_info[1]
+ d['ram_percent'] = int(float(ram_info[2])/float(ram_info[1]) * 100.0)
+
+ d['cpu_cores'] = 1
+ for l in open('/proc/cpuinfo', 'r'):
+ if l.split(":")[0].strip() == "cpu cores":
+ d['cpu_cores'] = int(l.split(":")[-1].strip())
+ break
+ cpu_info = cli_read('uptime')
+ d['cpu_load'] = cpu_info.split()[-3].strip(',')
+ d['cpu_percent'] = int(float(d['cpu_load'])/float(d['cpu_cores']) * 100.0)
+ return d
+
def get_dmesg():
try:
return cli_read('dmesg')
@@ -21,14 +43,14 @@ def get_dmesg():
def get_authlog():
try:
with open('/var/log/auth.log') as f:
- return '\n'.join(f.readlines())
+ return ''.join(f.readlines())
except IOError:
return None
def get_syslog():
try:
with open('/var/log/syslog') as f:
- return '\n'.join(f.readlines())
+ return ''.join(f.readlines())
except IOError:
return None
diff --git a/helpers/tor.py b/helpers/tor.py
index e69de29..438cb5d 100644
--- a/helpers/tor.py
+++ b/helpers/tor.py
@@ -0,0 +1,11 @@
+
+def get_tor_status():
+ d = dict()
+ d['state'] = 'DISABLED'
+ return d
+
+def get_tor_settings():
+ return dict()
+
+def save_tor_settings():
+ pass
diff --git a/helpers/util.py b/helpers/util.py
index 46030bf..229d975 100644
--- a/helpers/util.py
+++ b/helpers/util.py
@@ -11,7 +11,7 @@ def cli_read_lines(cmd):
def fs_read(path):
with open(path, 'r') as f:
- return '\n'.join(f.readlines()).strip()
+ return ''.join(f.readlines())
def prefix_to_ipv4_mask(prefixlen):
assert(prefixlen >= 0)
diff --git a/templates/base.html b/templates/base.html
index a2c1407..4c15c73 100644
--- a/templates/base.html
+++ b/templates/base.html
@@ -37,13 +37,12 @@
<li {% if path == request.path %}class="active"{% endif %}><a href="{{path}}">{{name}}</a></li>
{%- endmacro %}
{{ pagelink("/", "Status") }}
- {{ pagelink("/admin/", "Administration") }}
{{ pagelink("/reboot/", "Reboot...") }}
<li class="nav-header">Configuration</li>
- {{ pagelink("/wan/", "Upstream") }}
- {{ pagelink("/lan/", "Local") }}
- {{ pagelink("/wireless/", "Wireless") }}
- {{ pagelink("/tor/", "Tor") }}
+ {{ pagelink("/wan/", "Upstream Ethernet") }}
+ {{ pagelink("/lan/", "Local Ethernet") }}
+ {{ pagelink("/wifi/", "WiFi") }}
+ {{ pagelink("/tor/", "Tor Network") }}
<li class="nav-header">Monitoring</li>
{{ pagelink("/logs/", "Logs") }}
{{ pagelink("/processes/", "Processes") }}
@@ -51,8 +50,8 @@
</div><!--/.well -->
</div><!--/span-->
<div class="span9">
- {% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
+ {{ mesages }}
{% for category, message in messages %}
<div class="alert alert-{{ category }}">
<button type="button" class="close" data-dismiss="alert">×</button>
@@ -63,7 +62,6 @@
</div>
{% endfor %}
{% endif %}
- {% endwith %}
{% block body %}{% endblock %}
</div><!--/span-->
</div><!--/row-->
diff --git a/templates/home.html b/templates/home.html
index 30c328e..fa2ed2b 100644
--- a/templates/home.html
+++ b/templates/home.html
@@ -1,39 +1,71 @@
{% import "lib.html" as lib %}
{% extends "base.html" %}
{% block body %}
-<h3>System</h3>
-<table class="table table-condensed">
- <tr>
- <th>Host Name
- <td><code>{{ status.system.hostname }}</code>
- <tr>
- <th>Current Time
- <td><code>{{ status.system.current_time }}</code>
- <tr>
- <th>System Uptime
- <td><code>{{ status.system.uptime }}</code>
-</table>
-<h3>WAN</h3>
-{% if not status.wan %}
-Upstream ethernet interface hardware not detected at all!
-{% else %}
-{{ lib.ifstatus(status.wan) }}
-{% endif %}
-
-<h3>LAN</h3>
-{% if not status.lan %}
-Local network ethernet interface hardware not detected at all!
-{% else %}
-{{ lib.ifstatus(status.lan) }}
-{% endif %}
-
-<h3>Wireless</h3>
-{% if not status.wireless %}
-Wireless interface hardware not detected at all!
-{% else %}
-{{ lib.ifstatus(status.wireless) }}
-{% endif %}
+<div class="row-fluid">
+ <div class="span6">
+ <h3>System</h3>
+ <table class="table table-condensed">
+ <tr>
+ <th>Host Name
+ <td><span style="font-family:monospace;">{{ status.system.hostname }}</span>
+ <tr>
+ <th>Current Time
+ <td><span style="font-family:monospace;">{{ status.system.current_time }}</span>
+ <tr>
+ <th>System Uptime
+ <td><span style="font-family:monospace;">{{ status.system.uptime }}</span>
+ </table>
+ </div>
+ <div class="span6">
+ <h3>Resources</h3>
+ <ul class="unstyled">
+ <li>
+ CPU Load<span class="pull-right strong">{{status.resources.cpu_load}} / {{status.resources.cpu_cores}} cores</span>
+ <div class="progress progress-striped ">
+ <div class="bar" style="width: {{status.resources.cpu_percent}}%;"></div>
+ </div>
+ </li>
+ <li>RAM Usage<span class="pull-right strong">{{status.resources.ram_used}} / {{status.resources.ram_avail}}</span>
+ <div class="progress progress-success progress-striped ">
+ <div class="bar" style="width: {{ status.resources.ram_percent }}%;"></div>
+ </div>
+ </li>
+ <li>Primary Disk Space<span class="pull-right strong">{{status.resources.disk_used}} / {{status.resources.disk_avail}}</span>
+ <div class="progress progress-warning progress-striped ">
+ <div class="bar" style="width: {{ status.resources.disk_percent }}%;"></div>
+ </div>
+ </li>
+ </ul>
+ </div>
+</div>
+<div class="row-fluid">
+ <div class="span6">
+ <h3>WAN</h3>
+ {{ lib.ifstatus(status.wan) }}
+ </div>
+ <div class="span6">
+ <h3>LAN</h3>
+ {{ lib.ifstatus(status.lan) }}
+ </div>
+</div>
+<div class="row-fluid">
+ <div class="span6">
+ <h3>WiFi</h3>
+ {{ lib.ifstatus(status.wifi) }}
+ </div>
+ <div class="span6">
+ <h3>Tor</h3>
+ <table class="table table-condensed {% if status.tor.state in ["DISABLED"] %}muted{% endif %}">
+ <tr>
+ <th>Status
+ <td><span style="font-weight: bold;" class="label
+ {% if status.tor.state == 'RUNNING' %}label-success{% elif status.tor.state == 'STARTING' %}label-info{% else %}label-important{% endif %}">
+ {{ status.tor.state }}
+ </span>
+ </table>
+ </div>
+</div>
{% endblock %}
diff --git a/templates/lan.html b/templates/lan.html
new file mode 100644
index 0000000..09bcf0a
--- /dev/null
+++ b/templates/lan.html
@@ -0,0 +1,32 @@
+{% import "lib.html" as lib %}
+{% extends "base.html" %}
+{% block body %}
+
+<form action="/lan/" method="POST" class="form-horizontal">
+<legend>Local Ethernet Configuration</legend>
+
+{% if not status.lan %}
+<span class="text-error">Could not find the local ethernet hardware device!</span>
+{% else %}
+
+{{ lib.formcheckbox(form, formerr, 'ipv4enable', 'Enable this interface', 'true') }}
+
+<legend>Address Configuration</legend>
+{{ lib.forminput(form, formerr, 'ipv4addr', 'IPv4 Address', '0.0.0.0') }}
+{{ lib.forminput(form, formerr, 'ipv4netmask', 'Netmask', '255.255.0.0') }}
+{{ lib.forminput(form, formerr, 'ipv4gateway', 'Gateway IP Address', '0.0.0.0') }}
+
+<legend>DHCP Configuration</legend>
+{{ lib.forminput(form, formerr, 'dhcpbase', 'DHCP Range Base', '192.168.1.100') }}
+{{ lib.forminput(form, formerr, 'dhcptop', 'DHCP Range Top', '192.168.1.200') }}
+{{ lib.forminput(form, formerr, 'dhcptime', 'Lease Time', '12h') }}
+
+<div class="pull-right">
+<a href="."><button class="btn" type="button">Reset</button></a>
+<button type="submit" class="btn btn-primary" type="button">
+Save</button>
+</div>
+</form>
+{% endif %}
+
+{% endblock %}
diff --git a/templates/lib.html b/templates/lib.html
index 33534da..6ac5e12 100644
--- a/templates/lib.html
+++ b/templates/lib.html
@@ -1,41 +1,84 @@
+
{% macro ifstatus(ifstatus) -%}
-<table class="table table-condensed">
+{% if not ifstatus %}
+<span class="text-error">
+Network hardware not detected at all!
+</span>
+{% else %}
+<table class="table table-condensed {% if ifstatus.state in ["DOWN", "DISCONNECTED"] %}muted{% endif %}">
<tr>
<th>Interface Name
- <td><code>{{ ifstatus.ifname }}</code>
+ <td><span style="font-family:monospace;">{{ ifstatus.ifname }}</span>
<tr>
<th>Status
- <td><span style="font-weight: bold; color:
- {% if ifstatus.state == 'UP' %}green{% else %}red{% endif %};">
+ <td><span style="font-weight: bold;" class="label
+ {% if ifstatus.state == 'RUNNING' %}label-success{% elif ifstatus.state == 'up' %}label-info{% else %}label-important{% endif %}">
{{ ifstatus.state }}
</span>
<tr>
<th>MAC Address
- <td><code>{{ ifstatus.mac }}</code>
+ <td><span style="font-family:monospace;">{{ ifstatus.mac }}</span>
<tr>
<th>IPv4 Addresses
- <td><code>
+ <td><span style="font-family:monospace;">
{% for addr in ifstatus.ipv4addrs %}
{{ addr.addr }}/{{ addr.prefix}} ({{ addr.scope }})<br>
- {% endfor %}</code>
+ {% endfor %}</span>
<tr>
<th>IPv6 Addresses
- <td><code>
+ <td><span style="font-family:monospace;">
{% for addr in ifstatus.ipv6addrs %}
{{ addr.addr }}/{{ addr.prefix}} ({{ addr.scope }})<br>
- {% endfor %}</code>
+ {% endfor %}</span>
+ {% if ifstatus.radio_state %}
+ <tr>
+ <th>Radio State
+ <td><span style="font-family:monospace;">{{ ifstatus.radio_state }}</span>
+ <tr>
+ <th>SSID
+ <td><span style="font-family:monospace;">{{ ifstatus.ssid }}</span>
+ <tr>
+ <th>Frequency
+ <td><span style="font-family:monospace;">{{ ifstatus.freq }}</span>
+ <tr>
+ <th>Signal Strength
+ <td><span style="font-family:monospace;">{{ ifstatus.signal_dbm }}</span>
+ {% endif %}
</table>
+{% endif %}
{%- endmacro %}
+
{% macro logbox(name, contents) -%}
<h3>{{name}}</h3>
{% if contents == None %}
<span class="text-error">Access to {{name}} was denied, or file did not exist.</span>
{% else %}
-
-<pre style="height: 18em; width 40em; overflow: auto;">
+<div style="height: 18em; width: 60em;">
+<pre style="height: 18em; width 60em; overflow: auto;">
{{ contents }}
</pre>
+</div>
{% endif%}
{%- endmacro %}
+
+
+{% macro forminput(form, formerr, name, title, placeholder) -%}
+<div class="control-group {% if formerr[name] %}error{% endif %}">
+ <label class="control-label" for="{{name}}">{{ title }}</label>
+ <div class="controls">
+ <input type="text" name="{{name}}" placeholder="{{placeholder}}" {% if form[name] %}value="{{ form[name] }}"{% endif %}>
+ {% if formerr[name] %}
+ <span class="help-inline">{{ formerr[name] }}</span>
+ {% endif %}
+ </div>
+</div>
+{%- endmacro %}
+
+{% macro formcheckbox(form, formerr, name, title, value) -%}
+ <label class="checkbox">
+ <input type="checkbox" name="{{name}}" id="{{name}}" value="{{value}}" {% if form[name] == value %}checked{% endif %}>
+ <h4>{{title}}</h4>
+ </label>
+{% endmacro %}
diff --git a/templates/processes.html b/templates/processes.html
index c911b0a..2e35162 100644
--- a/templates/processes.html
+++ b/templates/processes.html
@@ -8,12 +8,12 @@
<th>PID
<th>%CPU
<th>%MEM
- <th>Status Code
+ <th>Status
<th>Started
<th>Time
<th>Command
{% for proc in process_list %}
- <tr>
+ <tr style="font-family: monospace;">
<td>{{ proc.user }}
<td>{{ proc.pid}}
<td>{{ proc.perc_cpu }}
@@ -21,7 +21,7 @@
<td>{{ proc.status_code }}
<td>{{ proc.started }}
<td>{{ proc.time }}
- <td>{{ proc.command }}
+ <td><div style="height: 1.5em; overflow:hidden;">{{ proc.command }}</div>
{% endfor %}
</table>
diff --git a/templates/tor.html b/templates/tor.html
new file mode 100644
index 0000000..a1f30dc
--- /dev/null
+++ b/templates/tor.html
@@ -0,0 +1,19 @@
+{% import "lib.html" as lib %}
+{% extends "base.html" %}
+{% block body %}
+
+<form action="/tor/" method="POST" class="form-horizontal">
+<legend>Tor Network Configuration</legend>
+
+{{ lib.formcheckbox(form, formerr, 'torenable', 'Enable Tor daemon', 'true') }}
+{{ lib.formcheckbox(form, formerr, 'torrelayenable', 'Enable Tor Relay', 'true') }}
+{{ lib.formcheckbox(form, formerr, 'torbridgeenable', 'Enable Tor Bridge', 'true') }}
+
+<div class="pull-right">
+<a href="."><button class="btn" type="button">Reset</button></a>
+<button type="submit" class="btn btn-primary" type="button">
+Save</button>
+</div>
+</form>
+
+{% endblock %}
diff --git a/templates/wan.html b/templates/wan.html
index e69de29..2da2733 100644
--- a/templates/wan.html
+++ b/templates/wan.html
@@ -0,0 +1,35 @@
+{% import "lib.html" as lib %}
+{% extends "base.html" %}
+{% block body %}
+
+<form action="/wan/" method="POST" class="form-horizontal">
+<legend>Upstream Ethernet Configuration</legend>
+
+<label class="radio">
+ <input type="radio" name="ipv4method" id="ipv4methoddhcp" value="dhcp" {% if form.ipv4method == "dhcp" %}checked{% endif %}>
+ <h4>Automatic DHCP Addressing</h4>
+</label>
+
+<label class="radio">
+ <input type="radio" name="ipv4method" id="ipv4methodstatic" value="static" {% if form.ipv4method == "static" %}checked{% endif %}>
+ <h4>Manual Addressing</h4>
+</label>
+
+{{ lib.forminput(form, formerr, 'ipv4addr', 'IPv4 Address', '0.0.0.0') }}
+{{ lib.forminput(form, formerr, 'ipv4netmask', 'Netmask', '255.255.255.0') }}
+{{ lib.forminput(form, formerr, 'ipv4gateway', 'Gateway IP Address', '0.0.0.0') }}
+
+<label class="radio">
+ <input type="radio" name="ipv4method" id="ipv4methoddisabled" value="disabled" {% if form.ipv4method == "disabled" %}checked{% endif %}>
+ <h4>Disable Interface</h4>
+</label>
+
+<div class="pull-right">
+<a href="."><button class="btn" type="button">Reset</button></a>
+<button type="submit" class="btn btn-primary" type="button">
+Save</button>
+</div>
+</form>
+
+
+{% endblock %}
diff --git a/templates/wifi.html b/templates/wifi.html
new file mode 100644
index 0000000..3e41788
--- /dev/null
+++ b/templates/wifi.html
@@ -0,0 +1,38 @@
+{% import "lib.html" as lib %}
+{% extends "base.html" %}
+{% block body %}
+
+<form action="/wifi/" method="POST" class="form-horizontal">
+<legend>WiFi Configuration</legend>
+
+{% if not status.wifi %}
+<span class="text-error">Could not find the WiFi hardware device!</span>
+{% else %}
+
+{{ lib.formcheckbox(form, formerr, 'wifienable', 'Enable the radio', 'true') }}
+{{ lib.formcheckbox(form, formerr, 'torifylanenable', 'Transparently "Tor-ify" this network interface', 'true') }}
+
+<legend>Radio</legend>
+{{ lib.forminput(form, formerr, 'wifissid', 'SSID name', 'internet') }}
+{{ lib.forminput(form, formerr, 'wifipower', 'Transmit Power', 'high, low') }}
+{{ lib.forminput(form, formerr, 'wifichannel', 'Channel', '11') }}
+
+<legend>Network Address</legend>
+{{ lib.forminput(form, formerr, 'ipv4addr', 'IPv4 Address', '0.0.0.0') }}
+{{ lib.forminput(form, formerr, 'ipv4netmask', 'Netmask', '255.255.0.0') }}
+{{ lib.forminput(form, formerr, 'ipv4gateway', 'Gateway IP Address', '0.0.0.0') }}
+
+<legend>DHCP Server</legend>
+{{ lib.forminput(form, formerr, 'dhcpbase', 'DHCP Range Base', '192.168.1.100') }}
+{{ lib.forminput(form, formerr, 'dhcptop', 'DHCP Range Top', '192.168.1.200') }}
+{{ lib.forminput(form, formerr, 'dhcptime', 'Lease Time', '12h') }}
+
+<div class="pull-right">
+<a href="."><button class="btn" type="button">Reset</button></a>
+<button type="submit" class="btn btn-primary" type="button">
+Save</button>
+</div>
+</form>
+{% endif %}
+
+{% endblock %}
diff --git a/torouterui.py b/torouterui.py
index 33a0018..5e8f346 100755
--- a/torouterui.py
+++ b/torouterui.py
@@ -6,6 +6,7 @@ import os
from helpers import sysstatus
from helpers import netif
+from helpers import tor
import config
app = Flask(__name__)
@@ -15,9 +16,11 @@ app = Flask(__name__)
def status():
status = dict()
status['system'] = sysstatus.get_system_status()
+ status['resources'] = sysstatus.get_resources_status()
status['wan'] = netif.get_wan_status()
status['lan'] = netif.get_lan_status()
- status['wireless'] = netif.get_wireless_status()
+ status['wifi'] = netif.get_wifi_status()
+ status['tor'] = tor.get_tor_status()
return render_template('home.html', settings=None, status=status)
@app.route('/administer/', methods=['GET', 'POST'])
@@ -26,35 +29,157 @@ def administer():
@app.route('/reboot/', methods=['GET', 'POST'])
def administer():
- print request.form
if request.method == 'GET':
return render_template('reboot.html', status=None)
- if request.form.has_key('confirm'):
- # TODO: check reboot flag here?
+ elif request.form.has_key('confirm'):
+ # XXX: execute reboot
return render_template('reboot.html', status='rebooting')
else:
# XXX: flashing introduces cookies
#flash("Didn't confirm, not rebooting", "warning")
return render_template('reboot.html', status=None)
-
@app.route('/wan/', methods=['GET', 'POST'])
def wan():
- status = dict()
- status['wan'] = netif.get_wan_status()
- return render_template('wan.html', settings=None, status=None)
+ msg = list()
+ status = dict(wan=netif.get_wan_status())
+ if not status['wan']:
+ msg.append(("error",
+ "Interface not detected, can not be configured."),)
+ return render_template('wan.html', form=None, status=status,
+ messages=msg, formerr=None)
+ if request.method == 'GET':
+ form = netif.get_wan_settings()
+ return render_template('wan.html', form=form, status=status,
+ formerr=None)
+ # Got this far, need to validated form
+ formerr = dict()
+ if request.form['ipv4method'] == 'disabled':
+ pass # no further validation
+ elif request.form['ipv4method'] == 'dhcp':
+ pass # no further validation
+ elif request.form['ipv4method'] == 'static':
+ if not netif.is_valid_ipv4(request.form['ipv4addr']):
+ formerr['ipv4addr'] = "Not a valid IPv4 address"
+ if not netif.is_valid_ipv4mask(request.form['ipv4netmask']):
+ formerr['ipv4netmask'] = "Not a valid IPv4 netmask"
+ if not netif.is_valid_ipv4(request.form['ipv4gateway']):
+ formerr['ipv4gateway'] = "Not a valid IPv4 address"
+ else:
+ ke = KeyError("Invalid net config method: %s" % form['ipv4method'])
+ print ke
+ raise ke
+ if len(formerr.keys()) > 0:
+ msg.append(("error",
+ "Please correct the validation issues below"),)
+ else:
+ # Ok, we have a valid form, now to commit it
+ try:
+ netif.save_wan_settings(request.form)
+ msg.append(("success",
+ "Configuration saved! Check logs for any errors"),)
+ except IOError, ioerr:
+ msg.append(("error",
+ "Was unable to commit changes... permissions problem? \"%s\"" \
+ % ioerr))
+ return render_template('wan.html', form=request.form, status=status,
+ formerr=formerr, messages=msg)
@app.route('/lan/', methods=['GET', 'POST'])
def lan():
- return render_template('lan.html', settings=None, status=None)
-
-@app.route('/wireless/', methods=['GET', 'POST'])
-def wireless():
- return render_template('wireless.html', settings=None, status=None)
+ msg = list()
+ status = dict()
+ status['lan'] = netif.get_lan_status()
+ if not status['lan']:
+ msg.append(("error",
+ "Interface not detected, can not be configured."),)
+ return render_template('lan.html', form=None, status=status,
+ messages=msg, formerr=None)
+ if request.method == 'GET':
+ form = netif.get_lan_settings()
+ return render_template('lan.html', form=form, status=status,
+ formerr=None)
+ # Got this far, need to validated form
+ formerr = dict()
+ if request.form['ipv4method'] == 'disabled':
+ pass # no further validation
+ elif request.form['ipv4method'] == 'static':
+ if not netif.is_valid_ipv4(request.form['ipv4addr']):
+ formerr['ipv4addr'] = "Not a valid IPv4 address"
+ if not netif.is_valid_ipv4mask(request.form['ipv4netmask']):
+ formerr['ipv4netmask'] = "Not a valid IPv4 netmask"
+ if not netif.is_valid_ipv4(request.form['ipv4gateway']):
+ formerr['ipv4gateway'] = "Not a valid IPv4 address"
+ else:
+ ke = KeyError("Invalid method: %s" % form['ipv4method'])
+ print ke
+ raise ke
+ if len(formerr.keys()) > 0:
+ msg.append(("error",
+ "Please correct the validation issues below"),)
+ else:
+ # Ok, we have a valid form, now to commit it
+ try:
+ netif.save_lan_settings(request.form)
+ msg.append(("success",
+ "Configuration saved! Check logs for any errors"),)
+ except IOError, ioerr:
+ msg.append(("error",
+ "Was unable to commit changes... permissions problem? \"%s\"" \
+ % ioerr))
+ return render_template('lan.html', form=request.form, status=status,
+ formerr=formerr, messages=msg)
+
+@app.route('/wifi/', methods=['GET', 'POST'])
+def wifi():
+ status = dict()
+ status['wifi'] = netif.get_wifi_status()
+ if not status['wifi']:
+ msg.append(("error",
+ "Interface not detected, can not be configured."),)
+ return render_template('wifi.html', form=None, status=status,
+ messages=msg, formerr=None)
+ if request.method == 'GET':
+ form = netif.get_wifi_settings()
+ return render_template('wifi.html', form=form, status=status,
+ formerr=None)
+ # Got this far, need to validated form
+ formerr = dict()
+ if request.form['ipv4method'] == 'disabled':
+ pass # no further validation
+ elif request.form['ipv4method'] == 'static':
+ if not netif.is_valid_ipv4(request.form['ipv4addr']):
+ formerr['ipv4addr'] = "Not a valid IPv4 address"
+ if not netif.is_valid_ipv4mask(request.form['ipv4netmask']):
+ formerr['ipv4netmask'] = "Not a valid IPv4 netmask"
+ if not netif.is_valid_ipv4(request.form['ipv4gateway']):
+ formerr['ipv4gateway'] = "Not a valid IPv4 address"
+ else:
+ ke = KeyError("Invalid method: %s" % form['ipv4method'])
+ print ke
+ raise ke
+ if len(formerr.keys()) > 0:
+ msg.append(("error",
+ "Please correct the validation issues below"),)
+ else:
+ # Ok, we have a valid form, now to commit it
+ try:
+ netif.save_wifi_settings(request.form)
+ msg.append(("success",
+ "Configuration saved! Check logs for any errors"),)
+ except IOError, ioerr:
+ msg.append(("error",
+ "Was unable to commit changes... permissions problem? \"%s\"" \
+ % ioerr))
+ return render_template('wifi.html', form=request.form, status=status,
+ formerr=formerr, messages=msg)
+ return render_template('wifi.html', settings=None, status=None)
@app.route('/tor/', methods=['GET', 'POST'])
-def tor():
- return render_template('tor.html', settings=None, status=None)
+def torpage():
+ msg = list()
+ return render_template('tor.html', settings=None, status=None,
+ form=request.form, formerr=None, messages=msg)
@app.route('/logs/', methods=['GET'])
def logs():