├── .gitignore ├── Dockerfile ├── LICENSE ├── README.md ├── charts └── isp-logger │ ├── Chart.yaml │ ├── README.md │ ├── templates │ ├── _helpers.tpl │ ├── deployment.yaml │ └── secret.yaml │ └── values.yaml ├── requirements.txt └── script.py /.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | venv -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.9-alpine 2 | 3 | ADD script.py . 4 | 5 | COPY requirements.txt . 6 | 7 | RUN pip install -r requirements.txt 8 | 9 | CMD [ "python", "./script.py" ] 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 isplogger.com 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 |

ISP Logger

3 |

Keep track of your internet speed.

4 | Automated internet speed logger for your connection at home, office and servers. 5 |
6 |
7 | 8 | Thank you for your interest in ISP Logger. 9 | 10 | The project is still heavily work in progress, but a basic version is working. 11 | 12 | ## Setup 13 | 14 | What you need: 15 | - Create an account over at https://isplogger.com/ 16 | - Create a network and obtain your Device ID. It's a UUID string. 17 | > For simplicity, run this project on Docker, but you're also free to run the python code directly. 18 | 19 | > It's been tested on MacOS and Linux. 20 | I'd love it if Windows users can help me out. It should work fine on Docker, but let me know if you have trouble. 21 | 22 | - Pull the project from the Docker Hub Hub (Recommended): 23 | ```shell 24 | $ docker pull ronaldl93/isp-logger 25 | ``` 26 | 27 | - If this is successful, run the container: 28 | 29 | ```shell 30 | $ docker run -it -d -e NETWORK_ID="" ronaldl93/isp-logger 31 | ``` 32 | 33 | A speed test will now perform once, and then again every 60 minutes. 34 | 35 | You can see the results on your account at ISP Logger. 36 | 37 | I highly recommend running this on a home server or computer that never sleeps / gets switched off. 38 | I'm using an old-ish computer, running Ubuntu Server. 39 | 40 | Something like a Raspberry Pi will work just fine as well. As long as it can run docker, you can run it on any device. 41 | -------------------------------------------------------------------------------- /charts/isp-logger/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | name: isp-logger 3 | description: Internet Performance Analytics Tool 4 | 5 | type: application 6 | 7 | # This is the chart version. This version number should be incremented each time you make changes 8 | # to the chart and its templates, including the app version. 9 | # Versions are expected to follow Semantic Versioning (https://semver.org/) 10 | version: 0.1.0 11 | 12 | # This is the version number of the application being deployed. This version number should be 13 | # incremented each time you make changes to the application. Versions are not expected to 14 | # follow Semantic Versioning. They should reflect the version the application is using. 15 | appVersion: latest 16 | -------------------------------------------------------------------------------- /charts/isp-logger/README.md: -------------------------------------------------------------------------------- 1 | # Helm Chart for ISP Logger 2 | 3 | ## Installation 4 | ```sh 5 | git clone https://github.com/ronaldlangeveld/isplogger_server 6 | helm install --set network_id= --set TZ= isp-logger ./isplogger_server/charts/isp-logger 7 | ``` 8 | -------------------------------------------------------------------------------- /charts/isp-logger/templates/_helpers.tpl: -------------------------------------------------------------------------------- 1 | {{/* 2 | Expand the name of the chart. 3 | */}} 4 | {{- define "isp-logger.name" -}} 5 | {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} 6 | {{- end }} 7 | 8 | {{/* 9 | Create a default fully qualified app name. 10 | We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). 11 | If release name contains chart name it will be used as a full name. 12 | */}} 13 | {{- define "isp-logger.fullname" -}} 14 | {{- if .Values.fullnameOverride }} 15 | {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} 16 | {{- else }} 17 | {{- $name := default .Chart.Name .Values.nameOverride }} 18 | {{- if contains $name .Release.Name }} 19 | {{- .Release.Name | trunc 63 | trimSuffix "-" }} 20 | {{- else }} 21 | {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} 22 | {{- end }} 23 | {{- end }} 24 | {{- end }} 25 | 26 | {{/* 27 | Create chart name and version as used by the chart label. 28 | */}} 29 | {{- define "isp-logger.chart" -}} 30 | {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} 31 | {{- end }} 32 | 33 | {{/* 34 | Common labels 35 | */}} 36 | {{- define "isp-logger.labels" -}} 37 | helm.sh/chart: {{ include "isp-logger.chart" . }} 38 | {{ include "isp-logger.selectorLabels" . }} 39 | {{- if .Chart.AppVersion }} 40 | app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} 41 | {{- end }} 42 | app.kubernetes.io/managed-by: {{ .Release.Service }} 43 | {{- end }} 44 | 45 | {{/* 46 | Selector labels 47 | */}} 48 | {{- define "isp-logger.selectorLabels" -}} 49 | app.kubernetes.io/name: {{ include "isp-logger.name" . }} 50 | app.kubernetes.io/instance: {{ .Release.Name }} 51 | {{- end }} 52 | 53 | -------------------------------------------------------------------------------- /charts/isp-logger/templates/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: {{ include "isp-logger.fullname" . }} 5 | labels: 6 | {{- include "isp-logger.labels" . | nindent 4 }} 7 | spec: 8 | replicas: {{ .Values.replicaCount }} 9 | selector: 10 | matchLabels: 11 | {{- include "isp-logger.selectorLabels" . | nindent 6 }} 12 | template: 13 | metadata: 14 | {{- with .Values.podAnnotations }} 15 | annotations: 16 | {{- toYaml . | nindent 8 }} 17 | {{- end }} 18 | labels: 19 | {{- include "isp-logger.selectorLabels" . | nindent 8 }} 20 | spec: 21 | {{- with .Values.imagePullSecrets }} 22 | imagePullSecrets: 23 | {{- toYaml . | nindent 8 }} 24 | {{- end }} 25 | securityContext: 26 | {{- toYaml .Values.podSecurityContext | nindent 8 }} 27 | containers: 28 | - name: {{ .Chart.Name }} 29 | securityContext: 30 | {{- toYaml .Values.securityContext | nindent 12 }} 31 | image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" 32 | imagePullPolicy: {{ .Values.image.pullPolicy }} 33 | env: 34 | - name: NETWORK_ID 35 | valueFrom: 36 | secretKeyRef: 37 | key: network_id 38 | name: {{ .Release.Name }}-auth 39 | - name: TZ 40 | value: {{ .Values.TZ }} 41 | resources: 42 | {{- toYaml .Values.resources | nindent 12 }} 43 | {{- with .Values.nodeSelector }} 44 | nodeSelector: 45 | {{- toYaml . | nindent 8 }} 46 | {{- end }} 47 | {{- with .Values.affinity }} 48 | affinity: 49 | {{- toYaml . | nindent 8 }} 50 | {{- end }} 51 | {{- with .Values.tolerations }} 52 | tolerations: 53 | {{- toYaml . | nindent 8 }} 54 | {{- end }} 55 | -------------------------------------------------------------------------------- /charts/isp-logger/templates/secret.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Secret 3 | metadata: 4 | name: {{ .Release.Name }}-auth 5 | data: 6 | network_id: {{ .Values.network_id | b64enc }} 7 | 8 | -------------------------------------------------------------------------------- /charts/isp-logger/values.yaml: -------------------------------------------------------------------------------- 1 | # Default values for isp-logger. 2 | 3 | replicaCount: 1 4 | 5 | image: 6 | repository: ronaldl93/isp-logger 7 | tag: "latest" 8 | 9 | network_id: "" 10 | TZ: "UTC" 11 | 12 | imagePullSecrets: [] 13 | nameOverride: "" 14 | fullnameOverride: "" 15 | 16 | podAnnotations: {} 17 | 18 | podSecurityContext: {} 19 | # fsGroup: 2000 20 | 21 | securityContext: {} 22 | # capabilities: 23 | # drop: 24 | # - ALL 25 | # readOnlyRootFilesystem: true 26 | # runAsNonRoot: true 27 | # runAsUser: 1000 28 | 29 | resources: {} 30 | # We usually recommend not to specify default resources and to leave this as a conscious 31 | # choice for the user. This also increases chances charts run on environments with little 32 | # resources, such as Minikube. If you do want to specify resources, uncomment the following 33 | # lines, adjust them as necessary, and remove the curly braces after 'resources:'. 34 | # limits: 35 | # cpu: 100m 36 | # memory: 128Mi 37 | # requests: 38 | # cpu: 100m 39 | # memory: 128Mi 40 | 41 | nodeSelector: {} 42 | 43 | tolerations: [] 44 | 45 | affinity: {} 46 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | APScheduler==3.6.3 2 | astroid==2.4.2 3 | certifi==2020.12.5 4 | chardet==4.0.0 5 | idna==2.10 6 | isort==5.6.4 7 | lazy-object-proxy==1.4.3 8 | mccabe==0.6.1 9 | pylint==2.6.0 10 | python-dotenv==0.15.0 11 | pytz==2020.5 12 | requests==2.25.1 13 | six==1.15.0 14 | speedtest-cli==2.1.3 15 | toml==0.10.2 16 | tzlocal==2.1 17 | urllib3==1.26.2 18 | wrapt==1.12.1 19 | -------------------------------------------------------------------------------- /script.py: -------------------------------------------------------------------------------- 1 | from dotenv import load_dotenv 2 | load_dotenv() 3 | import speedtest 4 | import requests 5 | import json 6 | import datetime 7 | import os 8 | from apscheduler.schedulers.blocking import BlockingScheduler 9 | import logging 10 | import ssl 11 | 12 | 13 | ssl._create_default_https_context = ssl._create_unverified_context 14 | 15 | sched = BlockingScheduler() 16 | 17 | # Enable logging 18 | logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', 19 | level=logging.INFO) 20 | 21 | logger = logging.getLogger(__name__) 22 | 23 | 24 | key = os.environ.get("NETWORK_ID", False) 25 | server = "https://api.isplogger.com" 26 | 27 | def initSpeedtest(): 28 | # key = os.environ.get("NETWORK_ID") 29 | print("Speedtest in Progress") 30 | s = speedtest.Speedtest() 31 | s.get_servers() 32 | s.get_best_server() 33 | s.download() 34 | s.upload() 35 | res = s.results.dict() 36 | data = { 37 | "status": "success", 38 | "upload": int(res["upload"]), 39 | "download": int(res["download"]), 40 | "ping": res["ping"], 41 | "isp": res["client"]["isp"], 42 | "ip": res["client"]["ip"], 43 | "country": res["client"]["country"], 44 | "sent": res["bytes_sent"], 45 | "received": res["bytes_received"] 46 | } 47 | 48 | return data 49 | 50 | @sched.scheduled_job('interval', minutes=60, id='tester') 51 | def test(): 52 | attempts = 10 53 | key = os.environ.get("NETWORK_ID") 54 | for i in range(attempts): 55 | 56 | try: 57 | 58 | tst = initSpeedtest() 59 | req = requests.post(server + "/api/speedtest/"+key, data=tst) 60 | print("Test complete") 61 | return "OK" 62 | 63 | except Exception as e: 64 | print(e) 65 | if i < attempts - 1: 66 | pass 67 | return "error" 68 | else: 69 | raise 70 | break 71 | 72 | if key is not False: 73 | test() 74 | sched.start() 75 | else: 76 | net_key = input("Enter the network key, obtained from the ISP Logger dashboard: ") 77 | os.environ["NETWORK_ID"] = str(net_key) 78 | test() 79 | sched.start() --------------------------------------------------------------------------------