Initial commit
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
2020-09-05 13:20:22 +02:00
commit 354ca56a80
4 changed files with 169 additions and 0 deletions

13
.drone.yml Normal file
View File

@@ -0,0 +1,13 @@
kind: pipeline
type: kubernetes
name: default
steps:
- name: image
image: registry.hkoerber.de/drone-kaniko:v1.0.0
pull: true
settings:
dockerfile: Dockerfile
registry: registry.hkoerber.de
repo: prometheus-restic-backblaze
tags:
- ${DRONE_COMMIT_SHA}

24
Dockerfile Normal file
View File

@@ -0,0 +1,24 @@
FROM debian:buster
RUN : \
&& apt-get update \
&& apt-get install -y \
curl \
python3 \
python3-prometheus-client \
python3-yaml \
restic \
&& apt-get -y clean
RUN : \
&& curl -s -L -o /usr/local/bin/dumb-init https://github.com/Yelp/dumb-init/releases/download/v1.2.2/dumb-init_1.2.2_amd64 \
&& chmod +x /usr/local/bin/dumb-init
RUN : \
&& curl -s -L https://github.com/restic/restic/releases/download/v0.9.6/restic_0.9.6_linux_amd64.bz2 \
| bzip2 > /usr/local/bin/restic \
&& chmod +x /usr/local/bin/restic
COPY ./restic-monitor.py /usr/local/bin/restic-monitor
CMD ["/usr/local/bin/dumb-init", "--", "/usr/local/bin/restic-monitor", "/etc/restic-backblaze-config.yml"]

5
README.md Normal file
View File

@@ -0,0 +1,5 @@
```
$ docker build -t prometheus-restic-backblaze .
$ docker run -v $(pwd)/config.yml:/etc/restic-config.yml prometheus-restic-backblaze
```

127
restic-monitor.py Executable file
View File

@@ -0,0 +1,127 @@
#!/usr/bin/env python3
import os
import json
import sys
import datetime
import subprocess
import time
import prometheus_client
import yaml
with open(sys.argv[1], 'r') as fd:
config = yaml.safe_load(fd)
output = {}
class ResticBackblazeCollector(object):
def __init__(self):
self._last_update = 0
def get_info(self):
for repo in config['repositories']:
print("Collecting snapshots from backblaze")
cmd = [
"/usr/bin/restic",
"--repo", "b2:{bucket}:{folder}".format(
bucket=repo['bucket'],
folder=repo['folder']),
"snapshots",
"--json",
"--no-cache",
"--no-lock"
]
env = {
"B2_ACCOUNT_ID": repo["b2_account_id"],
"B2_ACCOUNT_KEY": repo["b2_account_key"],
"RESTIC_PASSWORD": repo["restic_password"]
}
restic_cmd = subprocess.run(
cmd,
stdout=subprocess.PIPE,
check=True,
env=env
)
print("Done")
snapshots = json.loads(restic_cmd.stdout)
def _get_backup_id(snapshot):
hostname = snapshot['hostname']
paths = ','.join(snapshot['paths'])
return (hostname, paths)
def _parse_timestamp(timestamp):
# get rid of everything after .<millieconds>
timestamp = timestamp.split('.')[0]
return datetime.datetime.strptime(
timestamp,
'%Y-%m-%dT%H:%M:%S'
)
distinct_backups = []
for snapshot in snapshots:
backup = _get_backup_id(snapshot)
if backup not in distinct_backups:
distinct_backups.append(backup)
latest_snapshots = []
for backup in distinct_backups:
latest = None
latest_timestamp = datetime.datetime.fromtimestamp(0) # unixtime 0
for snapshot in snapshots:
if _get_backup_id(snapshot) == backup:
timestamp = _parse_timestamp(snapshot['time'])
if timestamp > latest_timestamp:
latest = snapshot
latest_timestamp = timestamp
latest_snapshots.append(latest)
output[repo['name']] = []
for snapshot in latest_snapshots:
output[repo['name']].append({
"host": snapshot["hostname"],
"paths": ",".join(snapshot["paths"]),
"timestamp": _parse_timestamp(snapshot['time']).timestamp(),
})
return output
def collect(self):
called = time.time()
age = called - self._last_update
if age > 24 * 60 * 60:
self._output = self.get_info()
self._last_update = time.time()
else:
print("Using cached metrics")
metric_timestamp = prometheus_client.core.GaugeMetricFamily(
'restic_latest_snapshot_timestamp',
"Timestamp of the latest restic snapshot",
labels=["repository", "host", "paths"])
for repo, info in self._output.items():
for snapshot in info:
metric_timestamp.add_metric(
labels=[
repo,
snapshot["host"],
snapshot["paths"]
],
value=snapshot["timestamp"]
)
yield metric_timestamp
prometheus_client.REGISTRY.register(ResticBackblazeCollector())
if __name__ == '__main__':
prometheus_client.start_http_server(int(os.environ["LISTEN_PORT"]))
while True:
time.sleep(1)