Pomiar rzeczywistej przepustowości łącza internetowego – narzędzia skryptowe

speed_testPomiar rzeczywistej przepustowości łączy do Internetu przydaje się nie tylko wtedy, gdy chcemy wygrać spór z nieuczciwym dostawcą usług dostępowych. Może przydać się również – a może przede wszystkim – w bardziej pokojowych okolicznościach, na przykład do szeroko pojętej diagnostyki problemów w sieci. Możemy przykładowo badać „wąskie gardła” sieci, identyfikując problemy z przepustowością i czasem odpowiedzi.

Narzędzie, które opracowałem bazuje na oprogramowaniu speedtest, które trzeba zainstalować na komputerze z interpreterem języka Python. Można to uczynić poleceniem:

pip install speedtest

Należy również doinstalować w analogiczny sposób wszystkie niestandardowe moduły przywoływane w kodzie skryptu dyrektywami import, a wśród nich na pewno ‚optparse‚ (do kontroli składni i obsługi parametrów wywołania skryptu) oraz ‚xlwt‚ (do tworzenia i zapisywania danych do arkuszy kalkulacyjnych Excela, nawet jeśli Excelem nie dysponujemy) oraz ‚sched‚ (implementujący funkcje schedulera).

Sposób uruchomienia i działanie widać na poniższym przykładzie:

$ python speed_testing.py -h
Usage:
      _____       _                       _
     |_   _|     | |                     | |
       | |  _ __ | |_ ___ _ __ _ __   ___| |_
       | | | '_ \| __/ _ \ '__| '_ \ / _ \ __|
      _| |_| | | | ||  __/ |  | | | |  __/ |_
     |_____|_| |_|\__\___|_|  |_|_|_|\___|\__|    _   _
      / ____|                   | | | |          | | (_)
     | (___  _ __   ___  ___  __| | | |_ ___  ___| |_ _ _ __   __ _
      \___ \| '_ \ / _ \/ _ \/ _` | | __/ _ \/ __| __| | '_ \ / _` |
      ____) | |_) |  __/  __/ (_| | | ||  __/\__ \ |_| | | | | (_| |
     |_____/| .__/ \___|\___|\__,_|  \__\___||___/\__|_|_| |_|\__, |
            | |                                                __/ |
            |_| =========== Janusz Nawrat - 2015 ============ |___/


Options:
  -h, --help            show this help message and exit
  -s NO_SAMPLE, --samples=NO_SAMPLE
                        Number of samples
  -t TIME_SAMLPLE, --time_of_sampling=TIME_SAMLPLE
                        Time of sampling in seconds

$ python speed_testing.py -s 20 -t 60

      _____       _                       _
     |_   _|     | |                     | |
       | |  _ __ | |_ ___ _ __ _ __   ___| |_
       | | | '_ \| __/ _ \ '__| '_ \ / _ \ __|
      _| |_| | | | ||  __/ |  | | | |  __/ |_
     |_____|_| |_|\__\___|_|  |_|_|_|\___|\__|    _   _
      / ____|                   | | | |          | | (_)
     | (___  _ __   ___  ___  __| | | |_ ___  ___| |_ _ _ __   __ _
      \___ \| '_ \ / _ \/ _ \/ _` | | __/ _ \/ __| __| | '_ \ / _` |
      ____) | |_) |  __/  __/ (_| | | ||  __/\__ \ |_| | | | | (_| |
     |_____/| .__/ \___|\___|\__,_|  \__\___||___/\__|_|_| |_|\__, |
            | |                                                __/ |
            |_| =========== Janusz Nawrat - 2015 ============ |___/

[*] START: 2015-11-07 15:16:44.333000
[*] Testing in progress...
[+] Pomiar: 00, czas: 2015-11-07 15:17:42.876000, Download: 1.76 Mbit/s
[+] Pomiar: 01, czas: 2015-11-07 15:20:39.877000, Download: 2.01 Mbit/s
[+] Pomiar: 02, czas: 2015-11-07 15:23:37.403000, Download: 2.02 Mbit/s
[+] Pomiar: 03, czas: 2015-11-07 15:26:20.620000, Download: 1.87 Mbit/s
[+] Pomiar: 04, czas: 2015-11-07 15:29:13.463000, Download: 1.79 Mbit/s
[+] Pomiar: 05, czas: 2015-11-07 15:32:18.168000, Download: 1.98 Mbit/s
...
[>] Do you want to extract data do Excel spreadsheet (Y/N)? y
[*] Data extraction started
 1 ; 2015-11-07 15:17:42 ; 1.76 ; 68.145
 2 ; 2015-11-07 15:20:39 ; 2.01 ; 44.712
 3 ; 2015-11-07 15:23:37 ; 2.02 ; 44.823
 4 ; 2015-11-07 15:26:20 ; 1.87 ; 836.417
 5 ; 2015-11-07 15:29:13 ; 1.79 ; 2240.205
...
[*] Results saved also to speedtest_results.xls spreadsheet file

Skrypt uruchamia w określonym parametrem -t cyklu (czas trwania cyklu) procedury pomiaru pasma łącza. Parametrem -s z kolei określona jest liczba cykli składających się na pomiar. Przy pomocy wyrażeń regularnych skrypt w następnym kroku wydobywa z danych wyjściowych tych procedur informacje na temat przepustowości i opóźnienia w czasie odpowiedzi sieci. Te dane zapisuje potem do pliku tekstowego, którego nazwę określa globalna zmienna programu REPORT_FILE. Na koniec pomiaru, w odpowiedzi na monit programu, użytkownik może wyrazić swoja wolę, by zgromadzone dane zapisać także do arkusza kalkulacyjnego w formacie MS Excel.

Skrypt w aktualnej postaci pozyskuje dane na temat przepustowości łącza wyłącznie dla strumienia przychodzącego (downstream transfer), ale nic nie stoi na przeszkodzie, by zmodyfikować go lekko, aby pobierał dane o przepustowości łącza także przy wysyłaniu danych (upstream transfer) do sieci. Te dwie wielkości na pewno będą się istotnie różnić w przypadku łączy z transferem asymetrycznym (na przykład ADSL).

Wspomniana modyfikacja polegałaby na „łapaniu” wielkości wskazującej na ‚upload speed’:

if 'Upload' in line:
    pom += 1
    result = re.sub(r'(^.+?)(\d+.\d+)(.*)', '\g', line)
    print "%2d ; %s ; %s ; %s" % (pom, data, result, opoznienie)
    sheet1.write(pom-1, 0, pom); sheet1.write(pom-1, 1, data); sheet1.write(pom-1, 2, float(result))
    sheet1.write(pom-1, 3, float(opoznienie))

Wspomniany eksport danych do arkusza kalkulacyjnego może służyć różnym celom, na przykład wizualizacji danych, i choć Python posiada dużo własnych tego rodzaju specjalistycznych bibliotek, to Excel wydaje się czasem być najprostszym z istniejących rozwiązań.

Pa przykład można pokusić się o wykreślenie wyników pomiaru w postaci następującego diagramu.

wykres
Kod źródłowy skryptu jest tu:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sched, re
import time, sys, datetime
import subprocess
from optparse import OptionParser
from xlwt import Workbook

REPORT_FILE = 'speed_report.txt'
WORKSHEET = 'speedtest_results.xls'

def pomiar(id):
    try: 
        proc = subprocess.Popen(['speedtest'], stdout=subprocess.PIPE)
    except:
        print >> sys.stderr, "[!] No speedtest module installed: run 'pip install speedtest'"
        sys.exit(1)
    output = proc.stdout.read()
    speed = re.search(r'(Download:.+)', output)
    try: 
        f = open(REPORT_FILE,'a')
    except:
        print >> sys.stderr, "[!] Problem with opening %s file" % REPORT_FILE
        sys.exit(1)
    
    spd = ""
    if speed:
        spd = speed.group()
    
    print "[+] Pomiar: %02d, czas: %s, %s" % (int(id), datetime.datetime.now(), spd)
    czas = datetime.datetime.now()
    f.write("%s\n" % czas)
    f.write(output)
    f.close()
# end of pomiar()
    
def extract_data_to_excel():
    try:
        book = Workbook()
        sheet1 = book.add_sheet('Pomiary')
    except:
        print >> sys.stderr, "[!] Problem with spreadsheet workbook creation"
        sys.exit(1)

    try:
        with open(REPORT_FILE) as f:
            content = f.readlines()
    except:
        print >> sys.stderr, "[!] Problem with %s file opening" % REPORT_FILE
        sys.exit(1)
        
    print >> sys.stderr, "[*] Data extraction started"
    pom = 0    
    for line in content:
        line = re.sub('\n', '', line)
        date_and_time = re.search(r'(^\d{4}-\d{2}-\d{2})\s+(\d{2}:\d{2}:\d{2})', line)
        
        if date_and_time:
            data = date_and_time.group()
        latency = re.search(r'^Hosted by.+?\[(\d+\.\d+\s+km)\]:.+?(\d+\.\d*).+', line)
        
        if latency:
            # print "[+] Latency: %s" % latency.group(2)
            opoznienie = latency.group(2)
        
        if 'Download' in line:
            pom += 1
            result = re.sub(r'(^.+?)(\d+.\d+)(.*)', '\g', line)
            print "%2d ; %s ; %s ; %s" % (pom, data, result, opoznienie)
            sheet1.write(pom-1, 0, pom); sheet1.write(pom-1, 1, data); sheet1.write(pom-1, 2, float(result))
            sheet1.write(pom-1, 3, float(opoznienie))        
    try:
        book.save(WORKSHEET)    
        print >> sys.stderr, "[*] Results saved also to %s spreadsheet file" % WORKSHEET
    except:
        print >> sys.stderr, "[!] Problem with saving data to %s spreadsheet file" % WORKSHEET
        sys.exit(1)
# end of extract_data_to_excel()
        
if __name__ == "__main__":    

    scheduler = sched.scheduler(time.time, time.sleep)
    usage = u"""
      _____       _                       _                         
     |_   _|     | |                     | |                        
       | |  _ __ | |_ ___ _ __ _ __   ___| |_                       
       | | | '_ \| __/ _ \ '__| '_ \ / _ \ __|                      
      _| |_| | | | ||  __/ |  | | | |  __/ |_                       
     |_____|_| |_|\__\___|_|  |_|_|_|\___|\__|    _   _             
      / ____|                   | | | |          | | (_)            
     | (___  _ __   ___  ___  __| | | |_ ___  ___| |_ _ _ __   __ _ 
      \___ \| '_ \ / _ \/ _ \/ _` | | __/ _ \/ __| __| | '_ \ / _` |
      ____) | |_) |  __/  __/ (_| | | ||  __/\__ \ |_| | | | | (_| |
     |_____/| .__/ \___|\___|\__,_|  \__\___||___/\__|_|_| |_|\__, |
            | |                                                __/ |
            |_| =========== Janusz Nawrat - 2015 ============ |___/ 
    """
    parser = OptionParser(usage=usage)
    
    parser.add_option('-s', '--samples', type='int',
                      help='Number of samples',
                      dest='no_sample', default=0)
                      
    parser.add_option('-t', '--time_of_sampling', type='int',
                      help='Time of sampling in seconds',
                      dest='time_samlple', default=0)
    
    options, args = parser.parse_args()

    if options.no_sample:
        nums = options.no_sample
    else:
        nums = int(raw_input("[>] Number of samples: "))
    
    if options.time_samlple:
        tms = options.time_samlple
    else:
        tms = int(raw_input("[>] Sampling time in seconds: "))
    
    print usage
    # parser.print_help()

    print '[*] START:', datetime.datetime.now()
    print "[*] Testing in progress..."

    for i in range(0,nums-1):
        scheduler.enter(i*tms, 1, pomiar, ('%d' % i,))

    scheduler.run()
    
    if (raw_input("[>] Do you want to extract data do Excel spreadsheet (Y/N)? ").upper()) == 'Y':
        extract_data_to_excel()

Życzę Wam dobrej zabawy z kodem. Mam nadzieję, że skrypt przyda się „sieciowcom”, do grona których mam przyjemność także należeć.

Informacje Janusz Nawrat
Just ordinary man who likes thinking...

Skomentuj

Wprowadź swoje dane lub kliknij jedną z tych ikon, aby się zalogować:

Logo WordPress.com

Komentujesz korzystając z konta WordPress.com. Log Out / Zmień )

Zdjęcie z Twittera

Komentujesz korzystając z konta Twitter. Log Out / Zmień )

Facebook photo

Komentujesz korzystając z konta Facebook. Log Out / Zmień )

Google+ photo

Komentujesz korzystając z konta Google+. Log Out / Zmień )

Connecting to %s

TOMASZ WEŁNA

artysta grafik | wykładowca

PRACOWNIA OKO

Szkoła Rysunku Malarstwa i Grafiki DR TOMASZA WEŁNY | KRAKÓW | Plac Matejki 10 | tel 691 81 75 74

Piękno neurobiologii

Blog Jerzego Vetulaniego

Teoria muzyki, zasady muzyki, podstawy muzyki

Teoria muzyki, zasady muzyki, podstawy muzyki - czyli to co każdy amator muzyki wiedzieć powinien :)

Personal Development & Inspirations

Przemyślenia i refleksje, którymi warto się podzielić (blog by Janusz Nawrat)

Business IT Cooperation Platform

Biznes i IT - dwa światy, które muszą współdziałać

%d bloggers like this: