First commit

This commit is contained in:
2025-04-28 10:01:03 +02:00
commit d8028d17ca
5 changed files with 243 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
venv/
*.csv
.env

15
Dockerfile Normal file
View File

@@ -0,0 +1,15 @@
# Init image
FROM python:slim
WORKDIR /app
# Install requirements
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Import app
COPY pybeemo .
RUN chmod +x pybeemo
# Run it
EXPOSE 8000
CMD ["./pybeemo"]

13
compose.yaml Normal file
View File

@@ -0,0 +1,13 @@
---
services:
pybeemo:
container_name: pybeemo
restart: always
image: ramiuslr/pybeemo:latest # You should set this tag to a fixed version
ports:
- 8000:8000
environment:
PYBEEMO_USER: <username>
PYBEEMO_PASSWORD: <password>
# PYBEEMO_INTERVAL: optional, defaults to 30 mins
...

209
pybeemo Executable file
View File

@@ -0,0 +1,209 @@
#!/bin/env python
import http.server
import io
import os
import socketserver
import sys
import time
from datetime import datetime
from threading import Lock, Thread
import pandas as pd
from dotenv import load_dotenv
from requests import Session
# Global variables
data_lock = Lock()
csv_data = {"licenses": None, "backupsets": None, "groups": None}
class BeemoHandler(http.server.SimpleHTTPRequestHandler):
def do_GET(self):
with data_lock:
if self.path == "/licenses.csv":
self.serve_csv(csv_data["licenses"])
elif self.path == "/backupsets.csv":
self.serve_csv(csv_data["backupsets"])
elif self.path == "/groups.csv":
self.serve_csv(csv_data["groups"])
else:
self.send_response(404)
self.end_headers()
self.wfile.write(b"File not found")
def serve_csv(self, data):
if data is None:
self.send_response(503)
self.send_header("Content-type", "text/plain")
self.end_headers()
self.wfile.write(b"Service unavailable - data not yet generated")
return
self.send_response(200)
self.send_header("Content-type", "text/csv")
self.send_header(
"Content-Disposition", "attachment; filename=" + self.path[1:]
)
self.send_header("Content-Length", str(len(data)))
self.end_headers()
self.wfile.write(data.encode("utf-8"))
def main():
# Read parameters from env
load_dotenv()
username = os.getenv("PYBEEMO_USER")
if not username:
print("ERROR: Please set username")
sys.exit(1)
password = os.getenv("PYBEEMO_PASSWORD")
if not password:
print("ERROR: Please set a password")
sys.exit(1)
port = 8000
interval = os.getenv("PYBEEMO_INTERVAL")
if not interval:
print("No interval provided, setting default (30)")
interval = 30
else:
interval = int(interval)
# Init persistent session
s = Session()
# Login
login(s, username, password)
# Start data update thread
update_thread = Thread(target=update_data, args=(s, interval), daemon=True)
update_thread.start()
# Start HTTP server
Handler = BeemoHandler
with socketserver.TCPServer(("", port), Handler) as httpd:
print(f"[{datetime.now()}] Running at http://localhost:{port}")
print("Available endpoints:")
print(f" http://localhost:{port}/licenses.csv")
print(f" http://localhost:{port}/backupsets.csv")
print(f" http://localhost:{port}/groups.csv")
print(f"Data refresh interval: {interval} minutes")
httpd.serve_forever()
def login(s: Session, username: str, password: str) -> bool:
url = "https://client.beemotechnologie.com/login_validate.php"
data = {
"login": username,
"mdp": password,
"type": "login",
}
r = s.post(url, data=data)
# Handle failed login checking response url
if r.url == "https://client.beemotechnologie.com/login.php":
print("Login failure, please check username and password")
sys.exit(1)
# Functions used to format retrieved data
def get_licenses(s: Session):
url = "https://client.beemotechnologie.com/logic/export_licence.php"
r = io.BytesIO(s.get(url).content)
cols = [
"License",
"Client",
"Storage (GB)",
"Remote Storage Quota (GB)",
"Disk Usage",
"Remote status",
]
df = pd.read_csv(r, usecols=cols, sep=";", encoding="ISO-8859-1")
mapping = {
"License": "Numéro de licence",
"Client": "Client",
"Storage (GB)": "Volume externalisé",
"Remote Storage Quota (GB)": "Quota",
"Disk Usage": "Utilisation du disque local",
"Remote status": "Etat de l'externalisation",
}
df.rename(columns=mapping, inplace=True)
df["Utilisation du disque local"] = df[
"Utilisation du disque local"
].str.replace("Unknown", "0")
df["Utilisation du disque local"] = df[
"Utilisation du disque local"
].str.replace(" %", "")
df["Utilisation du disque local"] = (
df["Utilisation du disque local"].str.replace(",", ".").astype(float)
)
df["Etat de l'externalisation"] = df[
"Etat de l'externalisation"
].str.replace(" %", "")
df["Ratio"] = df["Volume externalisé"] / df["Quota"] * 100
df["Ratio"] = df["Ratio"].round(1)
return df.to_csv(index=False)
def get_backupsets(s: Session):
url = "https://client.beemotechnologie.com/logic/export_jds.php"
r = io.BytesIO(s.get(url).content)
cols = ["License", "Client", "Backupsets", "Backup Status"]
df = pd.read_csv(r, usecols=cols, sep=";", encoding="ISO-8859-1")
mapping = {
"License": "Numéro de licence",
"Client": "Client",
"Backupsets": "Jeu de sauvegarde",
"Backup Status": "Etat",
}
df.rename(columns=mapping, inplace=True)
df.query('Etat != "Ok"', inplace=True)
return df.to_csv(index=False)
def get_groups(s: Session):
url = "https://client.beemotechnologie.com/logic/export_groupes.php"
r = io.BytesIO(s.get(url).content)
cols = ["Name", "Storage (GB)", "Remote Storage Quota (GB)"]
df = pd.read_csv(r, usecols=cols, sep=";", encoding="ISO-8859-1")
mapping = {
"Name": "Client",
"Storage (GB)": "Utilisé",
"Remote Storage Quota (GB)": "Quota",
}
df.rename(columns=mapping, inplace=True)
df["Utilisé"] = df["Utilisé"].str.replace(",", ".").astype(float)
df["Ratio"] = df["Utilisé"] / df["Quota"] * 100
df["Ratio"] = df["Ratio"].round(1)
return df.to_csv(index=False)
def update_data(s: Session, interval):
while True:
print(f"[{datetime.now()}] Updating data...")
# Update licenses
data = get_licenses(s)
with data_lock:
csv_data["licenses"] = data
print(f"[{datetime.now()}] Updated licenses.csv in memory")
# Update licenses
data = get_backupsets(s)
with data_lock:
csv_data["backupsets"] = data
print(f"[{datetime.now()}] Updated backupsets.csv in memory")
# Update groups
data = get_groups(s)
with data_lock:
csv_data["groups"] = data
print(f"[{datetime.now()}] Updated groups.csv in memory")
# Wait time interval before updating again
time.sleep(interval * 60)
main()

3
requirements.txt Normal file
View File

@@ -0,0 +1,3 @@
pandas
requests
dotenv