2025-12-29 11:39:57 +01:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
2023-12-25 23:20:30 +01:00
|
|
|
import argparse
|
2023-12-25 23:36:21 +01:00
|
|
|
import os
|
2025-12-29 11:39:57 +01:00
|
|
|
import re
|
|
|
|
|
import sys
|
|
|
|
|
import pandas as pd
|
|
|
|
|
from typing import Optional
|
2023-12-25 23:20:30 +01:00
|
|
|
|
2025-12-28 22:12:31 +01:00
|
|
|
|
2025-12-29 11:39:57 +01:00
|
|
|
DB_NAME_RE = re.compile(r"^[a-zA-Z0-9_][a-zA-Z0-9_-]*$")
|
|
|
|
|
|
|
|
|
|
def _validate_database_value(value: Optional[str], *, instance: str) -> str:
|
|
|
|
|
v = (value or "").strip()
|
|
|
|
|
if v == "":
|
|
|
|
|
raise ValueError(
|
|
|
|
|
f"Invalid databases.csv entry for instance '{instance}': "
|
|
|
|
|
"column 'database' must be '*' or a concrete database name (not empty)."
|
2025-12-28 22:12:31 +01:00
|
|
|
)
|
2025-12-29 11:39:57 +01:00
|
|
|
if v == "*":
|
|
|
|
|
return "*"
|
|
|
|
|
if v.lower() == "nan":
|
|
|
|
|
raise ValueError(
|
|
|
|
|
f"Invalid databases.csv entry for instance '{instance}': database must not be 'nan'."
|
|
|
|
|
)
|
|
|
|
|
if not DB_NAME_RE.match(v):
|
|
|
|
|
raise ValueError(
|
|
|
|
|
f"Invalid databases.csv entry for instance '{instance}': "
|
|
|
|
|
f"invalid database name '{v}'. Allowed: letters, numbers, '_' and '-'."
|
|
|
|
|
)
|
|
|
|
|
return v
|
|
|
|
|
|
|
|
|
|
def check_and_add_entry(
|
|
|
|
|
file_path: str,
|
|
|
|
|
instance: str,
|
|
|
|
|
database: Optional[str],
|
|
|
|
|
username: str,
|
|
|
|
|
password: str,
|
|
|
|
|
) -> None:
|
|
|
|
|
"""
|
|
|
|
|
Add or update an entry in databases.csv.
|
|
|
|
|
|
|
|
|
|
The function enforces strict validation:
|
|
|
|
|
- database MUST be set
|
|
|
|
|
- database MUST be '*' or a valid database name
|
|
|
|
|
"""
|
|
|
|
|
database = _validate_database_value(database, instance=instance)
|
2025-04-21 10:52:06 +02:00
|
|
|
|
2025-12-29 11:39:57 +01:00
|
|
|
if os.path.exists(file_path):
|
|
|
|
|
df = pd.read_csv(
|
|
|
|
|
file_path,
|
|
|
|
|
sep=";",
|
|
|
|
|
dtype=str,
|
|
|
|
|
keep_default_na=False,
|
|
|
|
|
)
|
2023-12-25 23:20:30 +01:00
|
|
|
else:
|
2025-12-29 11:39:57 +01:00
|
|
|
df = pd.DataFrame(
|
|
|
|
|
columns=["instance", "database", "username", "password"]
|
|
|
|
|
)
|
2023-12-25 23:29:54 +01:00
|
|
|
|
2025-12-29 11:39:57 +01:00
|
|
|
mask = (df["instance"] == instance) & (df["database"] == database)
|
2023-12-25 23:39:28 +01:00
|
|
|
|
2025-12-29 11:39:57 +01:00
|
|
|
if mask.any():
|
|
|
|
|
print("Updating existing entry.")
|
|
|
|
|
df.loc[mask, ["username", "password"]] = [username, password]
|
|
|
|
|
else:
|
|
|
|
|
print("Adding new entry.")
|
|
|
|
|
new_entry = pd.DataFrame(
|
|
|
|
|
[[instance, database, username, password]],
|
|
|
|
|
columns=["instance", "database", "username", "password"],
|
|
|
|
|
)
|
|
|
|
|
df = pd.concat([df, new_entry], ignore_index=True)
|
2023-12-25 23:29:54 +01:00
|
|
|
|
2025-12-28 22:12:31 +01:00
|
|
|
df.to_csv(file_path, sep=";", index=False)
|
|
|
|
|
|
2023-12-25 23:20:30 +01:00
|
|
|
|
2025-12-29 11:39:57 +01:00
|
|
|
def main() -> None:
|
2025-12-28 22:12:31 +01:00
|
|
|
parser = argparse.ArgumentParser(
|
2025-12-29 11:39:57 +01:00
|
|
|
description="Seed or update databases.csv for backup configuration."
|
|
|
|
|
)
|
|
|
|
|
parser.add_argument("file", help="Path to databases.csv")
|
|
|
|
|
parser.add_argument("instance", help="Instance name (e.g. bigbluebutton)")
|
|
|
|
|
parser.add_argument(
|
|
|
|
|
"database",
|
|
|
|
|
help="Database name or '*' to dump all databases",
|
2025-12-28 22:12:31 +01:00
|
|
|
)
|
2025-12-29 11:39:57 +01:00
|
|
|
parser.add_argument("username", help="Database username")
|
|
|
|
|
parser.add_argument("password", help="Database password")
|
2023-12-25 23:20:30 +01:00
|
|
|
|
|
|
|
|
args = parser.parse_args()
|
|
|
|
|
|
2025-12-29 11:39:57 +01:00
|
|
|
try:
|
|
|
|
|
check_and_add_entry(
|
|
|
|
|
file_path=args.file,
|
|
|
|
|
instance=args.instance,
|
|
|
|
|
database=args.database,
|
|
|
|
|
username=args.username,
|
|
|
|
|
password=args.password,
|
|
|
|
|
)
|
|
|
|
|
except Exception as exc:
|
|
|
|
|
print(f"ERROR: {exc}", file=sys.stderr)
|
|
|
|
|
sys.exit(1)
|
2025-12-28 22:12:31 +01:00
|
|
|
|
2023-12-25 23:20:30 +01:00
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
main()
|