Add the --use_interface_counters option.

This commit is contained in:
Marco Paganini 2015-09-12 13:38:00 -07:00
parent 93951f1154
commit 9e9df1117b
2 changed files with 75 additions and 6 deletions

View File

@ -58,6 +58,15 @@ URL of the Speedtest Mini server
Source IP address to bind to Source IP address to bind to
.RE .RE
\fB\-\-use_interface_counters INTF\fR
.RS
With this option the program uses the kernel counters for interface
"intf" to calculate the number of bytes transferred. This option is
useful when running this program on a computer that is the gateway
to the Internet, as it will include the bandwidth used by every user
of the system and thus provide more accurate bandwidth figures.
.RE
\fB\-\-version\fR \fB\-\-version\fR
.RS .RS
Show the version number and exit Show the version number and exit

View File

@ -16,6 +16,7 @@
# under the License. # under the License.
import os import os
import os.path
import re import re
import sys import sys
import math import math
@ -233,6 +234,33 @@ def catch_request(request):
return None, e return None, e
def get_interface_counter(intf, counter):
"""Retrieve a specific interface counter for interface 'intf'.
This requires a mounted /sys filesystem and is linux specific."""
try:
sysfile = os.path.join('/sys/class/net', intf, 'statistics', counter)
fd = open(sysfile)
except IOError as e:
raise IOError('Unable to access file in the sys filesystem: %s.\n'
'OS error: %s\n'
'Please make sure your sys filesystem is mounted.' %
(sysfile, e))
val = int(fd.readline().rstrip())
fd.close()
return val
def counter_diff(start, end):
"""Returns the difference between end and start, considering counter
wrap-arounds. Values are assumed to wrap at 64-bit boundaries."""
if end > start:
return end - start
else:
return ((2**64) - start) + end
class FileGetter(threading.Thread): class FileGetter(threading.Thread):
"""Thread class for retrieving a URL""" """Thread class for retrieving a URL"""
@ -257,11 +285,14 @@ class FileGetter(threading.Thread):
pass pass
def downloadSpeed(files, quiet=False): def downloadSpeed(files, quiet=False, net_interface=None):
"""Function to launch FileGetter threads and calculate download speeds""" """Function to launch FileGetter threads and calculate download speeds"""
start = timeit.default_timer() start = timeit.default_timer()
if net_interface:
start_bytes = get_interface_counter(net_interface, 'rx_bytes')
def producer(q, files): def producer(q, files):
for file in files: for file in files:
thread = FileGetter(file, start) thread = FileGetter(file, start)
@ -291,7 +322,17 @@ def downloadSpeed(files, quiet=False):
prod_thread.join(timeout=0.1) prod_thread.join(timeout=0.1)
while cons_thread.isAlive(): while cons_thread.isAlive():
cons_thread.join(timeout=0.1) cons_thread.join(timeout=0.1)
return (sum(finished) / (timeit.default_timer() - start))
elapsed = timeit.default_timer() - start
# If 'net_interface' is set, calculate the total bytes received
# based on the interface counters. Otherwise, use the total
# number of bytes in 'finished'.
if net_interface:
end_bytes = get_interface_counter(net_interface, 'rx_bytes')
return counter_diff(start_bytes, end_bytes) / elapsed
else:
return sum(finished) / elapsed
class FilePutter(threading.Thread): class FilePutter(threading.Thread):
@ -322,11 +363,14 @@ class FilePutter(threading.Thread):
self.result = 0 self.result = 0
def uploadSpeed(url, sizes, quiet=False): def uploadSpeed(url, sizes, quiet=False, net_interface=None):
"""Function to launch FilePutter threads and calculate upload speeds""" """Function to launch FilePutter threads and calculate upload speeds"""
start = timeit.default_timer() start = timeit.default_timer()
if net_interface:
start_bytes = get_interface_counter(net_interface, 'tx_bytes')
def producer(q, sizes): def producer(q, sizes):
for size in sizes: for size in sizes:
thread = FilePutter(url, start, size) thread = FilePutter(url, start, size)
@ -356,7 +400,17 @@ def uploadSpeed(url, sizes, quiet=False):
prod_thread.join(timeout=0.1) prod_thread.join(timeout=0.1)
while cons_thread.isAlive(): while cons_thread.isAlive():
cons_thread.join(timeout=0.1) cons_thread.join(timeout=0.1)
return (sum(finished) / (timeit.default_timer() - start))
elapsed = timeit.default_timer() - start
# If 'net_interface' is set, calculate the total bytes transmitted
# based on the interface counters. Otherwise, use the total
# number of bytes in 'finished'.
if net_interface:
end_bytes = get_interface_counter(net_interface, 'tx_bytes')
return counter_diff(start_bytes, end_bytes) / elapsed
else:
return sum(finished) / elapsed
def getAttributesByTagName(dom, tagName): def getAttributesByTagName(dom, tagName):
@ -590,6 +644,11 @@ def speedtest():
parser.add_argument('--secure', action='store_true', parser.add_argument('--secure', action='store_true',
help='Use HTTPS instead of HTTP when communicating ' help='Use HTTPS instead of HTTP when communicating '
'with speedtest.net operated servers') 'with speedtest.net operated servers')
parser.add_argument('--use_interface_counters',
help='Use the kernel counters for the specified '
'network interface to calculate the total '
'number of bytes transmitted/received '
'(Linux only).')
parser.add_argument('--version', action='store_true', parser.add_argument('--version', action='store_true',
help='Show the version number and exit') help='Show the version number and exit')
@ -716,7 +775,7 @@ def speedtest():
(os.path.dirname(best['url']), size, size)) (os.path.dirname(best['url']), size, size))
if not args.simple: if not args.simple:
print_('Testing download speed', end='') print_('Testing download speed', end='')
dlspeed = downloadSpeed(urls, args.simple) dlspeed = downloadSpeed(urls, args.simple, args.use_interface_counters)
if not args.simple: if not args.simple:
print_() print_()
print_('Download: %0.2f M%s/s' % print_('Download: %0.2f M%s/s' %
@ -729,7 +788,8 @@ def speedtest():
sizes.append(size) sizes.append(size)
if not args.simple: if not args.simple:
print_('Testing upload speed', end='') print_('Testing upload speed', end='')
ulspeed = uploadSpeed(best['url'], sizes, args.simple) ulspeed = uploadSpeed(best['url'], sizes,
args.simple, args.use_interface_counters)
if not args.simple: if not args.simple:
print_() print_()
print_('Upload: %0.2f M%s/s' % print_('Upload: %0.2f M%s/s' %