aidengilmartin/speedtest-to-influxdb

Module Not Found Error

Closed this issue · 28 comments

Hello!

Hopefully a not-too-dumb question, but when trying to run main.py I get the following error:

pi@raspberrypi:~/speedtest-to-influxdb $ python3 ./main.py
Traceback (most recent call last):
  File "./main.py", line 5, in <module>
    from influxdb import InfluxDBClient
ModuleNotFoundError: No module named 'influxdb'

I have installed the Python InfluxdDB module with # sudo pip install influxdb and confirmed it $ python -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())" | xargs ls which does return influxdb and influxdb-3.0.0.egg-info_

My only (beginner level) guess is that the path to the module isn't working? Any ideas that might get me pointed in the right direction?

Hi, definitely not too dumb.

Could it be something to do with pip vs pip3?
Try installing python3-pip, and installing the package with pip3 install influxdb. See how that goes.

Hey! Glad to know it's not too idiotic. We're making progress - I've installed python3:

pi@raspberrypi:~/Python-3.6.3 $ /usr/bin/python3 -V
Python 3.7.3

and installed influxdb as sudo (got a permissions issue otherwise).

Now when running main.py I get the following:

pi@raspberrypi:~/speedtest-to-influxdb $ python3 ./main.py
Speedtest CLI Data Logger to InfluxDB
Traceback (most recent call last):
  File "./main.py", line 98, in <module>
    main()
  File "./main.py", line 81, in main
    ["speedtest", "--accept-license", "--accept-gdpr", "-f", "json"], capture_output=True)
  File "/usr/local/lib/python3.6/subprocess.py", line 403, in run
    with Popen(*popenargs, **kwargs) as process:
TypeError: __init__() got an unexpected keyword argument 'capture_output'

Any further ideas? I'm keeping my fingers crossed, this seems like a fantastic opportunity for me to learn a bit more about influxdb and grafana! Thanks again!

After a quick visit to stackoverflow it would seem that this is a difference with Python 3.6 and Python 3.7+ which my system and the Docker container uses.

Try adding the line from subprocess import PIPE below import subprocess and replace capture_output=True with stdout=PIPE, stderr=PIPE

Or you can just replace the contents of main.py with that below. Remembering to adjust the configuration variables accordingly obviously. Should work after that.

import time
import json
import subprocess
from subprocess import PIPE

from influxdb import InfluxDBClient

# InfluxDB Settings
DB_ADDRESS = 'db_hostname.network'
DB_PORT = 8086
DB_USER = 'db_username'
DB_PASSWORD = 'db_password'
DB_DATABASE = 'speedtest_db'

# Speedtest Settings
TEST_INTERVAL = 1800  # Time between tests (in seconds).
TEST_FAIL_INTERVAL = 60  # Time before retrying a failed Speedtest (in seconds).

influxdb_client = InfluxDBClient(
    DB_ADDRESS, DB_PORT, DB_USER, DB_PASSWORD, None)


def init_db():
    databases = influxdb_client.get_list_database()

    if len(list(filter(lambda x: x['name'] == DB_DATABASE, databases))) == 0:
        influxdb_client.create_database(
            DB_DATABASE)  # Create if does not exist.
    else:
        influxdb_client.switch_database(DB_DATABASE)  # Switch to if does exist.


def format_for_influx(cliout):
    data = json.loads(cliout)
    # There is additional data in the speedtest-cli output but it is likely not necessary to store.
    influx_data = [
        {
            'measurement': 'ping',
            'time': data['timestamp'],
            'fields': {
                'jitter': data['ping']['jitter'],
                'latency': data['ping']['latency']
            }
        },
        {
            'measurement': 'download',
            'time': data['timestamp'],
            'fields': {
                # Byte to Megabit
                'bandwidth': data['download']['bandwidth'] / 125000,
                'bytes': data['download']['bytes'],
                'elapsed': data['download']['elapsed']
            }
        },
        {
            'measurement': 'upload',
            'time': data['timestamp'],
            'fields': {
                # Byte to Megabit
                'bandwidth': data['upload']['bandwidth'] / 125000,
                'bytes': data['upload']['bytes'],
                'elapsed': data['upload']['elapsed']
            }
        },
        {
            'measurement': 'packetLoss',
            'time': data['timestamp'],
            'fields': {
                'packetLoss': data['packetLoss']
            }
        }
    ]

    return influx_data


def main():
    init_db()  # Setup the database if it does not already exist.

    while (1):  # Run a Speedtest and send the results to influxDB indefinitely.
        speedtest = subprocess.run(
            ["speedtest", "--accept-license", "--accept-gdpr", "-f", "json"], stdout=PIPE, stderr=PIPE)

        if speedtest.returncode == 0:  # Speedtest was successful.
            data = format_for_influx(speedtest.stdout)
            print("Speedtest Successful:")
            if influxdb_client.write_points(data) == True:
                print("Data written to DB successfully")
                time.sleep(TEST_INTERVAL)
        else:  # Speedtest failed.
            print("Speedtest Failed:")
            print(speedtest.stderr)
            print(speedtest.stdout)
            time.sleep(TEST_FAIL_INTERVAL)


if __name__ == '__main__':
    print('Speedtest CLI Data Logger to InfluxDB')
    main()

Thanks so much for the fix dude! I think that moved things forward... but still not out of the woods entirely. After making the above fixes, here's the result:

pi@raspberrypi:~/speedtest-to-influxdb $ python3 ./main.py
Speedtest CLI Data Logger to InfluxDB
Traceback (most recent call last):
  File "./main.py", line 99, in <module>
    main()
  File "./main.py", line 85, in main
    data = format_for_influx(speedtest.stdout)
  File "./main.py", line 34, in format_for_influx
    data = json.loads(cliout)
  File "/usr/local/lib/python3.6/json/__init__.py", line 354, in loads
    return _default_decoder.decode(s)
  File "/usr/local/lib/python3.6/json/decoder.py", line 339, in decode
    obj, end = self.raw_decode(s, idx=_w(s, 0).end())
  File "/usr/local/lib/python3.6/json/decoder.py", line 357, in raw_decode
    raise JSONDecodeError("Expecting value", s, err.value) from None
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)

Could you post the output of speedtest --accept-license --accept-gdpr -f json?
You will need to wait a bit as there is no progress indicator with these settings.

See if you get a valid output.

Interestingly enough the output popped right up!

pi@raspberrypi:~/speedtest-to-influxdb $ speedtest --accept-license --accept-gdpr -f json
usage: speedtest [-h] [--no-download] [--no-upload] [--single] [--bytes]
                 [--share] [--simple] [--csv] [--csv-delimiter CSV_DELIMITER]
                 [--csv-header] [--json] [--list] [--server SERVER]
                 [--exclude EXCLUDE] [--mini MINI] [--source SOURCE]
                 [--timeout TIMEOUT] [--secure] [--no-pre-allocate]
                 [--version]
speedtest: error: unrecognized arguments: --accept-license --accept-gdpr -f json

Maybe try running speedtest with no arguments, see if that works. If it does try -f json , accept-gdpr etc, and see which is unrecognised?

Hmm speedtest works just fine, but none of the other commands are recognised, oddly enough...

pi@raspberrypi:~/speedtest-to-influxdb $ -f json
-bash: -f: command not found
pi@raspberrypi:~/speedtest-to-influxdb $ --accept-gdpr
-bash: --accept-gdpr: command not found
pi@raspberrypi:~/speedtest-to-influxdb $ --accept-license
-bash: --accept-license: command not found
pi@raspberrypi:~/speedtest-to-influxdb $ 

You need to run speedtest and pass -f json, --accept-gdpr and --accept-license as arguments.

Such as,

speedtest -f json

speedtest -f json --accept-license --accept-gdpr

Ah well that was rather daft of me! Here's what we got:

pi@raspberrypi:~/speedtest-to-influxdb $ speedtest -f json
usage: speedtest [-h] [--no-download] [--no-upload] [--single] [--bytes]
                 [--share] [--simple] [--csv] [--csv-delimiter CSV_DELIMITER]
                 [--csv-header] [--json] [--list] [--server SERVER]
                 [--exclude EXCLUDE] [--mini MINI] [--source SOURCE]
                 [--timeout TIMEOUT] [--secure] [--no-pre-allocate]
                 [--version]
speedtest: error: unrecognized arguments: -f json
pi@raspberrypi:~/speedtest-to-influxdb $ speedtest -f json --accept-license --accept-gdpr
usage: speedtest [-h] [--no-download] [--no-upload] [--single] [--bytes]
                 [--share] [--simple] [--csv] [--csv-delimiter CSV_DELIMITER]
                 [--csv-header] [--json] [--list] [--server SERVER]
                 [--exclude EXCLUDE] [--mini MINI] [--source SOURCE]
                 [--timeout TIMEOUT] [--secure] [--no-pre-allocate]
                 [--version]
speedtest: error: unrecognized arguments: -f json --accept-license --accept-gdpr
pi@raspberrypi:~/speedtest-to-influxdb $ speedtest --accept-license --accept-gdpr
usage: speedtest [-h] [--no-download] [--no-upload] [--single] [--bytes]
                 [--share] [--simple] [--csv] [--csv-delimiter CSV_DELIMITER]
                 [--csv-header] [--json] [--list] [--server SERVER]
                 [--exclude EXCLUDE] [--mini MINI] [--source SOURCE]
                 [--timeout TIMEOUT] [--secure] [--no-pre-allocate]
                 [--version]
speedtest: error: unrecognized arguments: --accept-license --accept-gdpr

Does speedtest run without any arguments at all?

Perfectly:

pi@raspberrypi:~/speedtest-to-influxdb $ speedtest
Retrieving speedtest.net configuration...
Testing from BT (86.184.77.75)...
Retrieving speedtest.net server list...
Selecting best server based on ping...
Hosted by Redraw Internet (London) [5.22 km]: 18.629 ms
Testing download speed...............................................................................
.Download: 33.83 Mbit/s
Testing upload speed................................................................................................
Upload: 16.80 Mbit/s

I really wish I had any ideas here... and really do appreciate the help on this!

Should have noticed this before, it seems you have installed speedtest-cli from the Debian repositories. It is an unofficial client which is launched with the same command (speedtest).

This script is for use with the relatively recently released official client from Ookla. As their client is proprietary it can't be included in the Debian repositories.

You should be able to get this working by doing the following.

  1. Uninstall speedtest-cli
    sudo apt remove speedtest-cli

  2. Install the Ooka Speedtest application (https://www.speedtest.net/apps/cli)

    sudo apt-get install gnupg1 apt-transport-https dirmngr

    export INSTALL_KEY=379CE192D401AB61

    export DEB_DISTRO=$(lsb_release -sc)

    sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys $INSTALL_KEY

    echo "deb https://ookla.bintray.com/debian ${DEB_DISTRO} main" | sudo tee /etc/apt/sources.list.d/speedtest.list

    sudo apt update

    sudo apt install speedtest

Try running the script then, should all work.

Ah super useful notice! So, before running through all of that, I attempted to uninstall the incorrect speedtest and got the following:

pi@raspberrypi:~/speedtest-to-influxdb $ sudo apt remove speedtest-cli
Reading package lists... Done
Building dependency tree       
Reading state information... Done
Package 'speedtest-cli' is not installed, so not removed
The following package was automatically installed and is no longer required:
  point-rpi
Use 'sudo apt autoremove' to remove it.
0 upgraded, 0 newly installed, 0 to remove and 12 not upgraded.

Before attempting to install the right app, I just wanted to check that we weren't missing something as it doesn't look like speedtest-cli was installed. I tried uninstalling both at ~ as well as ~/speedtest-to-influxdb

Right, so it looks like speedtest-cli didn't uninstall correctly.
What does which speedtest output?

Always something! Here's the result:

pi@raspberrypi:~/speedtest-to-influxdb $ which speedtest
/usr/local/bin/speedtest

So if you run /usr/local/bin/speedtest --version what do you get?

You're absolutely right on the flavor of speedtest!

pi@raspberrypi:~/speedtest-to-influxdb $ /usr/local/bin/speedtest --version
speedtest-cli 2.1.2
Python 2.7.16 (default, Oct 10 2019, 22:02:15) [GCC 8.3.0]

Okay, so you'll have to manually remove it which isn't ideal but should work for this.

sudo rm /usr/local/bin/speedtest

Then try installing the Ookla Speedtest with the instructions in step 2 above (or on the Ookla website).

At long last, seeming success! Thanks so much mate!

pi@raspberrypi:~/speedtest-to-influxdb $ python3 ./main.py
Speedtest CLI Data Logger to InfluxDB
Speedtest Successful:
Data written to DB successfully

Question - it seems that the script is actively running and I'm guessing I'm not supposed to exit out of it? Therefore should I run it in a screen or something to allow it to do it's thing? Additionally as it does it's own loop, I don't need to set it up as a cronjob or anything right?

Yeah, quitting the script will kill it so you will have to find your own way of starting it on boot.

Without modifying the script, i'd probably make it a systemd service which starts on boot and leaves it running in the background. You can change the frequency that it runs a speedtest with a variable in the script.

This tutorial looks promising.

Amazing, thanks again so much for everything! Now on to figuring out Grafana!

No worries, glad to help.

As for running the script if you would prefer to use cron to schedule tests you could replace time.sleep(TEST_INTERVAL) with quit() and the script will stop after a successful test.

Ah thanks! I'll have a think about whether or not cron or screen/systemd might be best! I hate to come back with a other error so quickly, but I rebooted the pi just to try and recreate everything from a clean slate - this is what happened:

pi@raspberrypi:~/speedtest-to-influxdb $ python3 ./main.py
Speedtest CLI Data Logger to InfluxDB
Traceback (most recent call last):
  File "/usr/local/lib/python3.6/site-packages/urllib3/connection.py", line 157, in _new_conn
    (self._dns_host, self.port), self.timeout, **extra_kw
  File "/usr/local/lib/python3.6/site-packages/urllib3/util/connection.py", line 84, in create_connection
    raise err
  File "/usr/local/lib/python3.6/site-packages/urllib3/util/connection.py", line 74, in create_connection
    sock.connect(sa)
ConnectionRefusedError: [Errno 111] Connection refused

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/local/lib/python3.6/site-packages/urllib3/connectionpool.py", line 672, in urlopen
    chunked=chunked,
  File "/usr/local/lib/python3.6/site-packages/urllib3/connectionpool.py", line 387, in _make_request
    conn.request(method, url, **httplib_request_kw)
  File "/usr/local/lib/python3.6/http/client.py", line 1239, in request
    self._send_request(method, url, body, headers, encode_chunked)
  File "/usr/local/lib/python3.6/http/client.py", line 1285, in _send_request
    self.endheaders(body, encode_chunked=encode_chunked)
  File "/usr/local/lib/python3.6/http/client.py", line 1234, in endheaders
    self._send_output(message_body, encode_chunked=encode_chunked)
  File "/usr/local/lib/python3.6/http/client.py", line 1026, in _send_output
    self.send(msg)
  File "/usr/local/lib/python3.6/http/client.py", line 964, in send
    self.connect()
  File "/usr/local/lib/python3.6/site-packages/urllib3/connection.py", line 184, in connect
    conn = self._new_conn()
  File "/usr/local/lib/python3.6/site-packages/urllib3/connection.py", line 169, in _new_conn
    self, "Failed to establish a new connection: %s" % e
urllib3.exceptions.NewConnectionError: <urllib3.connection.HTTPConnection object at 0xb5d3b830>: Failed to establish a new connection: [Errno 111] Connection refused

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/local/lib/python3.6/site-packages/requests/adapters.py", line 449, in send
    timeout=timeout
  File "/usr/local/lib/python3.6/site-packages/urllib3/connectionpool.py", line 720, in urlopen
    method, url, error=e, _pool=self, _stacktrace=sys.exc_info()[2]
  File "/usr/local/lib/python3.6/site-packages/urllib3/util/retry.py", line 436, in increment
    raise MaxRetryError(_pool, url, error or ResponseError(cause))
urllib3.exceptions.MaxRetryError: HTTPConnectionPool(host='localhost', port=8086): Max retries exceeded with url: /query?q=SHOW+DATABASES (Caused by NewConnectionError('<urllib3.connection.HTTPConnection object at 0xb5d3b830>: Failed to establish a new connection: [Errno 111] Connection refused',))

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "./main.py", line 99, in <module>
    main()
  File "./main.py", line 78, in main
    init_db()  # Setup the database if it does not already exist.
  File "./main.py", line 24, in init_db
    databases = influxdb_client.get_list_database()
  File "/usr/local/lib/python3.6/site-packages/influxdb/client.py", line 617, in get_list_database
    return list(self.query("SHOW DATABASES").get_points())
  File "/usr/local/lib/python3.6/site-packages/influxdb/client.py", line 450, in query
    expected_response_code=expected_response_code
  File "/usr/local/lib/python3.6/site-packages/influxdb/client.py", line 283, in request
    timeout=self._timeout
  File "/usr/local/lib/python3.6/site-packages/requests/sessions.py", line 533, in request
    resp = self.send(prep, **send_kwargs)
  File "/usr/local/lib/python3.6/site-packages/requests/sessions.py", line 646, in send
    r = adapter.send(request, **kwargs)
  File "/usr/local/lib/python3.6/site-packages/requests/adapters.py", line 516, in send
    raise ConnectionError(e, request=request)
requests.exceptions.ConnectionError: HTTPConnectionPool(host='localhost', port=8086): Max retries exceeded with url: /query?q=SHOW+DATABASES (Caused by NewConnectionError('<urllib3.connection.HTTPConnection object at 0xb5d3b830>: Failed to establish a new connection: [Errno 111] Connection refused',))

... I just have my head in my hands at this point...

Seems like InfluxDB might not be running.

What does systemctl status influxdb say?

Ah it returns this:

pi@raspberrypi:~/speedtest-to-influxdb $ systemctl status influxdb
● influxdb.service - InfluxDB is an open-source, distributed, time series database
   Loaded: loaded (/lib/systemd/system/influxdb.service; enabled; vendor preset: enabled)
   Active: active (running) since Thu 2020-01-09 16:00:00 GMT; 11min ago
     Docs: https://docs.influxdata.com/influxdb/
 Main PID: 242 (influxd)
   Memory: 186.7M
   CGroup: /system.slice/influxdb.service
           └─242 /usr/bin/influxd -config /etc/influxdb/influxdb.conf

Jan 09 16:06:17 raspberrypi influxd[242]: ts=2020-01-09T16:06:17.379755Z lvl=info msg="Starting snapshot service
Jan 09 16:06:17 raspberrypi influxd[242]: ts=2020-01-09T16:06:17.385005Z lvl=info msg="Starting continuous query
Jan 09 16:06:17 raspberrypi influxd[242]: ts=2020-01-09T16:06:17.389076Z lvl=info msg="Starting HTTP service" lo
Jan 09 16:06:17 raspberrypi influxd[242]: ts=2020-01-09T16:06:17.389405Z lvl=info msg="opened HTTP access log" l
Jan 09 16:06:17 raspberrypi influxd[242]: ts=2020-01-09T16:06:17.389586Z lvl=info msg="Auth is enabled but share
Jan 09 16:06:17 raspberrypi influxd[242]: ts=2020-01-09T16:06:17.391671Z lvl=info msg="Listening on HTTP" log_id
Jan 09 16:06:17 raspberrypi influxd[242]: ts=2020-01-09T16:06:17.397245Z lvl=info msg="Starting retention policy
Jan 09 16:06:17 raspberrypi influxd[242]: ts=2020-01-09T16:06:17.399308Z lvl=info msg="Listening for signals" lo
Jan 09 16:06:17 raspberrypi influxd[242]: ts=2020-01-09T16:06:17.400008Z lvl=info msg="Storing statistics" log_i
Jan 09 16:06:18 raspberrypi influxd[242]: ts=2020-01-09T16:06:18.206637Z lvl=info msg="Sending usage statistics 
lines 1-19/19 (END)
[1]+  Stopped                 systemctl status influxdb

Okay, so it's running. What's the output of sudo netstat -ltnp? If this doesn't run you may need to run sudo apt install net-tools.

Ah huh, it worked when I ran it again! It must be that influxdb is just slow to boot up when restarting. I noticed that yesterday when running influx it would take a good 3-4 minutes until I got the console line. I kinda presumed that was because I was running all this off an ancient Raspi3 that I had in the closet, but thats only a finger in the air assumption. When I ran main.py again I got what we were looking for:

pi@raspberrypi:~/speedtest-to-influxdb $ python3 ./main.py
Speedtest CLI Data Logger to InfluxDB
Speedtest Successful:
Data written to DB successfully
^Z
[2]+  Stopped                 python3 ./main.py