Compare commits

..

No commits in common. "master" and "1.4.0" have entirely different histories.

13 changed files with 201 additions and 1551 deletions

View File

@ -15,7 +15,7 @@ jobs:
checks: write checks: write
strategy: strategy:
matrix: matrix:
python-version: ["3.12"] python-version: ["3.11"]
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3

View File

@ -12,7 +12,7 @@ jobs:
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v1 uses: actions/setup-python@v1
with: with:
python-version: '3.12' python-version: '3.11'
- name: Install dependencies - name: Install dependencies
run: | run: |
python -m pip install --upgrade pip python -m pip install --upgrade pip

1
.gitignore vendored
View File

@ -136,4 +136,3 @@ config.env
# Ignore ruff files # Ignore ruff files
.ruff_cache .ruff_cache
.DS_Store

View File

@ -1,4 +1,4 @@
FROM python:3.12-alpine FROM python:3.11-alpine
# Install package # Install package
WORKDIR /code WORKDIR /code

View File

@ -1,39 +0,0 @@
name: prom-qb-alltime
services:
esanchezm:
cpu_shares: 90
command: []
deploy:
resources:
limits:
memory: 7943M
environment:
- QBITTORRENT_HOST=192.168.1.58
- QBITTORRENT_PASS=Cp3mMdP!#
- QBITTORRENT_PORT=8188
- QBITTORRENT_USER=noham
image: prom-qb-alltime
labels:
icon: https://raw.githubusercontent.com/esanchezm/prometheus-qbittorrent-exporter/master/logo.png
ports:
- target: 8000
published: "9101"
protocol: tcp
restart: unless-stopped
volumes: []
devices: []
cap_add: []
network_mode: bridge
privileged: false
container_name: ""
x-casaos:
author: self
category: self
hostname: ""
icon: https://raw.githubusercontent.com/esanchezm/prometheus-qbittorrent-exporter/master/logo.png
index: /metrics
port_map: "9101"
scheme: http
store_app_id: relaxed_albert
title:
custom: prom-qb-alltime

View File

@ -2,10 +2,8 @@
## Import ## Import
To import the dashboard into your grafana, download the [dashboard.json](https://raw.githubusercontent.com/nohamr/prometheus-qbittorrent-exporter/master/grafana/dashboard.json) file and import it into your server. Select your prometheus instance and that should be all. To import the dashboard into your grafana, download the [dashboard.json](https://raw.githubusercontent.com/esanchezm/prometheus-qbittorrent-exporter/master/grafana/dashboard.json) file and import it into your server. Select your prometheus instance and that should be all.
## Screenshot ## Screenshot
![](./screenshot1.png) ![](./screenshot.png)
![](./screenshot2.png)
![](./screenshot3.png)

File diff suppressed because it is too large Load Diff

BIN
grafana/screenshot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 208 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 343 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 701 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 420 KiB

View File

@ -1,13 +1,11 @@
import faulthandler import faulthandler
import logging import logging
import os import os
# from dotenv import load_dotenv
# load_dotenv()
import signal import signal
import sys import sys
import time import time
from dataclasses import dataclass, field from dataclasses import dataclass, field
from enum import Enum, auto from enum import StrEnum, auto
from typing import Any, Iterable from typing import Any, Iterable
from prometheus_client import start_http_server from prometheus_client import start_http_server
@ -19,7 +17,8 @@ from qbittorrentapi import Client, TorrentStates
faulthandler.enable() faulthandler.enable()
logger = logging.getLogger() logger = logging.getLogger()
class MetricType(Enum):
class MetricType(StrEnum):
""" """
Represents possible metric types (used in this project). Represents possible metric types (used in this project).
""" """
@ -86,22 +85,20 @@ class QbittorrentMetricsCollector:
""" """
Returns metrics about the state of the qbittorrent server. Returns metrics about the state of the qbittorrent server.
""" """
maindata: dict[str, Any] = {} response: dict[str, Any] = {}
version: str = "" version: str = ""
# Fetch data from API # Fetch data from API
try: try:
maindata = self.client.sync_maindata() response = self.client.transfer.info
version = self.client.app.version version = self.client.app.version
except Exception as e: except Exception as e:
logger.error(f"Couldn't get server info: {e}") logger.error(f"Couldn't get server info: {e}")
server_state = maindata.get('server_state', {})
return [ return [
Metric( Metric(
name=f"{self.config['metrics_prefix']}_up", name=f"{self.config['metrics_prefix']}_up",
value=bool(server_state), value=bool(response),
labels={"version": version}, labels={"version": version},
help_text=( help_text=(
"Whether the qBittorrent server is answering requests from this" "Whether the qBittorrent server is answering requests from this"
@ -110,7 +107,7 @@ class QbittorrentMetricsCollector:
), ),
Metric( Metric(
name=f"{self.config['metrics_prefix']}_connected", name=f"{self.config['metrics_prefix']}_connected",
value=server_state.get("connection_status", "") == "connected", value=response.get("connection_status", "") == "connected",
labels={}, # no labels in the example labels={}, # no labels in the example
help_text=( help_text=(
"Whether the qBittorrent server is connected to the Bittorrent" "Whether the qBittorrent server is connected to the Bittorrent"
@ -119,7 +116,7 @@ class QbittorrentMetricsCollector:
), ),
Metric( Metric(
name=f"{self.config['metrics_prefix']}_firewalled", name=f"{self.config['metrics_prefix']}_firewalled",
value=server_state.get("connection_status", "") == "firewalled", value=response.get("connection_status", "") == "firewalled",
labels={}, # no labels in the example labels={}, # no labels in the example
help_text=( help_text=(
"Whether the qBittorrent server is connected to the Bittorrent" "Whether the qBittorrent server is connected to the Bittorrent"
@ -128,91 +125,24 @@ class QbittorrentMetricsCollector:
), ),
Metric( Metric(
name=f"{self.config['metrics_prefix']}_dht_nodes", name=f"{self.config['metrics_prefix']}_dht_nodes",
value=server_state.get("dht_nodes", 0), value=response.get("dht_nodes", 0),
labels={}, # no labels in the example labels={}, # no labels in the example
help_text="Number of DHT nodes connected to.", help_text="Number of DHT nodes connected to.",
), ),
Metric( Metric(
name=f"{self.config['metrics_prefix']}_dl_info_data", name=f"{self.config['metrics_prefix']}_dl_info_data",
value=server_state.get("dl_info_data", 0), value=response.get("dl_info_data", 0),
labels={}, # no labels in the example labels={}, # no labels in the example
help_text="Data downloaded since the server started, in bytes.", help_text="Data downloaded since the server started, in bytes.",
metric_type=MetricType.COUNTER, metric_type=MetricType.COUNTER,
), ),
Metric( Metric(
name=f"{self.config['metrics_prefix']}_up_info_data", name=f"{self.config['metrics_prefix']}_up_info_data",
value=server_state.get("up_info_data", 0), value=response.get("up_info_data", 0),
labels={}, # no labels in the example labels={}, # no labels in the example
help_text="Data uploaded since the server started, in bytes.", help_text="Data uploaded since the server started, in bytes.",
metric_type=MetricType.COUNTER, metric_type=MetricType.COUNTER,
), ),
Metric(
name=f"{self.config['metrics_prefix']}_alltime_dl",
value=server_state.get("alltime_dl", 0),
labels={}, # no labels in the example
help_text="Total data downloaded, in bytes.",
metric_type=MetricType.COUNTER,
),
Metric(
name=f"{self.config['metrics_prefix']}_alltime_ul",
value=server_state.get("alltime_ul", 0),
labels={}, # no labels in the example
help_text="Total data uploaded, in bytes.",
metric_type=MetricType.COUNTER,
),
Metric(
name=f"{self.config['metrics_prefix']}_total_peer_connections",
value=server_state.get("total_peer_connections", 0),
labels={}, # no labels in the example
help_text="total_peer_connections.",
metric_type=MetricType.COUNTER,
),
#### Disk metrics
Metric(
name=f"{self.config['metrics_prefix']}_write_cache_overload",
value=server_state.get("write_cache_overload", 0),
labels={}, # no labels in the example
help_text="write_cache_overload.",
metric_type=MetricType.COUNTER,
),
Metric(
name=f"{self.config['metrics_prefix']}_read_cache_overload",
value=server_state.get("read_cache_overload", 0),
labels={}, # no labels in the example
help_text="read_cache_overload.",
metric_type=MetricType.COUNTER,
),
Metric(
name=f"{self.config['metrics_prefix']}_read_cache_hits",
value=server_state.get("read_cache_hits", 0),
labels={}, # no labels in the example
help_text="read_cache_hits.",
metric_type=MetricType.COUNTER,
),
Metric(
name=f"{self.config['metrics_prefix']}_average_time_queue",
value=server_state.get("average_time_queue", 0),
labels={}, # no labels in the example
help_text="average_time_queue.",
metric_type=MetricType.COUNTER,
),
Metric(
name=f"{self.config['metrics_prefix']}_free_space_on_disk",
value=server_state.get("free_space_on_disk", 0),
labels={}, # no labels in the example
help_text="free_space_on_disk.",
metric_type=MetricType.COUNTER,
),
Metric(
name=f"{self.config['metrics_prefix']}_queued_io_jobs",
value=server_state.get("queued_io_jobs", 0),
labels={}, # no labels in the example
help_text="queued_io_jobs.",
metric_type=MetricType.COUNTER,
),
] ]
def _fetch_categories(self) -> dict: def _fetch_categories(self) -> dict:

View File

@ -270,20 +270,6 @@ class TestQbittorrentMetricsCollector(unittest.TestCase):
help_text="Data uploaded since the server started, in bytes.", help_text="Data uploaded since the server started, in bytes.",
metric_type=MetricType.COUNTER, metric_type=MetricType.COUNTER,
), ),
Metric(
name="qbittorrent_alltime_dl",
value=0,
labels={}, # no labels in the example
help_text="Total data downloaded, in bytes.",
metric_type=MetricType.COUNTER,
),
Metric(
name="qbittorrent_alltime_ul",
value=0,
labels={}, # no labels in the example
help_text="Total data uploaded, in bytes.",
metric_type=MetricType.COUNTER,
),
] ]
metrics = self.collector._get_qbittorrent_status_metrics() metrics = self.collector._get_qbittorrent_status_metrics()