From 4d3261e66c69f5f361414548959d896621c84a7e Mon Sep 17 00:00:00 2001 From: Martin Prudek Date: Mon, 6 Nov 2017 11:09:55 +0100 Subject: [PATCH 01/11] netmetr: Temporary file names randomized. --- netmetr/netmetr | 40 +++++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/netmetr/netmetr b/netmetr/netmetr index 7aa6ae6..31ca08c 100755 --- a/netmetr/netmetr +++ b/netmetr/netmetr @@ -8,15 +8,17 @@ import locale import subprocess import shlex import os +from random import randint + -FLOWS_FILE = "/tmp/flows.json" -CONFIG_FILE = "/tmp/rmbt.cfg" RMBT_BIN = "rmbt" DEBUG = False # Debug printouts class Settings: def __init__(self): + self.flows_file = "/tmp/flows-"+str(randint(0, 1000000000))+".json" + self.config_file = "/tmp/rmbtcfg-"+str(randint(0, 1000000000))+".cfg" self.language = locale.getdefaultlocale()[0] self.timezone = subprocess.check_output(["date", "+%Z"])[:-1] if os.path.isfile("/etc/turris-version"): @@ -167,16 +169,16 @@ def measure_speed(sets): """Start RMBT client with saved arguments to measure the speed """ # Create config file needed by rmbt-client - if os.path.isfile(CONFIG_FILE): + if os.path.isfile(sets.config_file): try: - os.remove(CONFIG_FILE) + os.remove(sets.config_file) except Exception, e: print(e) return '' try: - with open(CONFIG_FILE, "w") as config_file: - config_file.write('{"cnf_file_flows": "'+FLOWS_FILE+'.xz"}') + with open(sets.config_file, "w") as config_file: + config_file.write('{"cnf_file_flows": "'+sets.flows_file+'.xz"}') except Exception, e: print("Error creating config file") print(e) @@ -188,19 +190,20 @@ def measure_speed(sets): RMBT_BIN, encryption.get(sets.test_server_encryption, ""), "-h", sets.test_server_address, "-p", str(sets.test_server_port), "-t", sets.test_token, "-f", sets.test_numthreads, "-d", - sets.test_duration, "-u", sets.test_duration, "-c", CONFIG_FILE]) + sets.test_duration, "-u", sets.test_duration, "-c", + sets.config_file]) if print_debug("Speed test result:"): print(test_result) return json.loads(test_result.split("}")[1] + "}") -def import_speed_flows(): +def import_speed_flows(sets): """The speedtest flow is saved to a file during the test. This function imports it so it could be sent to the control server. """ - if os.path.isfile(FLOWS_FILE): + if os.path.isfile(sets.flows_file): try: - os.remove(FLOWS_FILE) + os.remove(sets.flows_file) except Exception, e: print(e) return @@ -210,8 +213,8 @@ def import_speed_flows(): "ul": "upload" } try: - subprocess.call(shlex.split("unxz "+FLOWS_FILE+".xz")) - with open(FLOWS_FILE, 'r') as json_data: + subprocess.call(shlex.split("unxz "+sets.flows_file+".xz")) + with open(sets.flows_file, 'r') as json_data: flows_json = json.load(json_data) except Exception, e: print('Problem reading/decoding flows data.') @@ -239,11 +242,11 @@ def import_speed_flows(): # Remove generated files try: - os.remove(FLOWS_FILE) + os.remove(sets.flows_file) except Exception, e: print(e) try: - os.remove(CONFIG_FILE) + os.remove(sets.config_file) except Exception, e: print(e) return speed_array @@ -304,14 +307,21 @@ def upload_result(sets, pres, test_result_json, speed_array): settings = Settings() +# Request uuid from the control server request_uuid(settings) +# Request test settings from the control server request_settings(settings) +# Get the ping measurement result shortest_ping = measure_pings(settings) + +# Get the speed measurement result speed_result = measure_speed(settings) if speed_result == '': quit() -speed_flows = import_speed_flows() +# Get detailed test statistics +speed_flows = import_speed_flows(settings) +# Upload result to the control server upload_result(settings, shortest_ping, speed_result, speed_flows) -- GitLab From 20f801c18a39f9bab621cfe8ca4136a945fcc097 Mon Sep 17 00:00:00 2001 From: Martin Prudek Date: Tue, 7 Nov 2017 09:57:26 +0100 Subject: [PATCH 02/11] netmetr: Random wait Option that allows the script to wait random amount of time up to N seconds before the test starts added. --- netmetr/netmetr | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/netmetr/netmetr b/netmetr/netmetr index 31ca08c..a142580 100755 --- a/netmetr/netmetr +++ b/netmetr/netmetr @@ -9,6 +9,7 @@ import subprocess import shlex import os from random import randint +import argparse RMBT_BIN = "rmbt" @@ -306,6 +307,16 @@ def upload_result(sets, pres, test_result_json, speed_array): print(json.dumps(resp_json, indent=2)) +# Prepare argument parsing +parser = argparse.ArgumentParser(description='NetMetr - client application for\ + download and upload speed measurement.') +parser.add_argument('--rwait', nargs=1, type=int, default=[0], help='delay for\ + a random amount of time up to RWAIT seconds before the test strats') +args = parser.parse_args() + +# Wait appropriate amount of time +time.sleep(randint(0, args.rwait[0])) + settings = Settings() # Request uuid from the control server request_uuid(settings) -- GitLab From 026051887a75abecd1f0c41147b4031a3f3361b7 Mon Sep 17 00:00:00 2001 From: Martin Prudek Date: Tue, 7 Nov 2017 13:59:22 +0100 Subject: [PATCH 03/11] netmetr: Check whether to run or not on autostart --- netmetr/netmetr | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/netmetr/netmetr b/netmetr/netmetr index a142580..9869335 100755 --- a/netmetr/netmetr +++ b/netmetr/netmetr @@ -10,6 +10,7 @@ import shlex import os from random import randint import argparse +import datetime RMBT_BIN = "rmbt" @@ -312,8 +313,23 @@ parser = argparse.ArgumentParser(description='NetMetr - client application for\ download and upload speed measurement.') parser.add_argument('--rwait', nargs=1, type=int, default=[0], help='delay for\ a random amount of time up to RWAIT seconds before the test strats') +parser.add_argument('--autostart', action='store_true', help='use this\ + option oly when running as automated service - to check whether it is\ + right time to run the test') args = parser.parse_args() +# When autostarted - check whether it is right time to run the test +# In uci config, we expect hours of a day separated by commas (,) - these hours +# are the time the test should be run. So whenever the script is started, it +# looks to it's config and if it finds the current hour of day in it, +# it will start the test +if (args.autostart and os.path.isfile("/sbin/uci")): + hours = subprocess.check_output([ + "uci", "get", "netmetr.@settings[0].hours_to_run"])[:-1].split(',') + hours = map(int, hours) + if (datetime.datetime.now().hour not in hours): + exit() + # Wait appropriate amount of time time.sleep(randint(0, args.rwait[0])) -- GitLab From e9fcb00979861c0c2eaa6d52f730d682b0ac8455 Mon Sep 17 00:00:00 2001 From: Martin Prudek Date: Thu, 9 Nov 2017 13:17:07 +0100 Subject: [PATCH 04/11] netmetr: Download measurement history A new option was added that allows the user to download history of past speed measurements. These measurements would be stored in /tmp/ --- netmetr/netmetr | 60 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/netmetr/netmetr b/netmetr/netmetr index 9869335..91789ff 100755 --- a/netmetr/netmetr +++ b/netmetr/netmetr @@ -11,10 +11,12 @@ import os from random import randint import argparse import datetime +import re RMBT_BIN = "rmbt" DEBUG = False # Debug printouts +HIST_FILE_PRE = "netmetr-history" class Settings: @@ -308,6 +310,54 @@ def upload_result(sets, pres, test_result_json, speed_array): print(json.dumps(resp_json, indent=2)) +def purge(dir, pattern): + """Lists a directory and removes every file matching the pattern + """ + for f in os.listdir(dir): + if re.search(pattern, f): + try: + os.remove(os.path.join(dir, f)) + except OSError: + pass + + +def download_history(sets): + """Creates a http request and ask the control server for a measurement + history. + """ + # Create json to request history + req_json = { + "language": sets.language, + "timezone": sets.timezone, + "uuid": sets.uuid, + } + # Creating POST request to get history + req = urllib2.Request( + 'https://netmetr-control.labs.nic.cz/RMBTControlServer/history' + ) + req.add_header('Accept', 'application/json, text/javascript, */*; q=0.01') + req.add_header('Content-Type', 'application/json') + + if print_debug("Downloading measurement history from the control server."): + print(json.dumps(req_json, indent=2)) + # Send the request + resp = urllib2.urlopen(req, json.dumps(req_json)) + resp_json = json.loads(resp.read()) + # Remove all the previously saved measurements + purge("/tmp", HIST_FILE_PRE) + if print_debug("Measurement history response:"): + print(json.dumps(resp_json, indent=2)) + try: + with open( + "/tmp/" + HIST_FILE_PRE + "_" + sets.get_time() + ".json", + "w" + ) as hist_file: + hist_file.write(json.dumps(resp_json, indent=2)) + except Exception, e: + print("Error saving file") + print(e) + + # Prepare argument parsing parser = argparse.ArgumentParser(description='NetMetr - client application for\ download and upload speed measurement.') @@ -316,6 +366,12 @@ parser.add_argument('--rwait', nargs=1, type=int, default=[0], help='delay for\ parser.add_argument('--autostart', action='store_true', help='use this\ option oly when running as automated service - to check whether it is\ right time to run the test') +parser.add_argument( + '--dwlhist', + action='store_true', + help='download measurement history from control server and save it to \ + /tmp/' + HIST_FILE_PRE + '_.json' +) args = parser.parse_args() # When autostarted - check whether it is right time to run the test @@ -352,3 +408,7 @@ speed_flows = import_speed_flows(settings) # Upload result to the control server upload_result(settings, shortest_ping, speed_result, speed_flows) + +# Optionally download measurement history from the control server +if (args.dwlhist): + download_history(settings) -- GitLab From f2818a7338d8bc0b1e3bcad073a7b2fd25865701 Mon Sep 17 00:00:00 2001 From: Martin Prudek Date: Thu, 9 Nov 2017 16:31:49 +0100 Subject: [PATCH 05/11] netmetr: debug printouts turnable by cli switch --- netmetr/netmetr | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/netmetr/netmetr b/netmetr/netmetr index 91789ff..1f70677 100755 --- a/netmetr/netmetr +++ b/netmetr/netmetr @@ -15,7 +15,6 @@ import re RMBT_BIN = "rmbt" -DEBUG = False # Debug printouts HIST_FILE_PRE = "netmetr-history" @@ -372,8 +371,12 @@ parser.add_argument( help='download measurement history from control server and save it to \ /tmp/' + HIST_FILE_PRE + '_.json' ) +parser.add_argument('--debug', action='store_true', help='enables debug \ + printouts') args = parser.parse_args() +DEBUG = args.debug + # When autostarted - check whether it is right time to run the test # In uci config, we expect hours of a day separated by commas (,) - these hours # are the time the test should be run. So whenever the script is started, it -- GitLab From 74ba60dda59add4c58ba131170b828407798f029 Mon Sep 17 00:00:00 2001 From: Martin Prudek Date: Thu, 9 Nov 2017 17:05:12 +0100 Subject: [PATCH 06/11] netmetr: control server address moved to uci --- netmetr/netmetr | 35 +++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/netmetr/netmetr b/netmetr/netmetr index 1f70677..fbc8f6e 100755 --- a/netmetr/netmetr +++ b/netmetr/netmetr @@ -16,6 +16,7 @@ import re RMBT_BIN = "rmbt" HIST_FILE_PRE = "netmetr-history" +FALLBACK_CTRL_SRV = "netmetr-control.labs.nic.cz" class Settings: @@ -24,6 +25,32 @@ class Settings: self.config_file = "/tmp/rmbtcfg-"+str(randint(0, 1000000000))+".cfg" self.language = locale.getdefaultlocale()[0] self.timezone = subprocess.check_output(["date", "+%Z"])[:-1] + if os.path.isfile("/sbin/uci"): + process = subprocess.Popen( + ["uci", "get", "netmetr.@settings[0].control_server"], + stdout=subprocess.PIPE + ) + if process.wait() == 0: + self.control_server = process.stdout.read()[:-1] + else: + print( + 'control server not found, falling to: ' + + FALLBACK_CTRL_SRV + '.' + ) + self.control_server = FALLBACK_CTRL_SRV + subprocess.call([ + "uci", + "set", + "netmetr.@settings[0].control_server=" + + self.control_server + ]) + subprocess.call(["uci", "commit"]) + else: + print( + 'control server not found, falling to: ' + FALLBACK_CTRL_SRV + + '.' + ) + self.control_server = FALLBACK_CTRL_SRV if os.path.isfile("/etc/turris-version"): with open("/etc/turris-version", 'r') as turris_version: self.os_version = turris_version.read().split('\n')[0] @@ -78,7 +105,7 @@ def request_uuid(sets): # Creating GET request to obtain / check uuid req = urllib2.Request( - 'https://netmetr-control.labs.nic.cz/RMBTControlServer/settings' + 'https://' + sets.control_server + '/RMBTControlServer/settings' ) req.add_header('Accept', 'application/json, text/javascript, */*; q=0.01') req.add_header('Content-Type', 'application/json') @@ -108,7 +135,7 @@ def request_settings(sets): """ # Create request to start a test req = urllib2.Request( - 'https://netmetr-control.labs.nic.cz/RMBTControlServer/testRequest' + 'https://' + sets.control_server + '/RMBTControlServer/testRequest' ) # Add headers req.add_header('Accept', 'application/json, text/javascript, */*; q=0.01') @@ -297,7 +324,7 @@ def upload_result(sets, pres, test_result_json, speed_array): # Create GET request req = urllib2.Request( - 'https://netmetr-control.labs.nic.cz/RMBTControlServer/result' + 'https://' + sets.control_server + '/RMBTControlServer/result' ) # Add headers req.add_header('Accept', 'application/json, text/javascript, */*; q=0.01') @@ -332,7 +359,7 @@ def download_history(sets): } # Creating POST request to get history req = urllib2.Request( - 'https://netmetr-control.labs.nic.cz/RMBTControlServer/history' + 'https://' + sets.control_server + '/RMBTControlServer/history' ) req.add_header('Accept', 'application/json, text/javascript, */*; q=0.01') req.add_header('Content-Type', 'application/json') -- GitLab From 50a0e451ff49267824ac557d1b4ff01f65e7bc37 Mon Sep 17 00:00:00 2001 From: Martin Prudek Date: Fri, 10 Nov 2017 11:11:25 +0100 Subject: [PATCH 07/11] netmetr: Sync code, quiet uci, colored info Synchronization code is now printed at the end of each test. The code is either loaded from uci config file or - if missing - downloaded from the control server. When uuid is chanded the sync code is automatically flushed and prepared to be regenerated. The process is divided into two functions: load_sync_code: checks the uci for the code download_sync_code: download the code from the control server Some minor changes were done: -uci set commands produce no explicit error messages from now on -new colored info printouts were added - these inform the user that some config is missing and is dowloaded or generated --- netmetr/netmetr | 93 +++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 83 insertions(+), 10 deletions(-) diff --git a/netmetr/netmetr b/netmetr/netmetr index fbc8f6e..5c49f0a 100755 --- a/netmetr/netmetr +++ b/netmetr/netmetr @@ -27,14 +27,14 @@ class Settings: self.timezone = subprocess.check_output(["date", "+%Z"])[:-1] if os.path.isfile("/sbin/uci"): process = subprocess.Popen( - ["uci", "get", "netmetr.@settings[0].control_server"], + ["uci", "-q", "get", "netmetr.@settings[0].control_server"], stdout=subprocess.PIPE ) if process.wait() == 0: self.control_server = process.stdout.read()[:-1] else: - print( - 'control server not found, falling to: ' + + print_info( + 'Control server not found, falling to: ' + FALLBACK_CTRL_SRV + '.' ) self.control_server = FALLBACK_CTRL_SRV @@ -46,9 +46,9 @@ class Settings: ]) subprocess.call(["uci", "commit"]) else: - print( - 'control server not found, falling to: ' + FALLBACK_CTRL_SRV - + '.' + print_info( + 'Control server not found (uci missing), falling to: ' + + FALLBACK_CTRL_SRV + '.' ) self.control_server = FALLBACK_CTRL_SRV if os.path.isfile("/etc/turris-version"): @@ -73,6 +73,8 @@ def print_debug(msg): print('\033[93m' + msg + '\033[0m') return DEBUG +def print_info(msg): + print('\033[91m' + msg + '\033[0m') def request_uuid(sets): """Creates a http request and ask the control server for correct uuid""" @@ -91,16 +93,16 @@ def request_uuid(sets): # Load uuid saved in config file via uci if os.path.isfile("/sbin/uci"): process = subprocess.Popen( - ["uci", "get", "netmetr.@settings[0].uuid"], + ["uci", "-q", "get", "netmetr.@settings[0].uuid"], stdout=subprocess.PIPE ) if process.wait() == 0: req_json['uuid'] = process.stdout.read()[:-1] else: - print('uuid not found, requesting new one.') + print_info('Uuid not found, requesting new one.') req_json['uuid'] = 0 else: - print('uuid not found, requesting new one.') + print_info('Uuid not found (uci missing), requesting new one.') req_json['uuid'] = 0 # Creating GET request to obtain / check uuid @@ -118,10 +120,14 @@ def request_uuid(sets): if uuid_new: # New uuid was received sets.uuid = uuid_new if os.path.isfile("/sbin/uci"): - process = subprocess.Popen([ + subprocess.call([ "uci", "set", "netmetr.@settings[0].uuid="+sets.uuid ]) + subprocess.call([ + "uci", "-q", "delete", + "netmetr.@settings[0].sync_code" + ]) subprocess.call(["uci", "commit"]) else: sets.uuid = req_json['uuid'] @@ -384,6 +390,69 @@ def download_history(sets): print(e) +def download_sync_code(sets): + """Creates a http request and ask the control server for a synchronization + code that can be used to view saved measurements from different devices. + The new code is saved via uci. + """ + # Create json to request synchronization code + req_json = { + "language": sets.language, + "timezone": sets.timezone, + "uuid": sets.uuid, + } + # Creating POST request to get the sync code + req = urllib2.Request( + 'https://' + sets.control_server + '/RMBTControlServer/sync' + ) + req.add_header('Accept', 'application/json, text/javascript, */*; q=0.01') + req.add_header('Content-Type', 'application/json') + + if print_debug( + "Downloading synchronization code from the control server." + ): + print(json.dumps(req_json, indent=2)) + # Send the request + resp = urllib2.urlopen(req, json.dumps(req_json)) + resp_json = json.loads(resp.read()) + if print_debug("Synchronization token response:"): + print(json.dumps(resp_json, indent=2)) + + if not resp_json["error"]: + sets.sync_code = resp_json["sync"][0].get("sync_code", '') + # If we have uci and VALID sync code: + if (os.path.isfile("/sbin/uci") and sets.sync_code): + subprocess.call([ + "uci", "set", + "netmetr.@settings[0].sync_code="+sets.sync_code + ]) + subprocess.call(["uci", "commit"]) + else: + sets.sync_code = '' + print("Error downloading synchronization code.") + + +def load_sync_code(sets): + """Checks the uci config for sychronization code and loads it to the\ + script. If no synchronization code is found a https request is send to\ + download it. + """ + # Load synchronization code saved in config file via uci + if os.path.isfile("/sbin/uci"): + process = subprocess.Popen( + ["uci", "-q", "get", "netmetr.@settings[0].sync_code"], + stdout=subprocess.PIPE + ) + if process.wait() == 0: + sets.sync_code = process.stdout.read()[:-1] + else: + print_info('Sync code not found, requesting new one.') + download_sync_code(sets) + else: + print_info('Sync code not found (uci missing), requesting new one.') + download_sync_code(sets) + + # Prepare argument parsing parser = argparse.ArgumentParser(description='NetMetr - client application for\ download and upload speed measurement.') @@ -442,3 +511,7 @@ upload_result(settings, shortest_ping, speed_result, speed_flows) # Optionally download measurement history from the control server if (args.dwlhist): download_history(settings) + +load_sync_code(settings) +if (settings.sync_code): + print_info("Your Sync code is: " + settings.sync_code) -- GitLab From 52eeec2808a347110166d848a2dc81d493c85906 Mon Sep 17 00:00:00 2001 From: Martin Prudek Date: Fri, 10 Nov 2017 12:53:19 +0100 Subject: [PATCH 08/11] netmetr: Autostart - added main option enabling it --- netmetr/netmetr | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/netmetr/netmetr b/netmetr/netmetr index 5c49f0a..5b55b53 100755 --- a/netmetr/netmetr +++ b/netmetr/netmetr @@ -473,16 +473,33 @@ args = parser.parse_args() DEBUG = args.debug -# When autostarted - check whether it is right time to run the test +# When autostarted - check whether autostart is enabled and +# if it is right time to run the test. # In uci config, we expect hours of a day separated by commas (,) - these hours # are the time the test should be run. So whenever the script is started, it # looks to it's config and if it finds the current hour of day in it, # it will start the test if (args.autostart and os.path.isfile("/sbin/uci")): - hours = subprocess.check_output([ - "uci", "get", "netmetr.@settings[0].hours_to_run"])[:-1].split(',') - hours = map(int, hours) - if (datetime.datetime.now().hour not in hours): + process = subprocess.Popen( + ["uci", "-q", "get", "netmetr.@settings[0].autostart_enabled"], + stdout=subprocess.PIPE + ) + if (process.wait() == 0): + autostart_enabled = process.stdout.read()[:-1] + else: + print("Failed to load autostart uci settings.") + exit() + process = subprocess.Popen( + ["uci", "-q", "get", "netmetr.@settings[0].hours_to_run"], + stdout=subprocess.PIPE + ) + if (process.wait() == 0): + hours = process.stdout.read()[:-1].split(',') + hours = map(int, hours) + else: + print("Failed to load autostart time uci settings.") + exit() + if (autostart_enabled != '1' or datetime.datetime.now().hour not in hours): exit() # Wait appropriate amount of time -- GitLab From 7c8d14f2b3a5b1c6ef11d6a2d2abbda30eea6f4a Mon Sep 17 00:00:00 2001 From: Martin Prudek Date: Mon, 13 Nov 2017 15:54:27 +0100 Subject: [PATCH 09/11] netmetr: Minor fixes (typos, naming..) - Random file creation moved to tempfile.mkstemp() - Some printouts typos correctios - Option to disable colored output added --- netmetr/netmetr | 42 +++++++++++++++++++++++++++++------------- 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/netmetr/netmetr b/netmetr/netmetr index 5b55b53..bcf7b56 100755 --- a/netmetr/netmetr +++ b/netmetr/netmetr @@ -12,6 +12,7 @@ from random import randint import argparse import datetime import re +import tempfile RMBT_BIN = "rmbt" @@ -21,8 +22,8 @@ FALLBACK_CTRL_SRV = "netmetr-control.labs.nic.cz" class Settings: def __init__(self): - self.flows_file = "/tmp/flows-"+str(randint(0, 1000000000))+".json" - self.config_file = "/tmp/rmbtcfg-"+str(randint(0, 1000000000))+".cfg" + _, self.flows_file = tempfile.mkstemp() + _, self.config_file = tempfile.mkstemp() self.language = locale.getdefaultlocale()[0] self.timezone = subprocess.check_output(["date", "+%Z"])[:-1] if os.path.isfile("/sbin/uci"): @@ -35,7 +36,7 @@ class Settings: else: print_info( 'Control server not found, falling to: ' + - FALLBACK_CTRL_SRV + '.' + FALLBACK_CTRL_SRV ) self.control_server = FALLBACK_CTRL_SRV subprocess.call([ @@ -48,7 +49,7 @@ class Settings: else: print_info( 'Control server not found (uci missing), falling to: ' - + FALLBACK_CTRL_SRV + '.' + + FALLBACK_CTRL_SRV ) self.control_server = FALLBACK_CTRL_SRV if os.path.isfile("/etc/turris-version"): @@ -70,15 +71,27 @@ class Settings: def print_debug(msg): if DEBUG: - print('\033[93m' + msg + '\033[0m') + if COLORED_OUTPUT: + print('\033[93m' + msg + '\033[0m') + else: + print(msg) return DEBUG def print_info(msg): - print('\033[91m' + msg + '\033[0m') + if COLORED_OUTPUT: + print('\033[91m' + msg + '\033[0m') + else: + print(msg) + +def print_progress(msg): + if COLORED_OUTPUT: + print('\033[93m' + msg + '\033[0m') + else: + print(msg) def request_uuid(sets): """Creates a http request and ask the control server for correct uuid""" - print('\033[93m'+"Requesting test config from control server..."+'\033[0m') + print_progress("Requesting test config from control server...") # Create json to request uuid req_json = { "language": sets.language, @@ -183,7 +196,7 @@ def measure_pings(sets): the lowest one """ - print('\033[93m'+"Starting ping test..."+'\033[0m') + print_progress("Starting ping test...") ping_result_lines = subprocess.check_output([ "ping", "-c", sets.test_numpings, sets.test_server_address @@ -221,7 +234,7 @@ def measure_speed(sets): return '' encryption = {True: "-e"} - print('\033[93m'+"Starting speed test..."+'\033[0m') + print_progress("Starting speed test...") test_result = subprocess.check_output([ RMBT_BIN, encryption.get(sets.test_server_encryption, ""), "-h", sets.test_server_address, "-p", str(sets.test_server_port), "-t", @@ -386,7 +399,7 @@ def download_history(sets): ) as hist_file: hist_file.write(json.dumps(resp_json, indent=2)) except Exception, e: - print("Error saving file") + print("Error saving measurement history.") print(e) @@ -457,21 +470,24 @@ def load_sync_code(sets): parser = argparse.ArgumentParser(description='NetMetr - client application for\ download and upload speed measurement.') parser.add_argument('--rwait', nargs=1, type=int, default=[0], help='delay for\ - a random amount of time up to RWAIT seconds before the test strats') + a random amount of time up to RWAIT seconds before the test starts') parser.add_argument('--autostart', action='store_true', help='use this\ - option oly when running as automated service - to check whether it is\ + option only when running as an automated service - to check whether it is\ right time to run the test') parser.add_argument( '--dwlhist', action='store_true', - help='download measurement history from control server and save it to \ + help='download measurement history from the control server and save it to \ /tmp/' + HIST_FILE_PRE + '_.json' ) parser.add_argument('--debug', action='store_true', help='enables debug \ printouts') +parser.add_argument('--no-color', action='store_true', help='disables colored \ + text output') args = parser.parse_args() DEBUG = args.debug +COLORED_OUTPUT = not args.no_color # When autostarted - check whether autostart is enabled and # if it is right time to run the test. -- GitLab From aa17d6fce070c2fbaced2a8729103e48098456e3 Mon Sep 17 00:00:00 2001 From: Martin Prudek Date: Tue, 14 Nov 2017 08:16:28 +0100 Subject: [PATCH 10/11] netmetr: ---no-run option added. This optio prevents from running the test. It coul be used for obtaining the sync code or downloading measurement history. --- netmetr/netmetr | 90 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 59 insertions(+), 31 deletions(-) diff --git a/netmetr/netmetr b/netmetr/netmetr index bcf7b56..27094ba 100755 --- a/netmetr/netmetr +++ b/netmetr/netmetr @@ -22,8 +22,9 @@ FALLBACK_CTRL_SRV = "netmetr-control.labs.nic.cz" class Settings: def __init__(self): - _, self.flows_file = tempfile.mkstemp() - _, self.config_file = tempfile.mkstemp() + if (not args.no_run): + _, self.flows_file = tempfile.mkstemp() + _, self.config_file = tempfile.mkstemp() self.language = locale.getdefaultlocale()[0] self.timezone = subprocess.check_output(["date", "+%Z"])[:-1] if os.path.isfile("/sbin/uci"): @@ -77,32 +78,33 @@ def print_debug(msg): print(msg) return DEBUG + def print_info(msg): if COLORED_OUTPUT: print('\033[91m' + msg + '\033[0m') else: print(msg) + def print_progress(msg): if COLORED_OUTPUT: print('\033[93m' + msg + '\033[0m') else: print(msg) -def request_uuid(sets): - """Creates a http request and ask the control server for correct uuid""" - print_progress("Requesting test config from control server...") - # Create json to request uuid - req_json = { - "language": sets.language, - "timezone": sets.timezone, - "name": "RMBT", - "terms_and_conditions_accepted": "true", - "type": "DESKTOP", - "version_code": "1", - "version_name": "1.0", - } +def print_error(msq, error_code): + if COLORED_OUTPUT: + print('\033[41m' + msg + '\033[0m') + else: + print(msg) + exit(error_code) + +def load_uuid(sets): + """Checks the uci config for uuid and loads it to the + script. If no uuid is found a https request is send to the control server + to download it. + """ # Load uuid saved in config file via uci if os.path.isfile("/sbin/uci"): process = subprocess.Popen( @@ -110,13 +112,33 @@ def request_uuid(sets): stdout=subprocess.PIPE ) if process.wait() == 0: - req_json['uuid'] = process.stdout.read()[:-1] + sets.uuid = process.stdout.read()[:-1] else: print_info('Uuid not found, requesting new one.') - req_json['uuid'] = 0 + sets.uuid = 0; else: print_info('Uuid not found (uci missing), requesting new one.') - req_json['uuid'] = 0 + sets.uuid = 0; + + # the download request must be sent all the time - either to raquest new + # uuid or to check the existing one + download_uuid(sets) + + +def download_uuid(sets): + """Creates a http request and ask the control server for correct uuid""" + print_progress("Checking uuid on the control server...") + # Create json to request uuid + req_json = { + "uuid": sets.uuid, + "language": sets.language, + "timezone": sets.timezone, + "name": "RMBT", + "terms_and_conditions_accepted": "true", + "type": "DESKTOP", + "version_code": "1", + "version_name": "1.0", + } # Creating GET request to obtain / check uuid req = urllib2.Request( @@ -152,6 +174,7 @@ def request_settings(sets): """Creates a http request to get test token, number of threads, number of pings, server address and port and so on. """ + print_progress("Requesting test config from the control server...") # Create request to start a test req = urllib2.Request( 'https://' + sets.control_server + '/RMBTControlServer/testRequest' @@ -484,6 +507,9 @@ parser.add_argument('--debug', action='store_true', help='enables debug \ printouts') parser.add_argument('--no-color', action='store_true', help='disables colored \ text output') +parser.add_argument('--no-run', action='store_true', help='this option\ + prevents from running the test. It could be used only to obtain sync code\ + or (with --dwlhist) to download measurement history') args = parser.parse_args() DEBUG = args.debug @@ -523,23 +549,25 @@ time.sleep(randint(0, args.rwait[0])) settings = Settings() # Request uuid from the control server -request_uuid(settings) -# Request test settings from the control server -request_settings(settings) +load_uuid(settings) + +if (not args.no_run): + # Request test settings from the control server + request_settings(settings) -# Get the ping measurement result -shortest_ping = measure_pings(settings) + # Get the ping measurement result + shortest_ping = measure_pings(settings) -# Get the speed measurement result -speed_result = measure_speed(settings) -if speed_result == '': - quit() + # Get the speed measurement result + speed_result = measure_speed(settings) + if speed_result == '': + quit() -# Get detailed test statistics -speed_flows = import_speed_flows(settings) + # Get detailed test statistics + speed_flows = import_speed_flows(settings) -# Upload result to the control server -upload_result(settings, shortest_ping, speed_result, speed_flows) + # Upload result to the control server + upload_result(settings, shortest_ping, speed_result, speed_flows) # Optionally download measurement history from the control server if (args.dwlhist): -- GitLab From 9a0213052e356f8e877e14f625228e21bda95dfd Mon Sep 17 00:00:00 2001 From: Martin Prudek Date: Tue, 14 Nov 2017 15:07:34 +0100 Subject: [PATCH 11/11] netmetr: Minor fixes - ping output added to console - file with saved history renamed - fallback control server changed - uci config formatting of times of automated runs changed --- netmetr/netmetr | 100 ++++++++++++++++++++++-------------------------- 1 file changed, 46 insertions(+), 54 deletions(-) diff --git a/netmetr/netmetr b/netmetr/netmetr index 27094ba..5495af3 100755 --- a/netmetr/netmetr +++ b/netmetr/netmetr @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python # coding: utf-8 import urllib2 import json @@ -16,20 +16,18 @@ import tempfile RMBT_BIN = "rmbt" -HIST_FILE_PRE = "netmetr-history" -FALLBACK_CTRL_SRV = "netmetr-control.labs.nic.cz" +HIST_FILE = "/tmp/netmetr-history.json" +# FALLBACK_CTRL_SRV = "netmetr-control.labs.nic.cz" +FALLBACK_CTRL_SRV = "control.netmetr.cz" class Settings: def __init__(self): - if (not args.no_run): - _, self.flows_file = tempfile.mkstemp() - _, self.config_file = tempfile.mkstemp() self.language = locale.getdefaultlocale()[0] self.timezone = subprocess.check_output(["date", "+%Z"])[:-1] if os.path.isfile("/sbin/uci"): process = subprocess.Popen( - ["uci", "-q", "get", "netmetr.@settings[0].control_server"], + ["uci", "-q", "get", "netmetr.settings.control_server"], stdout=subprocess.PIPE ) if process.wait() == 0: @@ -43,7 +41,7 @@ class Settings: subprocess.call([ "uci", "set", - "netmetr.@settings[0].control_server=" + "netmetr.settings.control_server=" + self.control_server ]) subprocess.call(["uci", "commit"]) @@ -92,7 +90,8 @@ def print_progress(msg): else: print(msg) -def print_error(msq, error_code): + +def print_error(msg, error_code): if COLORED_OUTPUT: print('\033[41m' + msg + '\033[0m') else: @@ -108,17 +107,17 @@ def load_uuid(sets): # Load uuid saved in config file via uci if os.path.isfile("/sbin/uci"): process = subprocess.Popen( - ["uci", "-q", "get", "netmetr.@settings[0].uuid"], + ["uci", "-q", "get", "netmetr.settings.uuid"], stdout=subprocess.PIPE ) if process.wait() == 0: sets.uuid = process.stdout.read()[:-1] else: print_info('Uuid not found, requesting new one.') - sets.uuid = 0; + sets.uuid = 0 else: print_info('Uuid not found (uci missing), requesting new one.') - sets.uuid = 0; + sets.uuid = 0 # the download request must be sent all the time - either to raquest new # uuid or to check the existing one @@ -157,11 +156,11 @@ def download_uuid(sets): if os.path.isfile("/sbin/uci"): subprocess.call([ "uci", "set", - "netmetr.@settings[0].uuid="+sets.uuid + "netmetr.settings.uuid="+sets.uuid ]) subprocess.call([ "uci", "-q", "delete", - "netmetr.@settings[0].sync_code" + "netmetr.settings.sync_code" ]) subprocess.call(["uci", "commit"]) else: @@ -220,34 +219,37 @@ def measure_pings(sets): """ print_progress("Starting ping test...") - ping_result_lines = subprocess.check_output([ - "ping", "-c", sets.test_numpings, - sets.test_server_address - ]).split('\n') ping_values = list() for i in range(1, int(sets.test_numpings)+1): - try: - start = ping_result_lines[i].index("time=") + len("time=") - end = ping_result_lines[i].index(" ms") - ping = int(float(ping_result_lines[i][start:end])*1000000) - ping_values.append(ping) - except: - print("Problem decoding pings.") - return '' - return min(int(s) for s in ping_values) + process = subprocess.Popen([ + "ping", "-c1", + sets.test_server_address + ], stdout=subprocess.PIPE) + if (process.wait() == 0): + try: + ping_result = process.stdout.read() + start = ping_result.index("time=") + len("time=") + end = ping_result.index(" ms") + ping = float(ping_result[start:end]) + print("ping_"+str(i)+"_msec = "+format(ping, '.2f')) + ping = int(ping * 1000000) + ping_values.append(ping) + except: + print("Problem decoding pings.") + return '' + time.sleep(0.5) + try: + return min(int(s) for s in ping_values) + except: + return '' def measure_speed(sets): """Start RMBT client with saved arguments to measure the speed """ # Create config file needed by rmbt-client - if os.path.isfile(sets.config_file): - try: - os.remove(sets.config_file) - except Exception, e: - print(e) - return '' - + _, sets.config_file = tempfile.mkstemp() + _, sets.flows_file = tempfile.mkstemp() try: with open(sets.config_file, "w") as config_file: config_file.write('{"cnf_file_flows": "'+sets.flows_file+'.xz"}') @@ -273,19 +275,12 @@ def import_speed_flows(sets): """The speedtest flow is saved to a file during the test. This function imports it so it could be sent to the control server. """ - if os.path.isfile(sets.flows_file): - try: - os.remove(sets.flows_file) - except Exception, e: - print(e) - return - directions = { "dl": "download", "ul": "upload" } try: - subprocess.call(shlex.split("unxz "+sets.flows_file+".xz")) + subprocess.call(shlex.split("unxz -f "+sets.flows_file+".xz")) with open(sets.flows_file, 'r') as json_data: flows_json = json.load(json_data) except Exception, e: @@ -411,16 +406,13 @@ def download_history(sets): # Send the request resp = urllib2.urlopen(req, json.dumps(req_json)) resp_json = json.loads(resp.read()) - # Remove all the previously saved measurements - purge("/tmp", HIST_FILE_PRE) if print_debug("Measurement history response:"): print(json.dumps(resp_json, indent=2)) + _, sets.hist_file = tempfile.mkstemp() try: - with open( - "/tmp/" + HIST_FILE_PRE + "_" + sets.get_time() + ".json", - "w" - ) as hist_file: + with open(sets.hist_file, "w") as hist_file: hist_file.write(json.dumps(resp_json, indent=2)) + os.rename(sets.hist_file, HIST_FILE) except Exception, e: print("Error saving measurement history.") print(e) @@ -460,7 +452,7 @@ def download_sync_code(sets): if (os.path.isfile("/sbin/uci") and sets.sync_code): subprocess.call([ "uci", "set", - "netmetr.@settings[0].sync_code="+sets.sync_code + "netmetr.settings.sync_code="+sets.sync_code ]) subprocess.call(["uci", "commit"]) else: @@ -476,7 +468,7 @@ def load_sync_code(sets): # Load synchronization code saved in config file via uci if os.path.isfile("/sbin/uci"): process = subprocess.Popen( - ["uci", "-q", "get", "netmetr.@settings[0].sync_code"], + ["uci", "-q", "get", "netmetr.settings.sync_code"], stdout=subprocess.PIPE ) if process.wait() == 0: @@ -501,7 +493,7 @@ parser.add_argument( '--dwlhist', action='store_true', help='download measurement history from the control server and save it to \ - /tmp/' + HIST_FILE_PRE + '_.json' + /tmp/' + HIST_FILE ) parser.add_argument('--debug', action='store_true', help='enables debug \ printouts') @@ -523,7 +515,7 @@ COLORED_OUTPUT = not args.no_color # it will start the test if (args.autostart and os.path.isfile("/sbin/uci")): process = subprocess.Popen( - ["uci", "-q", "get", "netmetr.@settings[0].autostart_enabled"], + ["uci", "-q", "get", "netmetr.settings.autostart_enabled"], stdout=subprocess.PIPE ) if (process.wait() == 0): @@ -532,11 +524,11 @@ if (args.autostart and os.path.isfile("/sbin/uci")): print("Failed to load autostart uci settings.") exit() process = subprocess.Popen( - ["uci", "-q", "get", "netmetr.@settings[0].hours_to_run"], + ["uci", "-q", "get", "netmetr.settings.hours_to_run"], stdout=subprocess.PIPE ) if (process.wait() == 0): - hours = process.stdout.read()[:-1].split(',') + hours = process.stdout.read()[:-1].split(' ') hours = map(int, hours) else: print("Failed to load autostart time uci settings.") -- GitLab