This commit is contained in:
13
.drone.yml
Normal file
13
.drone.yml
Normal 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
24
Dockerfile
Normal 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
5
README.md
Normal 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
127
restic-monitor.py
Executable 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)
|
||||||
Reference in New Issue
Block a user