├── Dockerfile ├── README.md ├── docker-compose.yml ├── pihole-influx.service └── pihole_influx.py /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.7-alpine 2 | 3 | RUN pip install --no-cache-dir influxdb 4 | 5 | WORKDIR /usr/src/app 6 | 7 | COPY pihole_influx.py ./ 8 | 9 | CMD [ "python", "/usr/src/app/pihole_influx.py" ] -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Pihole_Influx 2 | 3 | A couple of basic scripts for inserting pihole data into influxdb for graphing. 4 | 5 | *pihole_influx.py* - A python script for inserting records into influxdb. 6 | 7 | Configuration options: 8 | ``` bash 9 | HOSTNAMES = "ns-01" # Pi-hole hostname(s) to report in InfluxDB for each measurement. Comma separated list. 10 | INFLUXDB_SERVER = "127.0.0.1" # IP or hostname to InfluxDB server 11 | INFLUXDB_PORT = 8086 # Port on InfluxDB server 12 | INFLUXDB_USERNAME = "username" 13 | INFLUXDB_PASSWORD = "password" 14 | INFLUXDB_DATABASE = "piholestats" 15 | DELAY = 600 # seconds 16 | ``` 17 | *docker-compose.yml* - An example Docker setup to run this script 18 | 19 | Configuration options above can be specified within the environment section of the compose file. 20 | 21 | *pihole-influx.service* - A SystemD Unit File for starting pihole_influx at boot (and logging) 22 | On Centos7, put this file in /lib/systemd/system/. 23 | 24 | Run: 25 | ``` bash 26 | systemctl daemon-reload 27 | systemctl enable pihole-influx 28 | systemctl start pihole-influx 29 | ``` 30 | 31 | To run pihole_influx.py from the command line without the startup script: 32 | ```bash 33 | /usr/bin/python ./pihole_influx.py 34 | ``` 35 | 36 | I installed this script in /opt/pihole_influx. If you put it somewhere else you'll have to update the systemD startup script. 37 | 38 | NOTE: The script pauses for DELAY seconds at start because I had problems with the systemD script if it was started to early. If you know how to fix this please submit a pull request. 39 | 40 | ### Troubleshooting 41 | If you get the following error: 42 | ``` 43 | Traceback (most recent call last): File "./pihole_influx.py", line 11, in from influxdb import InfluxDBClient 44 | ``` 45 | You'll need to install the python-influxdb module for python. On a raspberry pi, you can do this with: 46 | ``` 47 | sudo apt-get install python-influxdb 48 | ``` 49 | 50 | Or on CentOS / RHEL: 51 | ``` 52 | yum install python-influxdb 53 | ``` 54 | --- 55 | 56 | If you get this error: 57 | ``` 58 | Traceback (most recent call last): File "./pihole_influx.py", line 8, in import requests ImportError: No module named requests 59 | ``` 60 | You'll need to install the python-requests module. 61 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | services: 3 | pihole_influx: 4 | build: . 5 | container_name: pihole_influx 6 | environment: 7 | - INFLUXDB_SERVER=172.0.0.1 8 | - INFLUXDB_PORT=8086 9 | - INFLUXDB_USERNAME=username 10 | - INFLUXDB_PASSWORD=password 11 | - INFLUXDB_DATABASE=piholestats 12 | - DELAY=600 13 | - PIHOLE_HOSTS=pi.hole,pihole2 -------------------------------------------------------------------------------- /pihole-influx.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=PiHole Influx - Send pihole stats to influxdb for Grafana 3 | After=multi-user.target 4 | 5 | [Service] 6 | User=root 7 | Type=idle 8 | ExecStart=/usr/bin/python /opt/pihole_influx/pihole_influx.py > /tmp/pihole_influx.log 2>&1 9 | 10 | [Install] 11 | WantedBy=multi-user.target 12 | -------------------------------------------------------------------------------- /pihole_influx.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/python 2 | 3 | # Script originally created by JON HAYWARD: https://fattylewis.com/Graphing-pi-hole-stats/ 4 | # Adapted to work with InfluxDB by /u/tollsjo in December 2016 5 | # Updated by Cludch December 2016 6 | # Updated and dockerised by rarosalion in September 2019 7 | 8 | # To install and run the script as a service under SystemD. See: https://linuxconfig.org/how-to-automatically-execute-shell-script-at-startup-boot-on-systemd-linux 9 | 10 | import requests 11 | import time 12 | import json 13 | import os 14 | import logging 15 | from influxdb import InfluxDBClient 16 | 17 | # Modify these values if running as a standalone script 18 | _DEFAULTS = { 19 | 'INFLUXDB_SERVER': "127.0.0.1", # IP or hostname to InfluxDB server 20 | 'INFLUXDB_PORT': 8086, # Port on InfluxDB server 21 | 'INFLUXDB_USERNAME': "username", 22 | 'INFLUXDB_PASSWORD': "password", 23 | 'INFLUXDB_DATABASE': "piholestats", 24 | 'DELAY': 600, # seconds 25 | 'PIHOLE_HOSTS': ["pi.hole"] # Pi-hole hostname(s) 26 | } 27 | 28 | 29 | def get_config(): 30 | """ Combines config options from config.json file and environment variables """ 31 | 32 | # Read a config file (json dictionary) if it exists in the script folder 33 | script_dir = os.path.dirname(os.path.realpath(__file__)) 34 | config_file = os.path.join(script_dir, 'config.json') 35 | if os.path.exists(config_file): 36 | config = json.load(open(config_file)) 37 | else: 38 | config = _DEFAULTS 39 | 40 | # Overwrite config with environment variables (set via Docker) 41 | for var_name in _DEFAULTS.keys(): 42 | config[var_name] = os.getenv(var_name, _DEFAULTS[var_name]) 43 | if var_name == 'PIHOLE_HOSTS' and ',' in config[var_name]: 44 | config[var_name] = config[var_name].split(',') 45 | 46 | # Make sure PIHOLE_HOSTS is a list (even if it's just one entry) 47 | if not isinstance(config['PIHOLE_HOSTS'], list): 48 | config['PIHOLE_HOSTS'] = [config['PIHOLE_HOSTS']] 49 | 50 | return config 51 | 52 | 53 | def check_db_status(config, logger): 54 | """ Check the required DB exists, and create it if necessary """ 55 | 56 | logger.debug("Connecting to {}".format(config['INFLUXDB_SERVER'])) 57 | client = InfluxDBClient( 58 | config['INFLUXDB_SERVER'], 59 | config['INFLUXDB_PORT'], 60 | config['INFLUXDB_USERNAME'], 61 | config['INFLUXDB_PASSWORD'] 62 | ) 63 | for db in client.get_list_database(): 64 | if db['name'] == client: 65 | logger.info('Found existing database {}.'.format(config['INFLUXDB_DATABASE'])) 66 | return True 67 | else: 68 | logger.info('Database {} not found. Will attempt to create it.'.format(config['INFLUXDB_DATABASE'])) 69 | client.create_database(config['INFLUXDB_DATABASE']) 70 | return True 71 | 72 | 73 | def send_msg(config, logger, hostname, domains_being_blocked, dns_queries_today, ads_percentage_today, ads_blocked_today): 74 | """ Sends message to InfluxDB server defined in config """ 75 | json_body = [ 76 | { 77 | "measurement": "piholestats." + hostname.replace(".", "_"), 78 | "tags": { 79 | "host": hostname 80 | }, 81 | "fields": { 82 | "domains_being_blocked": int(domains_being_blocked), 83 | "dns_queries_today": int(dns_queries_today), 84 | "ads_percentage_today": float(ads_percentage_today), 85 | "ads_blocked_today": int(ads_blocked_today) 86 | } 87 | } 88 | ] 89 | logger.debug(json_body) 90 | 91 | # InfluxDB host, InfluxDB port, Username, Password, database 92 | client = InfluxDBClient( 93 | config['INFLUXDB_SERVER'], 94 | config['INFLUXDB_PORT'], 95 | config['INFLUXDB_USERNAME'], 96 | config['INFLUXDB_PASSWORD'], 97 | config['INFLUXDB_DATABASE'] 98 | ) 99 | 100 | client.write_points(json_body) 101 | print(json_body) 102 | 103 | 104 | if __name__ == '__main__': 105 | 106 | # Setup logger 107 | logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s') 108 | logging.getLogger("requests").setLevel(logging.WARNING) 109 | logging.getLogger("urllib3").setLevel(logging.WARNING) 110 | logger = logging.getLogger(os.path.splitext(os.path.basename(__file__))[0]) 111 | 112 | # Get configuration details 113 | config = get_config() 114 | logger.info("Querying {} pihole servers: {}".format(len(config['PIHOLE_HOSTS']), config['PIHOLE_HOSTS'])) 115 | logger.info("Logging to InfluxDB server {}:{}".format( 116 | config['INFLUXDB_SERVER'], config['INFLUXDB_PORT'] 117 | )) 118 | 119 | # Create database if it doesn't exist 120 | check_db_status(config, logger) 121 | 122 | # Loop pulling stats from pihole, and pushing to influxdb 123 | while True: 124 | for host in config['PIHOLE_HOSTS']: 125 | 126 | # Get PiHole Stats 127 | pihole_api = "http://{}/admin/api.php".format(host) 128 | logger.info("Attempting to contact {} with URL {}".format(host, pihole_api)) 129 | api = requests.get(pihole_api) # URI to pihole server api 130 | API_out = api.json() 131 | domains_being_blocked = (API_out['domains_being_blocked']) 132 | dns_queries_today = (API_out['dns_queries_today']) 133 | ads_percentage_today = (API_out['ads_percentage_today']) 134 | ads_blocked_today = (API_out['ads_blocked_today']) 135 | 136 | # Update DB 137 | send_msg(config, logger, host, domains_being_blocked, dns_queries_today, ads_percentage_today, ads_blocked_today) 138 | 139 | # Wait... 140 | logger.info("Waiting {}".format(config['DELAY'])) 141 | time.sleep(int(config['DELAY'])) 142 | --------------------------------------------------------------------------------