Merge "conman: Update previous scan results."
diff --git a/Makefile b/Makefile
index 5de9eaa..921debb 100644
--- a/Makefile
+++ b/Makefile
@@ -9,6 +9,7 @@
BUILD_WAVEGUIDE?=y
BUILD_DVBUTILS?=y
BUILD_SYSMGR?=y
+BUILD_CACHE_WARMING?=y
BUILD_STATUTILS?=y
BUILD_SPEEDTEST?=y
BUILD_CRYPTDEV?= # default off: needs libdevmapper
@@ -18,7 +19,7 @@
export BUILD_HNVRAM BUILD_SSDP BUILD_DNSSD BUILD_LOGUPLOAD \
BUILD_IBEACON BUILD_WAVEGUIDE BUILD_DVBUTILS BUILD_SYSMGR \
BUILD_STATUTILS BUILD_CRYPTDEV BUILD_SIGNING BUILD_JSONPOLL \
- BUILD_PRESTERASTATS
+ BUILD_PRESTERASTATS BUILD_CACHE_WARMING
# note: libgpio is not built here. It's conditionally built
# via buildroot/packages/google/google_platform/google_platform.mk
@@ -40,6 +41,10 @@
DIRS+=hnvram
endif
+ifeq ($(BUILD_CACHE_WARMING),y)
+DIRS+=cache_warming
+endif
+
ifeq ($(BUILD_LOGUPLOAD),y)
DIRS+=logupload/client
endif
diff --git a/cache_warming/Makefile b/cache_warming/Makefile
new file mode 100644
index 0000000..eb489d9
--- /dev/null
+++ b/cache_warming/Makefile
@@ -0,0 +1,37 @@
+INSTALL?=install
+PREFIX=$(DESTDIR)/bin
+LIBDIR=$(PREFIX)/cache_warming
+PYTHON?=python
+GPYLINT=$(shell \
+ if which gpylint >/dev/null; then \
+ echo gpylint; \
+ else \
+ echo 'echo "(gpylint-missing)" >&2'; \
+ fi \
+)
+
+all:
+
+%.test: %_test.py
+ $(PYTHON) $<
+
+runtests: $(patsubst %_test.py,%.test,$(wildcard *_test.py))
+
+lint: $(filter-out options.py,$(wildcard *.py))
+ $(GPYLINT) $^
+
+test_only: all
+ PYTHONPATH=..:$(PYTHONPATH) ./wvtest/wvtestrun $(MAKE) runtests
+
+test: all
+ $(MAKE) test_only lint
+
+install:
+ mkdir -p $(LIBDIR)
+ $(INSTALL) -m 0755 -D cache_warming.py $(LIBDIR)/
+
+install-libs:
+ @echo "No libs to install."
+
+clean:
+ rm -rf *~ .*~ *.pyc
\ No newline at end of file
diff --git a/cache_warming/cache_warming.py b/cache_warming/cache_warming.py
index 7b76fc3..3416b56 100644
--- a/cache_warming/cache_warming.py
+++ b/cache_warming/cache_warming.py
@@ -13,10 +13,13 @@
import json
import os
import socket
+import sys
+import dns.exception
import dns.resolver
hit_log = {}
last_fetch = datetime.min
+verbose = False
TOP_N = 50
FETCH_INTERVAL = 60 # seconds
UDP_SERVER_PATH = '/tmp/dns_query_log_socket'
@@ -33,6 +36,8 @@
log: Dictionary of top requested hosts with host key and tuple value
containing most recent hit time and hit count.
"""
+ if verbose:
+ print 'Saving hosts in %s.' % HOSTS_JSON_PATH
d = os.path.dirname(HOSTS_JSON_PATH)
if not os.path.exists(d):
os.makedirs(d)
@@ -46,6 +51,8 @@
Loads dictionary with host key and tuple value containing most recent hit
time and hit count as hit_log if it exists.
"""
+ if verbose:
+ print 'Loading hosts from %s.' % HOSTS_JSON_PATH
if os.path.isfile(HOSTS_JSON_PATH):
with open(HOSTS_JSON_PATH, 'r') as hosts_json:
global hit_log
@@ -63,6 +70,8 @@
'[Unix time] [host name]'.
"""
time, _, host = qry.partition(' ')
+ if verbose:
+ print 'Received query for %s.' % host
if host in hit_log:
hit_log[host] = (hit_log[host][0] + 1, time)
else:
@@ -102,9 +111,13 @@
if len(hosts) > TOP_N:
hosts = hosts[:TOP_N]
for host in hosts:
+ if verbose:
+ print 'Fetching %s.' % host
try:
my_resolver.query(host)
- except:
+ except dns.exception.DNSException:
+ if verbose:
+ print 'Failed to fetch %s.' % host
del hit_log[host]
hosts.remove(host)
@@ -120,14 +133,20 @@
return sorted(hit_log, key=hit_log.get, reverse=True)
-def warm_cache():
+def warm_cache(port, server):
"""Warms cache with predetermined number of most requested hosts.
Sorts hosts in hit log by hit count, fetches predetermined
number of top requested hosts, updates last fetch time.
+
+ Args:
+ port: Port to which to send queries (default is 53).
+ server: Alternate nameservers to query (default is None).
"""
+ if verbose:
+ print 'Warming cache.'
sorted_hosts = sort_hit_log()
- fetch(sorted_hosts, args.port, args.server)
+ fetch(sorted_hosts, port, server)
global last_fetch
last_fetch = datetime.now()
@@ -143,11 +162,15 @@
help='port to which to send queries (default is 53).')
parser.add_argument('-s', '--server', nargs='*', type=str,
help='alternate nameservers to query (default is None).')
+ parser.add_argument('-v', '--verbose', action='store_true')
return parser.parse_args()
if __name__ == '__main__':
+ sys.stdout = os.fdopen(1, 'w', 1)
+ sys.stderr = os.fdopen(2, 'w', 1)
args = set_args()
+ verbose = args.verbose
load_hosts()
server_address = UDP_SERVER_PATH
@@ -160,10 +183,12 @@
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(server_address)
os.chmod(server_address, 0o777)
+ if verbose:
+ print 'Set up socket at %s.' % HOSTS_JSON_PATH
while 1:
diff = datetime.now() - last_fetch
if diff.total_seconds() > 60:
- warm_cache()
+ warm_cache(args.port, args.server)
data = sock.recv(128)
process_query(data)
diff --git a/cache_warming/cache_warming_test.py b/cache_warming/cache_warming_test.py
index 83ff1e8..220edc1 100644
--- a/cache_warming/cache_warming_test.py
+++ b/cache_warming/cache_warming_test.py
@@ -6,7 +6,7 @@
@wvtest.wvtest
-def testProcessQuery_firstHit():
+def test_process_query_first_hit():
qry = '123456789 www.yahoo.com'
expected = {'www.yahoo.com': (1, '123456789')}
cache_warming.hit_log = {}
@@ -16,7 +16,7 @@
@wvtest.wvtest
-def testProcessQuery_updateHitCount():
+def test_process_query_update_hit_count():
qry = '123456789 www.yahoo.com'
cache_warming.hit_log = {'www.yahoo.com': (1, '123456789')}
cache_warming.process_query(qry)
@@ -26,7 +26,7 @@
@wvtest.wvtest
-def testProcessQuery_updateRecentHitTime():
+def test_process_query_update_recent_hit_time():
qry = '123456789 www.yahoo.com'
cache_warming.hit_log = {'www.yahoo.com': (1, '987654321')}
cache_warming.process_query(qry)
@@ -36,7 +36,7 @@
@wvtest.wvtest
-def testSortHitLog_empty():
+def test_sort_hit_log_empty():
cache_warming.hit_log = {}
expected = []
actual = cache_warming.sort_hit_log()
@@ -44,7 +44,7 @@
@wvtest.wvtest
-def testSortHitLog_nonEmpty():
+def test_sort_hit_log_non_empty():
cache_warming.hit_log = {
'www.google.com': (2, '123456789'),
'www.yahoo.com': (1, '987654321'),
@@ -54,10 +54,11 @@
actual = cache_warming.sort_hit_log()
wvtest.WVPASSEQ(actual, expected)
+
@wvtest.wvtest
-def testHitLogSubset():
+def test_hit_log_subset():
hosts = ['www.google.com', 'www.yahoo.com']
- cache_warming.hit_log = cache_warming.hit_log = {
+ cache_warming.hit_log = cache_warming.hit_log = {
'www.youtube.com': (4, '987654321'),
'www.google.com': (1, '987654321'),
'www.espn.com': (3, '123456789'),
diff --git a/cmds/.gitignore b/cmds/.gitignore
index 5189e0e..fcc39f5 100644
--- a/cmds/.gitignore
+++ b/cmds/.gitignore
@@ -1,4 +1,5 @@
*.o
+*.tmp.*
alivemonitor
asus_hosts
balloon
@@ -9,15 +10,19 @@
cpulog
device_stats.pb.h
dhcp-rogue
+dhcpnametax
+dhcpvendortax
dir-monitor
diskbench
dnsck
dnssd_hosts
+eddystone
freemegs
gsetsid
gstatic
host-*
http_bouncer
+ibeacon
ionice
isoping
isostream
diff --git a/cmds/Makefile b/cmds/Makefile
index 38557fa..60c1b5e 100644
--- a/cmds/Makefile
+++ b/cmds/Makefile
@@ -225,13 +225,14 @@
wifi_files: LIBS += -lnl-3 -lnl-genl-3 -lglib-2.0
host-wifi_files_test: host-wifi_files_test.o
host-wifi_files_test: LIBS += -lnl-3 -lnl-genl-3 -lglib-2.0
-dhcpvendortax: dhcpvendortax.o dhcpvendorlookup.o
-dhcpvendorlookup.c: dhcpvendorlookup.gperf
- $(GPERF) -G -C -t -L ANSI-C -N exact_match -K vendor_class --delimiters="|" \
- --includes --output-file=dhcpvendorlookup.c dhcpvendorlookup.gperf
-dhcpvendorlookup.o: CFLAGS += -Wno-missing-field-initializers
-host-dhcpvendorlookup.o: CFLAGS += -Wno-missing-field-initializers
-host-dhcpvendortax: host-dhcpvendortax.o host-dhcpvendorlookup.o
+dhcpvendortax: dhcpvendortax.o dhcpvendorlookup.tmp.o
+dhcpvendorlookup.tmp.c: dhcpvendorlookup.gperf
+ $(GPERF) -G -C -t -L ANSI-C -N exact_match -K vendor_class \
+ --delimiters="|" \
+ --includes --output-file=$@ $<
+dhcpvendorlookup.tmp.o: CFLAGS += -Wno-missing-field-initializers
+host-dhcpvendorlookup.tmp.o: CFLAGS += -Wno-missing-field-initializers
+host-dhcpvendortax: host-dhcpvendortax.o host-dhcpvendorlookup.tmp.o
dnssdmon: dnssdmon.o l2utils.o modellookup.o
dnssdmon: LIBS += -lnl-3 -lstdc++ -lm -lresolv
modellookup.c: modellookup.gperf
@@ -239,13 +240,14 @@
--includes --output-file=modellookup.c modellookup.gperf
modellookup.o: CFLAGS += -Wno-missing-field-initializers
host-modellookup.o: CFLAGS += -Wno-missing-field-initializers
-dhcpnametax: dhcpnametax.o hostnamelookup.o
-host-dhcpnametax: host-dhcpnametax.o host-hostnamelookup.o
-hostnamelookup.c: hostnamelookup.gperf
- $(GPERF) -G -C -t -T -L ANSI-C -n -c -N hostname_lookup -K name --delimiters="|" \
- --includes --output-file=hostnamelookup.c hostnamelookup.gperf
-hostnamelookup.o: CFLAGS += -Wno-missing-field-initializers
-host-hostnamelookup.o: CFLAGS += -Wno-missing-field-initializers
+dhcpnametax: dhcpnametax.o hostnamelookup.tmp.o
+host-dhcpnametax: host-dhcpnametax.o host-hostnamelookup.tmp.o
+hostnamelookup.tmp.c: hostnamelookup.gperf
+ $(GPERF) -G -C -t -T -L ANSI-C -n -c -N hostname_lookup -K name \
+ --delimiters="|" \
+ --includes --output-file=$@ $<
+hostnamelookup.tmp.o: CFLAGS += -Wno-missing-field-initializers
+host-hostnamelookup.tmp.o: CFLAGS += -Wno-missing-field-initializers
TESTS = $(wildcard test-*.sh) $(wildcard test-*.py) $(wildcard *_test.py) $(TEST_TARGETS)
@@ -269,5 +271,5 @@
$(TEST_TARGETS) \
$(HOST_TEST_TARGETS) \
$(ARCH_TARGETS) \
- *~ .*~ */*.pyc test_file *.pb.*
+ *~ .*~ */*.pyc test_file *.pb.* *.tmp.*
rm -rf test_dir
diff --git a/cmds/dhcpnametax.c b/cmds/dhcpnametax.c
index f4af92a..f1bba07 100644
--- a/cmds/dhcpnametax.c
+++ b/cmds/dhcpnametax.c
@@ -119,6 +119,9 @@
} else if (strcmp(dhcpsig, "1,28,2,3,15,6,119,12,44,47,26,121,42") == 0) {
// SleepIQ
sn = hostname_lookup(hostname, 11);
+ } else if (strcmp(dhcpsig, "1,121,33,3,6,12,15,28,42,51,54,58,59,119") == 0) {
+ // Lutron
+ sn = hostname_lookup(hostname, 7);
}
}
diff --git a/cmds/gstatic.c b/cmds/gstatic.c
index 15b23fc..518871f 100644
--- a/cmds/gstatic.c
+++ b/cmds/gstatic.c
@@ -85,11 +85,13 @@
total = 0;
while (total < count) {
rc = write(fd, buf + total, count - total);
- if (rc < 0)
- perror_die("write");
- else if (rc == 0)
+ if (rc < 0) {
+ perror("write");
+ return -1;
+ } else if (rc == 0) {
+ fprintf(stderr, "write: EOF\n");
return total;
- else
+ } else
total += rc;
}
@@ -125,8 +127,10 @@
socket_set_blocking(fd, false);
rc = connect(fd, addr->ai_addr, addr->ai_addrlen);
- if (rc < 0 && errno != EINPROGRESS)
- perror_die("connect");
+ if (rc < 0 && errno != EINPROGRESS) {
+ perror("connect");
+ return -1;
+ }
if (rc != 0) {
FD_ZERO(&writeset);
@@ -136,7 +140,8 @@
rc = select(fd + 1, NULL, &writeset, NULL, &timeout);
if (rc < 0) {
- perror_die("select");
+ perror("connect-select");
+ return -1;
} else if (rc == 0) {
/* timeout */
return -1;
@@ -156,27 +161,24 @@
ssize_t read_timeout(int fd, void *buf, size_t count, int timeout_ms)
{
- fd_set readset, exceptset;
+ fd_set readset;
struct timeval timeout;
int rc;
FD_ZERO(&readset);
FD_SET(fd, &readset);
- exceptset = readset;
timeout.tv_sec = timeout_ms / 1000;
timeout.tv_usec = (timeout_ms % 1000) * 1000;
- rc = select(fd + 1, &readset, NULL, &exceptset, &timeout);
+ rc = select(fd + 1, &readset, NULL, NULL, &timeout);
if (rc < 0) {
- perror_die("select");
+ perror("select");
+ return -1;
} else if (rc == 0) {
- /* timeout */
- return 0;
+ fprintf(stderr, "select: timed out\n");
+ return -1;
}
- if (FD_ISSET(fd, &exceptset))
- return 0;
-
return read(fd, buf, count);
}
@@ -198,7 +200,7 @@
rc = xwrite(fd, HTTP_REQUEST, sizeof(HTTP_REQUEST));
if (rc < (ssize_t)sizeof(HTTP_REQUEST))
- perror_die("write");
+ goto err;
rc = read_timeout(fd, http_response, sizeof(http_response), TIMEOUT_MS);
if (rc < 0)
@@ -223,6 +225,9 @@
int bad;
int rc;
+ // In case we get stuck in one of the blocking syscalls (write, read, etc)
+ alarm(60);
+
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
@@ -241,8 +246,11 @@
int fd;
fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
- if (fd < 0)
- perror_die("socket");
+ if (fd < 0) {
+ perror("socket");
+ bad = 1;
+ continue;
+ }
rc = do_http_request(fd, res);
if (rc != 0)
diff --git a/cmds/hostnamelookup.gperf b/cmds/hostnamelookup.gperf
index 5c3526f..afe272e 100644
--- a/cmds/hostnamelookup.gperf
+++ b/cmds/hostnamelookup.gperf
@@ -55,6 +55,7 @@
Joey_WiFi%1,3,6,12,15,28,42| "DISH Networks Joey", "Joey"
LGSmartTV%252,3,42,15,6,1,12| "LG Smart TV", "LG Smart TV"
LGwebOSTV%252,3,42,15,6,1,12| "LG Smart TV", "LG Smart TV"
+Lutron-| "Lutron Caséta", "Lutron Caséta"
MyBookLive%1,28,2,3,15,6,119,12,44,47,26,121| "WD My Book Live", "My Book Live"
NP-20| "Roku", "Netflix Player"
NP-C0| "Roku", "Roku HD 1100"
diff --git a/cmds/statcatcher.cc b/cmds/statcatcher.cc
index f8f270c..64db2b6 100644
--- a/cmds/statcatcher.cc
+++ b/cmds/statcatcher.cc
@@ -23,7 +23,7 @@
#include "device_stats.pb.h"
-std::string multicast_addr = "FF30::8000:1";
+std::string multicast_addr = "FF12::8000:1";
const char *optstring = "i:f:";
std::string interface = "wan0";
std::string stat_file;
@@ -119,6 +119,8 @@
if (recvsize < 0) {
perror("Failed to receive data on socket.\n");
exit(1);
+ } else {
+ fprintf(stderr, "received %d bytes\n", recvsize);
}
pkt.resize(recvsize);
diff --git a/cmds/statpitcher.cc b/cmds/statpitcher.cc
index 7e1b989..412be5d 100644
--- a/cmds/statpitcher.cc
+++ b/cmds/statpitcher.cc
@@ -25,7 +25,7 @@
#define GOOG_PROTOCOL 0x8930
#define STAT_INTERVAL 60
-std::string multicast_addr = "FF30::8000:1";
+std::string multicast_addr = "FF12::8000:1";
uint8_t mc_mac[] = { 0x01, 0x00, 0x5e, 0x00, 0x00, 0x01 };
const char *optstring = "s:i:a:";
diff --git a/cmds/test-dhcpnametax.sh b/cmds/test-dhcpnametax.sh
index 73be720..a9455dc 100755
--- a/cmds/test-dhcpnametax.sh
+++ b/cmds/test-dhcpnametax.sh
@@ -20,6 +20,7 @@
WVPASSEQ "$($TAX -l label -d 1,3,6,12,15,28,40,41,42 -h DIRECTV-H21-01234567)" "name label DirecTV;H21"
WVPASSEQ "$($TAX -l label -d 1,3,6,12,15,28,42 -h DIRECTV-HR22-01234567)" "name label DirecTV;HR22"
WVPASSEQ "$($TAX -l label -d 1,28,2,3,15,6,119,12,44,47,26,121,42 -h 500-cc04b40XXXXX)" "name label Select Comfort SleepIQ;SleepIQ"
+WVPASSEQ "$($TAX -l label -d 1,121,33,3,6,12,15,28,42,51,54,58,59,119 -h Lutron-012a34bcde56)" "name label Lutron Caséta;Lutron Caséta"
# check invalid or missing arguments.
WVFAIL $TAX
diff --git a/conman/interface.py b/conman/interface.py
index 82d1506..04c809c 100755
--- a/conman/interface.py
+++ b/conman/interface.py
@@ -2,7 +2,6 @@
"""Models wired and wireless interfaces."""
-import json
import logging
import os
import re
@@ -479,27 +478,7 @@
self._events = []
def _qcsapi(self, *command):
- try:
- return subprocess.check_output(['qcsapi'] + list(command)).strip()
- except subprocess.CalledProcessError:
- return None
-
- def _wifiinfo_filename(self):
- return os.path.join(self.WIFIINFO_PATH, self._interface)
-
- def _get_wifiinfo(self):
- try:
- return json.load(open(self._wifiinfo_filename()))
- except IOError:
- return None
-
- def _get_ssid(self):
- wifiinfo = self._get_wifiinfo()
- if wifiinfo:
- return wifiinfo.get('SSID')
-
- def _check_client_mode(self):
- return self._qcsapi('get_mode', 'wifi0') == 'Station'
+ return subprocess.check_output(['qcsapi'] + list(command)).strip()
def attach(self):
self._update()
@@ -517,9 +496,13 @@
def _update(self):
"""Generate and cache events, update state."""
- client_mode = self._check_client_mode()
- ssid = self._get_ssid()
- status = self._qcsapi('get_status', 'wifi0')
+ try:
+ client_mode = self._qcsapi('get_mode', 'wifi0') == 'Station'
+ ssid = self._qcsapi('get_ssid', 'wifi0')
+ status = self._qcsapi('get_status', 'wifi0')
+ except subprocess.CalledProcessError:
+ # If QCSAPI failed, skip update.
+ return
# If we have an SSID and are in client mode, and at least one of those is
# new, then we have just connected.
diff --git a/conman/interface_test.py b/conman/interface_test.py
index 4283a2a..4c7d52b 100755
--- a/conman/interface_test.py
+++ b/conman/interface_test.py
@@ -2,7 +2,6 @@
"""Tests for connection_manager.py."""
-import json
import logging
import os
import shutil
@@ -199,16 +198,15 @@
def add_connected_event(self):
self.fake_qcsapi['get_mode'] = 'Station'
- json.dump({'SSID': self.ssid_testonly},
- open(self._wifiinfo_filename(), 'w'))
+ self.fake_qcsapi['get_ssid'] = self.ssid_testonly
def add_disconnected_event(self):
self.ssid_testonly = None
- json.dump({'SSID': ''}, open(self._wifiinfo_filename(), 'w'))
+ self.fake_qcsapi['get_ssid'] = None
def add_terminating_event(self):
self.ssid_testonly = None
- json.dump({'SSID': ''}, open(self._wifiinfo_filename(), 'w'))
+ self.fake_qcsapi['get_ssid'] = None
self.fake_qcsapi['get_mode'] = 'AP'
def detach(self):
diff --git a/craftui/.gitignore b/craftui/.gitignore
index 5d9b5ae..7dd7a03 100644
--- a/craftui/.gitignore
+++ b/craftui/.gitignore
@@ -1,7 +1,9 @@
*.pyo
*.swp
.started
-.sim.extracted
-sim
+.sim1.extracted
+.sim2.extracted
+sim1
+sim2
LOG
tmp-certs
diff --git a/craftui/HOW.restart_if_changed b/craftui/HOW.restart_if_changed
index 0bfd11f..2a26b18 100644
--- a/craftui/HOW.restart_if_changed
+++ b/craftui/HOW.restart_if_changed
@@ -4,21 +4,26 @@
export PATH="$(pwd)/../../../../out.gfch100_defconfig/host/usr/bin:$PATH"
-pid=
+pid1=
+pid2=
restart() {
- [ -n "$pid" ] && kill $pid
+ [ -n "$pid1" ] && kill $pid1
+ [ -n "$pid2" ] && kill $pid2
echo "######################################################################"
echo "# starting craftui"
gpylint *.py
make test
./craftui &
- pid=$!
+ pid1=$!
+ ./craftui -2 &
+ pid2=$!
touch .started
}
onExit() {
- [ -n "$pid" ] && kill $pid
+ [ -n "$pid1" ] && kill $pid1
+ [ -n "$pid2" ] && kill $pid2
exit 1
}
@@ -26,7 +31,7 @@
restart
while sleep 1; do
- if ! kill -0 $pid; then
+ if ! kill -0 $pid1 || ! kill -0 $pid2; then
restart
continue
fi
diff --git a/craftui/HOW.updatesim b/craftui/HOW.updatesim
index d5f2e6a..f01d964 100644
--- a/craftui/HOW.updatesim
+++ b/craftui/HOW.updatesim
@@ -1,27 +1,31 @@
#! /bin/sh
-ssh chimera '
- rm -rf /tmp/sim;
- mkdir -p /tmp/sim/proc && cat /proc/uptime > /tmp/sim/proc/uptime;
- for n in /sys/class/net/*/statistics/*; do
- mkdir -p /tmp/sim/$(dirname $n);
- test ! -d $n && cat $n > /tmp/sim/$n;
- done;
- ip -o -d link > /tmp/sim/ip.link.txt;
- ip -o addr > /tmp/sim/ip.addr.txt;
- presterastats > /tmp/sim/presterastats.json;
- '
+for suffix in 1 2; do
-ssh chimera cd / "&&" tar czf - -C / \
- config/settings \
- etc/platform \
- etc/serial \
- etc/softwaredate \
- etc/version \
- tmp/glaukus \
- tmp/serial \
- tmp/ssl \
- tmp/platform \
- tmp/gpio \
- tmp/sim \
- > sim.tgz
+ ssh chimera$suffix '
+ rm -rf /tmp/sim;
+ mkdir -p /tmp/sim/proc && cat /proc/uptime > /tmp/sim/proc/uptime;
+ for n in /sys/class/net/*/statistics/*; do
+ mkdir -p /tmp/sim/$(dirname $n);
+ test ! -d $n && cat $n > /tmp/sim/$n;
+ done;
+ ip -o -d link > /tmp/sim/ip.link.txt;
+ ip -o addr > /tmp/sim/ip.addr.txt;
+ presterastats > /tmp/sim/presterastats.json;
+ '
+
+ ssh chimera$suffix cd / "&&" tar czf - -C / \
+ config/settings \
+ etc/platform \
+ etc/serial \
+ etc/softwaredate \
+ etc/version \
+ tmp/glaukus \
+ tmp/serial \
+ tmp/ssl \
+ tmp/platform \
+ tmp/gpio \
+ tmp/sim \
+ > sim$suffix.tgz
+
+done
diff --git a/craftui/Makefile b/craftui/Makefile
index 782ca22..dbb2082 100644
--- a/craftui/Makefile
+++ b/craftui/Makefile
@@ -15,14 +15,14 @@
install-libs:
@echo "No libs to install."
-.sim.extracted: sim.tgz
- -chmod -R +w sim
- rm -rf sim
- rsync -av sim-tools/ sim
- tar xf sim.tgz -C sim
+.sim%.extracted: sim%.tgz
+ -chmod -R +w sim$*
+ rm -rf sim$*
+ rsync -av sim-tools/ sim$*
+ tar xf sim$*.tgz -C sim$*
touch $@
-test: .sim.extracted lint
+test: .sim1.extracted .sim2.extracted lint
set -e; \
for n in $(wildcard ./*_test.*); do \
echo; \
diff --git a/craftui/craftui b/craftui/craftui
index 527eba7..9d2a17a 100755
--- a/craftui/craftui
+++ b/craftui/craftui
@@ -7,15 +7,22 @@
# in developer environment if vendor/google/catawapus is above us
if [ -d "$devcw" ]; then
- isdev=1
+ sim=1
+fi
+
+if [ -n "$sim" ] && [ "$1" = "-2" ]; then
+ sim=2
+ shift
fi
# if running from developer desktop, use simulated data
-if [ "$isdev" = 1 ]; then
+if [ -n "$sim" ]; then
cw="$devcw"
- args="$args --http-port=8888 --https-port=8889 --sim=./sim"
+ args="$args --http-port=$((8888+2*($sim-1)))"
+ args="$args --https-port=$((8889+2*($sim-1)))"
+ args="$args --sim=./sim$sim"
pycode=./craftui_fortesting.py
- export PATH="$PWD/sim/bin:$PATH"
+ export PATH="$PWD/sim1/bin:$PATH"
fi
# for debugging on the device, use the local (/tmp/www?) web tree
@@ -32,13 +39,6 @@
continue
fi
- # enable https
- if [ "$1" = -S ]; then
- httpsmode="-S"
- shift
- continue
- fi
-
echo "$0: '$1': unknown command line option" >&2
exit 1
done
diff --git a/craftui/craftui.py b/craftui/craftui.py
index 10f5e91..2971729 100755
--- a/craftui/craftui.py
+++ b/craftui/craftui.py
@@ -251,6 +251,7 @@
class Glaukus(Config):
"""Configure using glaukus json api."""
+ baseurl = 'http://localhost:8080'
def __init__(self, validator, api, fmt):
super(Glaukus, self).__init__(validator)
@@ -262,7 +263,7 @@
print 'Glaukus: ', url, payload
try:
- fd = urllib2.urlopen(url, payload)
+ fd = urllib2.urlopen(url, payload, timeout=2)
except urllib2.URLError as ex:
print 'Connection to %s failed: %s' % (url, ex.reason)
raise ConfigError('failed to contact glaukus')
@@ -275,7 +276,7 @@
raise ConfigError('failed to configure glaukus')
def Configure(self):
- url = 'http://localhost:8080' + self.api
+ url = self.baseurl + self.api
payload = self.fmt % self.validator.config
self.CallJson(url, payload)
@@ -288,16 +289,16 @@
def Configure(self):
enable = self.validator.config
- if enable:
- url = '/api/modem/acm'
+ if enable is 'true':
+ url = self.baseurl + '/api/modem/acm'
payload = '{"rxSensorsEnabled":true,"txSwitchEnabled":true}'
self.CallJson(url, payload)
else:
- url = '/api/modem/acm'
+ url = self.baseurl + '/api/modem/acm'
payload = '{"rxSensorsEnabled":false,"txSwitchEnabled":false}'
self.CallJson(url, payload)
- url = '/api/modem/acm/profile'
+ url = self.baseurl + '/api/modem/acm/profile'
payload = '{"profileIndex":0,"isLocal":true}'
self.CallJson(url, payload)
@@ -381,16 +382,18 @@
'tx_errors',
'tx_dropped'
]
- realm = 'gfch100'
def __init__(self, wwwroot, http_port, https_port, sim):
- """initialize."""
+ """Initialize."""
self.wwwroot = wwwroot
self.http_port = http_port
self.https_port = https_port
self.sim = sim
self.data = {}
self.data['refreshCount'] = 0
+ platform = self.ReadFile(sim + '/etc/platform')
+ serial = self.ReadFile(sim + '/etc/serial')
+ self.realm = '%s-%s' % (platform, serial)
def ApplyChanges(self, changes):
"""Apply changes to system."""
@@ -556,6 +559,7 @@
return response
def GetUserCreds(self, user):
+ """Create a dict with the requested password."""
if user not in ('admin', 'guest'):
return None
b64 = self.ReadFile('%s/config/settings/password_%s' % (self.sim, user))
@@ -583,32 +587,110 @@
"""Common class to add args to html template."""
auth = 'unset'
+ def IsProxy(self):
+ """Check if this request was proxied, (ie, we are the peer)."""
+ return self.request.headers.get('craftui-proxy', 0) == '1'
+
+ def IsPeer(self):
+ """Check args to see if this is a request for the peer."""
+ return self.get_argument('peer', default='0') == '1'
+
+ def IsHttps(self):
+ """See if https:// was used."""
+ return (self.request.protocol == 'https' or
+ self.request.headers.get('craftui-https', 0) == '1')
+
def TemplateArgs(self):
+ """Build template args to dynamically adjust html file."""
+ is_https = self.IsHttps()
+ is_proxy = self.IsProxy()
+
+ peer_arg = '?peer=1'
+
args = {}
- args['hidepeer'] = ''
- args['hidehttps'] = ''
- args['peer'] = ''
- if self.request.protocol is 'https':
- args['hidehttps'] = 'hidden'
- if self.get_argument('peer', default='0') == '1':
- args['peer'] = '?peer=1'
- args['hidepeer'] = 'hidden'
- print args
+ args['hidden_on_https'] = 'hidden' if is_https else ''
+ args['hidden_on_peer'] = 'hidden' if is_proxy else ''
+ args['shown_on_peer'] = 'hidden' if not is_proxy else ''
+ args['peer_arg'] = peer_arg
+ args['peer_arg_on_peer'] = peer_arg if is_proxy else ''
return args
- def get(self):
- ui = self.settings['ui']
- if self.auth is 'any':
- if not ui.Authenticate(self):
- return
- elif self.auth is 'admin':
- if not ui.AuthenticateAdmin(self):
- return
- elif self.auth is not 'none':
- raise Exception('unknown authentication type "%s"' % self.auth)
+ def TryProxy(self):
+ """Check if we should proxy this request to the peer."""
+ if not self.IsPeer() or self.IsProxy():
+ return False
+ self.Proxy()
+ return True
+ class ErrorHandler(urllib2.HTTPDefaultErrorHandler):
+ """Catch the error, don't raise exception."""
+ error = {}
+
+ def http_error_default(self, req, fd, code, msg, hdrs):
+ self.error = {
+ 'request': req,
+ 'fd': fd,
+ 'code': code,
+ 'msg': msg,
+ 'hdrs': hdrs
+ }
+
+ def Proxy(self):
+ """Proxy to the peer."""
+ ui = self.settings['ui']
+ r = self.request
+ cs = '/config/settings/'
+ peer_ipaddr = ui.ReadFile(ui.sim + cs + 'peer_ipaddr')
+ peer_ipaddr = re.sub(r'/\d+$', '', peer_ipaddr)
+ if ui.sim:
+ peer_ipaddr = 'localhost:8890'
+ url = 'http://' + peer_ipaddr + r.uri
+ print 'proxy: ', url
+
+ eh = self.ErrorHandler()
+ opener = urllib2.build_opener(eh)
+
+ body = None
+ if r.method == 'POST':
+ body = '' if r.body is None else r.body
+ req = urllib2.Request(url, body, r.headers)
+ req.add_header('CraftUI-Proxy', 1)
+ req.add_header('CraftUI-Https', int(self.IsHttps()))
+ fd = opener.open(req, timeout=2)
+ if eh.error:
+ fd = eh.error['fd']
+ self.set_status(eh.error['code'])
+ hdrs = eh.error['hdrs']
+ for h in hdrs:
+ v = hdrs.get(h)
+ self.set_header(h, v)
+
+ response = fd.read()
+ if response:
+ self.write(response)
+ self.finish()
+
+ def Authenticated(self):
+ """Authenticate the user per the required auth type."""
+ ui = self.settings['ui']
+ if self.auth == 'any':
+ if not ui.Authenticate(self):
+ return False
+ elif self.auth == 'admin':
+ if not ui.AuthenticateAdmin(self):
+ return False
+ elif self.auth != 'none':
+ raise Exception('unknown authentication type "%s"' % self.auth)
+ return True
+
+ def get(self):
+ if self.TryProxy():
+ return
+ if not self.Authenticated():
+ return
+ ui = self.settings['ui']
path = ui.wwwroot + '/' + self.page + '.thtml'
- print 'GET %s page' % self.page
+ print '%s %s page (%s)' % (self.request.method, self.page, ui.sim)
self.render(path, **self.TemplateArgs())
class WelcomeHandler(CraftHandler):
@@ -625,22 +707,29 @@
class JsonHandler(CraftHandler):
"""Provides JSON-formatted content to be displayed in the UI."""
+ page = 'json'
def get(self):
- ui = self.settings['ui']
- if not ui.Authenticate(self):
+ if self.TryProxy():
return
- print 'GET json data'
+ self.auth = 'any'
+ if not self.Authenticated():
+ return
+ ui = self.settings['ui']
+ print '%s %s page (%s)' % (self.request.method, self.page, ui.sim)
jsonstring = ui.GetData()
self.set_header('Content-Type', 'application/json')
self.write(jsonstring)
self.finish()
def post(self):
- ui = self.settings['ui']
- if not ui.AuthenticateAdmin(self):
+ if self.TryProxy():
return
- print 'POST JSON data for craft page'
+ self.auth = 'admin'
+ if not self.Authenticated():
+ return
+ ui = self.settings['ui']
+ print '%s %s page (%s)' % (self.request.method, self.page, ui.sim)
request = self.request.body
result = {}
result['error'] = 0
diff --git a/craftui/craftui_test.sh b/craftui/craftui_test.sh
index 5fc11ce..677f719 100755
--- a/craftui/craftui_test.sh
+++ b/craftui/craftui_test.sh
@@ -52,7 +52,7 @@
onexit() {
testname "process not running at exit"
- kill -0 $pid
+ kill -0 $pid1
check_failure
testname "end of script reached"
@@ -86,15 +86,19 @@
# add a signed cert (using our fake CA)
sh HOW.cert
- chmod 750 sim/tmp/ssl/*
- cp tmp-certs/localhost.pem sim/tmp/ssl/certs/craftui.pem
- cp tmp-certs/localhost.key sim/tmp/ssl/private/craftui.key
+ for n in sim1 sim2; do
+ chmod 750 $n/tmp/ssl/*
+ cp tmp-certs/localhost.pem $n/tmp/ssl/certs/craftui.pem
+ cp tmp-certs/localhost.key $n/tmp/ssl/private/craftui.key
+ done
./craftui &
- pid=$!
+ pid1=$!
+ ./craftui -2 &
+ pid2=$!
testname "process running"
- kill -0 $pid
+ kill -0 $pid1
check_success
sleep 1
@@ -225,15 +229,36 @@
$curl $admin_auth -d "$d" $url/content.json |& grep '"error": 1}'
check_success
+ testname proxy read from peer
+ $curl $admin_auth $url/content.json'?peer=1' |& grep '"platform": "GFCH100"'
+ check_success
+
+ testname proxy write to peer
+ d='{"config":[{"peer_ipaddr":"192.168.99.99/24"}]}'
+ $curl $admin_auth -d $d $url/content.json'?peer=1' |& grep '"error": 0}'
+ check_success
+
+ done
+
+ # verify insecure message is hidden on https and not on http
+ for peer in '' '?peer=1'; do
+ testname http warning $peer
+ $curl http://localhost:$http$peer |& grep 'hidden_on_https value=""'
+ check_success
+
+ testname no https warning $peer
+ $curl https://localhost:$https$peer |& grep 'hidden_on_https value="hidden"'
+ check_success
done
testname "process still running at end of test sequence"
- kill -0 $pid
+ kill -0 $pid1
check_success
# cleanup
t0=$(date +%s)
- kill $pid
+ kill $pid1
+ kill $pid2
wait
t1=$(date +%s)
dt=$((t1 - t0))
diff --git a/craftui/sim.tgz b/craftui/sim.tgz
deleted file mode 100644
index d50297f..0000000
--- a/craftui/sim.tgz
+++ /dev/null
Binary files differ
diff --git a/craftui/sim1.tgz b/craftui/sim1.tgz
new file mode 100644
index 0000000..e1d1a97
--- /dev/null
+++ b/craftui/sim1.tgz
Binary files differ
diff --git a/craftui/sim2.tgz b/craftui/sim2.tgz
new file mode 100644
index 0000000..a69b101
--- /dev/null
+++ b/craftui/sim2.tgz
Binary files differ
diff --git a/craftui/www/config.thtml b/craftui/www/config.thtml
index 1227ad0..88ed88f 100644
--- a/craftui/www/config.thtml
+++ b/craftui/www/config.thtml
@@ -14,16 +14,23 @@
<h1><img src=static/logo.png alt="Google Fiber"></h1>
<nav>
<ul>
- <li ><a href=/{{peer}}>Welcome</a></li>
- <li ><a href=/status{{peer}}>Status</a></li>
- <li class=active><a href=/config{{peer}}>Configuration</a></li>
- <li ><a {{hidepeer}} href="/?peer=1" target=_blank>Peer</a></li>
+ <li ><a href=/{{peer_arg_on_peer}}>Welcome</a></li>
+ <li ><a href=/status{{peer_arg_on_peer}}>Status</a></li>
+ <li class=active><a href=/config{{peer_arg_on_peer}}>Configuration</a></li>
+ <li ><a {{hidden_on_peer}} href="/{{peer_arg}}" target=_blank>Peer</a></li>
</ul>
</nav>
</section>
</header>
<br>
-
+ <div hidden>
+ <input id=hidden_on_https value="{{hidden_on_https}}">
+ <input id=hidden_on_peer value="{{hidden_on_peer}}">
+ <input id=shown_on_peer value="{{shown_on_peer}}">
+ <input id=peer_arg value="{{peer_arg}}">
+ <input id=peer_arg_on_peer value="{{peer_arg_on_peer}}">
+ </div>
+ <div {{shown_on_peer}}><font color="red"><b>This is the Peer</b></font></div>
<div class="tabs">
<div class="tab">
<input type="radio" id="tab-1" name="tab-group-1" checked>
@@ -218,7 +225,7 @@
<td>
<span id=tx_powerlevel_result>...</span>
- <tr>
+ <tr hidden>
<td><b>Transmit VGA Gain
<td align=right><span id="radio/tx/vgaGain">...</span>
<td>
@@ -227,7 +234,7 @@
<td>
<span id=tx_gain_result>...</span>
- <tr>
+ <tr hidden>
<td><b>Receiver AGC Digital Gain Index
<td align=right><span id="radio/rx/agcDigitalGainIndex">...</span>
<td>
diff --git a/craftui/www/static/craft.js b/craftui/www/static/craft.js
index fe7d9bc..7b539ba 100644
--- a/craftui/www/static/craft.js
+++ b/craftui/www/static/craft.js
@@ -64,6 +64,7 @@
if (CraftUI.am_sending) {
return;
}
+ var peer_arg_on_peer = document.getElementById("peer_arg_on_peer").value;
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
self.unhandled = '';
@@ -74,20 +75,18 @@
CraftUI.updateField('unhandled', self.unhandled);
CraftUI.am_sending = false
};
- var payload = [];
- payload.push('checksum=' + encodeURIComponent(CraftUI.info.checksum));
- payload.push('_=' + encodeURIComponent((new Date()).getTime()));
- xhr.open('get', 'content.json?' + payload.join('&'), true);
+ xhr.open('get', '/content.json' + peer_arg_on_peer, true);
CraftUI.am_sending = true
xhr.send();
};
CraftUI.config = function(key, activate, is_password) {
// POST as json
+ var peer_arg_on_peer = document.getElementById("peer_arg_on_peer").value;
var el = document.getElementById(key);
var xhr = new XMLHttpRequest();
var action = "Configured";
- xhr.open('post', 'content.json');
+ xhr.open('post', '/content.json' + peer_arg_on_peer);
xhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8');
var data;
if (is_password) {
diff --git a/craftui/www/status.thtml b/craftui/www/status.thtml
index 28b5027..f2a4ee9 100644
--- a/craftui/www/status.thtml
+++ b/craftui/www/status.thtml
@@ -14,15 +14,23 @@
<h1><img src=static/logo.png alt="Google Fiber"></h1>
<nav>
<ul>
- <li ><a href=/?{{peer}}>Welcome</a></li>
- <li class=active><a href=/status{{peer}}>Status</a></li>
- <li ><a href=/config{{peer}}>Configuration</a></li>
- <li ><a {{hidepeer}} href="/?peer=1" target=_blank>Peer</a></li>
+ <li ><a href=/{{peer_arg_on_peer}}>Welcome</a></li>
+ <li class=active><a href=/status{{peer_arg_on_peer}}>Status</a></li>
+ <li ><a href=/config{{peer_arg_on_peer}}>Configuration</a></li>
+ <li ><a {{hidden_on_peer}} href="/{{peer_arg}}" target=_blank>Peer</a></li>
</ul>
</nav>
</section>
</header>
<br>
+ <div hidden>
+ <input id=hidden_on_https value="{{hidden_on_https}}">
+ <input id=hidden_on_peer value="{{hidden_on_peer}}">
+ <input id=shown_on_peer value="{{shown_on_peer}}">
+ <input id=peer_arg value="{{peer_arg}}">
+ <input id=peer_arg_on_peer value="{{peer_arg_on_peer}}">
+ </div>
+ <div {{shown_on_peer}}><font color="red"><b>This is the Peer</b></font></div>
<div class="tabs">
<div class="tab">
<input type="radio" id="tab-1" name="tab-group-1" checked>
@@ -218,6 +226,7 @@
<td><b></b></td>
<td colspan=4 align=center><b>received</b></td>
<td colspan=4 align=center><b>transmitted</b></td>
+ <td colspan=3 align=center><b>errors</b></td></tr>
<tr>
<td align=center><b>interface</b></td>
@@ -231,53 +240,89 @@
<td align=center><b>broadcast</b></td>
<td align=center><b>unicast</b></td>
+ <td align=center><b>CRC</b></td>
+ <td align=center><b>dropped</b></td>
+ <td align=center><b>collisions</b></td>
+
<tr>
<td><b>Switch Port 0/0 (PoE)</b></td>
- <td align=right><span id="platform/switch/0/0/bytes_received">...</span></td>
- <td align=right><span id="platform/switch/0/0/multicast_packets_received">...</span></td>
- <td align=right><span id="platform/switch/0/0/broadcast_packets_received">...</span></td>
- <td align=right><span id="platform/switch/0/0/unicast_packets_received">...</span></td>
+ <td align=right><span id="platform/switch/0/0/goodOctetsRcv">...</span></td>
+ <td align=right><span id="platform/switch/0/0/mcPktsRcv">...</span></td>
+ <td align=right><span id="platform/switch/0/0/brdcPktsRcv">...</span></td>
+ <td align=right><span id="platform/switch/0/0/ucPktsRcv">...</span></td>
- <td align=right><span id="platform/switch/0/0/bytes_sent">...</span></td>
- <td align=right><span id="platform/switch/0/0/multicast_packets_sent">...</span></td>
- <td align=right><span id="platform/switch/0/0/broadcast_packets_sent">...</span></td>
- <td align=right><span id="platform/switch/0/0/unicast_packets_sent">...</span></td>
+ <td align=right><span id="platform/switch/0/0/goodOctetsSent">...</span></td>
+ <td align=right><span id="platform/switch/0/0/mcPktsSent">...</span></td>
+ <td align=right><span id="platform/switch/0/0/brdcPktsSent">...</span></td>
+ <td align=right><span id="platform/switch/0/0/ucPktsSent">...</span></td>
+
+ <td align=right><span id="platform/switch/0/0/badCrc">...</span></td>
+ <td align=right><span id="platform/switch/0/0/dropEvents">...</span></td>
+ <td align=right><span id="platform/switch/0/0/collisions">...</span></td>
<tr>
<td><b>Switch Port 0/4 (SOC)</b></td>
- <td align=right><span id="platform/switch/0/4/bytes_received">...</span></td>
- <td align=right><span id="platform/switch/0/4/multicast_packets_received">...</span></td>
- <td align=right><span id="platform/switch/0/4/broadcast_packets_received">...</span></td>
- <td align=right><span id="platform/switch/0/4/unicast_packets_received">...</span></td>
+ <td align=right><span id="platform/switch/0/4/goodOctetsRcv">...</span></td>
+ <td align=right><span id="platform/switch/0/4/mcPktsRcv">...</span></td>
+ <td align=right><span id="platform/switch/0/4/brdcPktsRcv">...</span></td>
+ <td align=right><span id="platform/switch/0/4/ucPktsRcv">...</span></td>
- <td align=right><span id="platform/switch/0/4/bytes_sent">...</span></td>
- <td align=right><span id="platform/switch/0/4/multicast_packets_sent">...</span></td>
- <td align=right><span id="platform/switch/0/4/broadcast_packets_sent">...</span></td>
- <td align=right><span id="platform/switch/0/4/unicast_packets_sent">...</span></td>
+ <td align=right><span id="platform/switch/0/4/goodOctetsSent">...</span></td>
+ <td align=right><span id="platform/switch/0/4/mcPktsSent">...</span></td>
+ <td align=right><span id="platform/switch/0/4/brdcPktsSent">...</span></td>
+ <td align=right><span id="platform/switch/0/4/ucPktsSent">...</span></td>
+
+ <td align=right><span id="platform/switch/0/4/badCrc">...</span></td>
+ <td align=right><span id="platform/switch/0/4/dropEvents">...</span></td>
+ <td align=right><span id="platform/switch/0/4/collisions">...</span></td>
<tr>
<td><b>Switch Port 0/24 (modem)</b></td>
- <td align=right><span id="platform/switch/0/24/bytes_received">...</span></td>
- <td align=right><span id="platform/switch/0/24/multicast_packets_received">...</span></td>
- <td align=right><span id="platform/switch/0/24/broadcast_packets_received">...</span></td>
- <td align=right><span id="platform/switch/0/24/unicast_packets_received">...</span></td>
+ <td align=right><span id="platform/switch/0/24/goodOctetsRcv">...</span></td>
+ <td align=right><span id="platform/switch/0/24/mcPktsRcv">...</span></td>
+ <td align=right><span id="platform/switch/0/24/brdcPktsRcv">...</span></td>
+ <td align=right><span id="platform/switch/0/24/ucPktsRcv">...</span></td>
- <td align=right><span id="platform/switch/0/24/bytes_sent">...</span></td>
- <td align=right><span id="platform/switch/0/24/multicast_packets_sent">...</span></td>
- <td align=right><span id="platform/switch/0/24/broadcast_packets_sent">...</span></td>
- <td align=right><span id="platform/switch/0/24/unicast_packets_sent">...</span></td>
+ <td align=right><span id="platform/switch/0/24/goodOctetsSent">...</span></td>
+ <td align=right><span id="platform/switch/0/24/mcPktsSent">...</span></td>
+ <td align=right><span id="platform/switch/0/24/brdcPktsSent">...</span></td>
+ <td align=right><span id="platform/switch/0/24/ucPktsSent">...</span></td>
+
+ <td align=right><span id="platform/switch/0/24/badCrc">...</span></td>
+ <td align=right><span id="platform/switch/0/24/dropEvents">...</span></td>
+ <td align=right><span id="platform/switch/0/24/collisions">...</span></td>
<tr>
- <td><b>Switch Port 0/25 (SFP+)</b></td>
- <td align=right><span id="platform/switch/0/25/bytes_received">...</span></td>
- <td align=right><span id="platform/switch/0/25/multicast_packets_received">...</span></td>
- <td align=right><span id="platform/switch/0/25/broadcast_packets_received">...</span></td>
- <td align=right><span id="platform/switch/0/25/unicast_packets_received">...</span></td>
+ <td><b>Switch Port 0/25 (SFP+ #1)</b></td>
+ <td align=right><span id="platform/switch/0/25/goodOctetsRcv">...</span></td>
+ <td align=right><span id="platform/switch/0/25/mcPktsRcv">...</span></td>
+ <td align=right><span id="platform/switch/0/25/brdcPktsRcv">...</span></td>
+ <td align=right><span id="platform/switch/0/25/ucPktsRcv">...</span></td>
- <td align=right><span id="platform/switch/0/25/bytes_sent">...</span></td>
- <td align=right><span id="platform/switch/0/25/multicast_packets_sent">...</span></td>
- <td align=right><span id="platform/switch/0/25/broadcast_packets_sent">...</span></td>
- <td align=right><span id="platform/switch/0/25/unicast_packets_sent">...</span></td>
+ <td align=right><span id="platform/switch/0/25/goodOctetsSent">...</span></td>
+ <td align=right><span id="platform/switch/0/25/mcPktsSent">...</span></td>
+ <td align=right><span id="platform/switch/0/25/brdcPktsSent">...</span></td>
+ <td align=right><span id="platform/switch/0/25/ucPktsSent">...</span></td>
+
+ <td align=right><span id="platform/switch/0/25/badCrc">...</span></td>
+ <td align=right><span id="platform/switch/0/25/dropEvents">...</span></td>
+ <td align=right><span id="platform/switch/0/25/collisions">...</span></td>
+
+ <tr>
+ <td><b>Switch Port 0/26 (SFP+ #2)</b></td>
+ <td align=right><span id="platform/switch/0/26/goodOctetsRcv">...</span></td>
+ <td align=right><span id="platform/switch/0/26/mcPktsRcv">...</span></td>
+ <td align=right><span id="platform/switch/0/26/brdcPktsRcv">...</span></td>
+ <td align=right><span id="platform/switch/0/26/ucPktsRcv">...</span></td>
+
+ <td align=right><span id="platform/switch/0/26/goodOctetsSent">...</span></td>
+ <td align=right><span id="platform/switch/0/26/mcPktsSent">...</span></td>
+ <td align=right><span id="platform/switch/0/26/bcPktsSent">...</span></td>
+ <td align=right><span id="platform/switch/0/26/ucPktsSent">...</span></td>
+
+ <td align=right><span id="platform/switch/0/26/badCrc">...</span></td>
+ <td align=right><span id="platform/switch/0/26/dropEvents">...</span></td>
+ <td align=right><span id="platform/switch/0/26/collisions">...</span></td>
</table>
</div>
diff --git a/craftui/www/welcome.thtml b/craftui/www/welcome.thtml
index de7c098..43966ff 100644
--- a/craftui/www/welcome.thtml
+++ b/craftui/www/welcome.thtml
@@ -14,21 +14,29 @@
<h1><img src=static/logo.png alt="Google Fiber"></h1>
<nav>
<ul>
- <li class=active><a href=/{{peer}}>Welcome</a></li>
- <li ><a href=/status{{peer}}>Status</a></li>
- <li ><a href=/config{{peer}}>Configuration</a></li>
- <li ><a {{hidepeer}} href="/?peer=1" target=_blank>Peer</a></li>
+ <li class=active><a href=/{{peer_arg_on_peer}}>Welcome</a></li>
+ <li ><a href=/status{{peer_arg_on_peer}}>Status</a></li>
+ <li ><a href=/config{{peer_arg_on_peer}}>Configuration</a></li>
+ <li ><a {{hidden_on_peer}} href="/{{peer_arg}}" target=_blank>Peer</a></li>
</ul>
</nav>
</section>
</header>
<br>
+ <div hidden>
+ <input id=hidden_on_https value="{{hidden_on_https}}">
+ <input id=hidden_on_peer value="{{hidden_on_peer}}">
+ <input id=shown_on_peer value="{{shown_on_peer}}">
+ <input id=peer_arg value="{{peer_arg}}">
+ <input id=peer_arg_on_peer value="{{peer_arg_on_peer}}">
+ </div>
+ <div {{shown_on_peer}}><font color="red"><b>This is the Peer</b></font></div>
<div class="tabs">
<div class="tab">
<input type="radio" id="tab-1" name="tab-group-1" checked>
<label for="tab-1">Authorized Use Only</label>
<div class="content">
- <div {{hidehttps}}>
+ <div {{hidden_on_https}}>
<b>
Warning: You are not connected securely. Consider https://...
<br>
@@ -40,9 +48,6 @@
<p>
Unauthorized access to this system is forbidden and will be
prosecuted by law.
- <br>
- By accessing this system, you agree that your actions may be
- monitored if unauthorized usage is suspected.
</div>
</div>
</div>
diff --git a/jsonpoll/jsonpoll.py b/jsonpoll/jsonpoll.py
index bee52a0..11f9c64 100755
--- a/jsonpoll/jsonpoll.py
+++ b/jsonpoll/jsonpoll.py
@@ -106,15 +106,15 @@
try:
response = self.GetHttpResponse(url)
if not response:
- self.WriteToStderr('Failed to get response from glaukus: %s', url)
+ self.WriteToStderr('Failed to get response from glaukus: %s\n' % url)
continue
elif self.last_response == response:
- self.WriteToStderr('Skipping file write as content has not changed.')
+ self.WriteToStderr('Skip file write as content has not changed.\n')
continue
self.last_response = response
with tempfile.NamedTemporaryFile(delete=False) as fd:
if not self.CreateDirs(os.path.dirname(output_file)):
- self.WriteToStderr('Failed to create output directory: %s' %
+ self.WriteToStderr('Failed to create output directory: %s\n' %
os.path.dirname(output_file))
continue
tmpfile = fd.name
@@ -124,7 +124,7 @@
try:
os.rename(tmpfile, output_file)
except OSError as ex:
- self.WriteToStderr('Failed to move %s to %s: %s' % (
+ self.WriteToStderr('Failed to move %s to %s: %s\n' % (
tmpfile, output_file, ex))
continue
finally:
@@ -137,11 +137,11 @@
handle = urllib2.urlopen(url, timeout=self._SOCKET_TIMEOUT_SECS)
response = handle.read()
except socket.timeout as ex:
- self.WriteToStderr('Connection to %s timed out after %d seconds: %s'
+ self.WriteToStderr('Connection to %s timed out after %d seconds: %s\n'
% (url, self._SOCKET_TIMEOUT_SECS, ex))
return None
except urllib2.URLError as ex:
- self.WriteToStderr('Connection to %s failed: %s' % (url, ex.reason))
+ self.WriteToStderr('Connection to %s failed: %s\n' % (url, ex.reason))
return None
# Write the response to stderr so it will be uploaded with the other system
# log files. This will allow turbogrinder to alert on the radio subsystem.
@@ -155,7 +155,7 @@
except os.error as ex:
if ex.errno == errno.EEXIST:
return True
- self.WriteToStderr('Failed to create directory: %s' % ex)
+ self.WriteToStderr('Failed to create directory: %s\n' % ex)
return False
return True
diff --git a/waveguide/waveguide.py b/waveguide/waveguide.py
index cbc9fe8..87324c3 100755
--- a/waveguide/waveguide.py
+++ b/waveguide/waveguide.py
@@ -907,7 +907,8 @@
wifiblaster.enable Enable WiFi performance measurement.
wifiblaster.interval Average time between automated measurements in
seconds, or 0 to disable automated measurements.
- wifiblaster.measureall Unix time at which to measure all clients.
+ wifiblaster.measureall Unix time at which to measure all clients, or 0
+ to disable measureall requests.
wifiblaster.onassociation Enable WiFi performance measurement after clients
associate.
@@ -938,6 +939,20 @@
except ValueError:
return None
+ def _GetParameters(self):
+ """Reads and returns all parameters if valid, or Nones."""
+ duration = self._ReadParameter('duration', float)
+ enable = self._ReadParameter('enable', self._StrToBool)
+ fraction = self._ReadParameter('fraction', int)
+ interval = self._ReadParameter('interval', float)
+ measureall = self._ReadParameter('measureall', float)
+ onassociation = self._ReadParameter('onassociation', self._StrToBool)
+ size = self._ReadParameter('size', int)
+ if (duration > 0 and enable and fraction > 0 and interval >= 0
+ and measureall >= 0 and size > 0):
+ return (duration, fraction, interval, measureall, onassociation, size)
+ return (None, None, None, None, None, None)
+
def _SaveResult(self, line):
"""Save wifiblaster result to the status file for that client."""
g = re.search(MACADDR_REGEX, line)
@@ -975,23 +990,18 @@
"""Return the time of the next measurement event."""
return self._next_measurement_time
- def Measure(self, interface, client):
+ def Measure(self, interface, client, duration, fraction, size):
"""Measures the performance of a client."""
- enable = self._ReadParameter('enable', self._StrToBool)
- duration = self._ReadParameter('duration', float)
- fraction = self._ReadParameter('fraction', int)
- size = self._ReadParameter('size', int)
- if enable and duration > 0 and fraction > 0 and size > 0:
- RunProc(callback=self._HandleResults,
- args=[WIFIBLASTER_BIN, '-i', interface, '-d', str(duration),
- '-f', str(fraction), '-s', str(size),
- helpers.DecodeMAC(client)])
+ RunProc(callback=self._HandleResults,
+ args=[WIFIBLASTER_BIN, '-i', interface, '-d', str(duration),
+ '-f', str(fraction), '-s', str(size),
+ helpers.DecodeMAC(client)])
def MeasureOnAssociation(self, interface, client):
"""Measures the performance of a client after association."""
- onassociation = self._ReadParameter('onassociation', self._StrToBool)
+ (duration, fraction, _, _, onassociation, size) = self._GetParameters()
if onassociation:
- self.Measure(interface, client)
+ self.Measure(interface, client, duration, fraction, size)
def Poll(self, now):
"""Polls the state machine."""
@@ -1005,29 +1015,33 @@
# Inter-arrival times in a Poisson process are exponentially distributed.
# The timebase slip prevents a burst of measurements in case we fall
# behind.
- self._next_measurement_time = now + random.expovariate(1 / interval)
+ self._next_measurement_time = now + random.expovariate(1.0 / interval)
- interval = self._ReadParameter('interval', float)
- if interval <= 0:
+ # Read parameters.
+ (duration, fraction, interval, measureall, _, size) = self._GetParameters()
+
+ # Handle automated mode.
+ if interval > 0:
+ if self._interval != interval:
+ # Enable or change interval.
+ StartMeasurementTimer(interval)
+ elif now >= self._next_measurement_time:
+ # Measure a random client.
+ StartMeasurementTimer(interval)
+ try:
+ (interface, client) = random.choice(self._GetAllClients())
+ except IndexError:
+ pass
+ else:
+ self.Measure(interface, client, duration, fraction, size)
+ else:
Disable()
- elif self._interval != interval:
- # Enable or change interval.
- StartMeasurementTimer(interval)
- elif now >= self._next_measurement_time:
- # Measure a random client.
- StartMeasurementTimer(interval)
- try:
- (interface, client) = random.choice(self._GetAllClients())
- except IndexError:
- pass
- else:
- self.Measure(interface, client)
- measureall = self._ReadParameter('measureall', float)
+ # Handle measureall request.
if time.time() >= measureall and measureall > self._last_measureall_time:
self._last_measureall_time = measureall
for (interface, client) in self._GetAllClients():
- self.Measure(interface, client)
+ self.Measure(interface, client, duration, fraction, size)
# Poll again in at most one second. This allows parameter changes (e.g. a
# measureall request or a long interval to a short interval) to take effect
diff --git a/waveguide/wifiblaster_controller_test.py b/waveguide/wifiblaster_controller_test.py
index 6b562b1..9e300f2 100755
--- a/waveguide/wifiblaster_controller_test.py
+++ b/waveguide/wifiblaster_controller_test.py
@@ -17,6 +17,7 @@
import glob
import os
+import random
import shutil
import sys
import tempfile
@@ -102,6 +103,7 @@
d = tempfile.mkdtemp()
old_wifiblaster_dir = waveguide.WIFIBLASTER_DIR
waveguide.WIFIBLASTER_DIR = tempfile.mkdtemp()
+ oldexpovariate = random.expovariate
oldpath = os.environ['PATH']
oldtime = time.time
@@ -110,6 +112,7 @@
return faketime[0]
try:
+ random.expovariate = lambda lambd: random.uniform(0, 2 * 1.0 / lambd)
time.time = FakeTime
os.environ['PATH'] = 'fake:' + os.environ['PATH']
sys.path.insert(0, 'fake')
@@ -141,11 +144,6 @@
WriteConfig('measureall', '0')
WriteConfig('size', '1470')
- # Disabled. No measurements should be run.
- print manager.GetState()
- for t in xrange(0, 100):
- wc.Poll(t)
-
def CountRuns():
try:
v = open('fake/wifiblaster.out').readlines()
@@ -155,16 +153,24 @@
os.unlink('fake/wifiblaster.out')
return len(v)
- CountRuns() # get rid of any leftovers
+ # Get rid of any leftovers.
+ CountRuns()
+
+ # Disabled.
+ # No measurements should be run.
+ print manager.GetState()
+ for t in xrange(0, 100):
+ wc.Poll(t)
wvtest.WVPASSEQ(CountRuns(), 0)
+ # Enabled.
# The first measurement should be one cycle later than the start time.
# This is not an implementation detail: it prevents multiple APs from
# running simultaneous measurements if measurements are enabled at the
# same time.
WriteConfig('enable', 'True')
wc.Poll(100)
- wvtest.WVPASSGE(wc.NextMeasurement(), 100)
+ wvtest.WVPASSGT(wc.NextMeasurement(), 100)
for t in xrange(101, 200):
wc.Poll(t)
wvtest.WVPASSGE(CountRuns(), 1)
@@ -183,20 +189,16 @@
wc.Poll(t)
wvtest.WVPASSGE(CountRuns(), 1)
- # Run the measurement at t=400 to restart the timer.
- wc.Poll(400)
- wvtest.WVPASSGE(CountRuns(), 0)
-
# Next poll should be in at most one second regardless of interval.
- wvtest.WVPASSLE(wc.NextTimeout(), 401)
+ wvtest.WVPASSLE(wc.NextTimeout(), 400)
- # Enabled with longer average interval. The change in interval should
+ # Enabled with shorter average interval. The change in interval should
# trigger a change in next poll timeout.
WriteConfig('interval', '0.5')
old_to = wc.NextMeasurement()
- wc.Poll(401)
+ wc.Poll(400)
wvtest.WVPASSNE(old_to, wc.NextMeasurement())
- for t in xrange(402, 500):
+ for t in xrange(401, 500):
wc.Poll(t)
wvtest.WVPASSGE(CountRuns(), 1)
@@ -218,6 +220,7 @@
manager.GetState().assoc[0].mac)
wvtest.WVPASSEQ(CountRuns(), 1)
finally:
+ random.expovariate = oldexpovariate
time.time = oldtime
shutil.rmtree(d)
os.environ['PATH'] = oldpath
diff --git a/wifi/quantenna.py b/wifi/quantenna.py
index 755fb8b..39dfabf 100755
--- a/wifi/quantenna.py
+++ b/wifi/quantenna.py
@@ -120,12 +120,12 @@
try:
_ensure_initialized('ap')
+ _qcsapi('wifi_create_bss', lif, mac)
+ _qcsapi('set_ssid', lif, opt.ssid)
_qcsapi('set_bw', 'wifi0', opt.width)
_qcsapi('set_channel', 'wifi0',
149 if opt.channel == 'auto' else opt.channel)
- _qcsapi('wifi_create_bss', lif, mac)
- _qcsapi('set_ssid', lif, opt.ssid)
if opt.encryption == 'NONE':
_qcsapi('set_beacon_type', lif, 'Basic')
else:
@@ -164,9 +164,9 @@
try:
_ensure_initialized('sta')
+ _qcsapi('create_ssid', lif, opt.ssid)
_qcsapi('set_bw', 'wifi0', 80)
- _qcsapi('create_ssid', lif, opt.ssid)
if opt.bssid:
_qcsapi('set_ssid_bssid', lif, opt.ssid, opt.bssid)
if opt.encryption == 'NONE' or not os.environ.get('WIFI_CLIENT_PSK'):