notebookVb
This commit is contained in:
@@ -0,0 +1,16 @@
|
||||
import psycopg2
|
||||
|
||||
conn = psycopg2.connect(host="192.168.1.76", port=5432, user="vladimir.buzalka",
|
||||
password="Vlado7309208104++", database="fotky_buzalkovi")
|
||||
conn.autocommit = True
|
||||
cur = conn.cursor()
|
||||
|
||||
cur.execute("TRUNCATE TABLE zdrojove_soubory, zaloha_obrazku RESTART IDENTITY CASCADE")
|
||||
print("Tabulky vyprazdneny.")
|
||||
|
||||
cur.execute("SELECT COUNT(*) FROM zaloha_obrazku")
|
||||
print(f"zaloha_obrazku: {cur.fetchone()[0]} radku")
|
||||
cur.execute("SELECT COUNT(*) FROM zdrojove_soubory")
|
||||
print(f"zdrojove_soubory: {cur.fetchone()[0]} radku")
|
||||
|
||||
conn.close()
|
||||
@@ -0,0 +1,284 @@
|
||||
"""
|
||||
collect_pictures.py
|
||||
-------------------
|
||||
Prochází všechny shares pod /mnt/user/ (kromě ZalohaVsechObrazku),
|
||||
najde JPG/JPEG soubory, zkopíruje je (deduplikace BLAKE3) do
|
||||
/mnt/user/ZalohaVsechObrazku/ a zapíše záznamy do PostgreSQL.
|
||||
|
||||
Tabulky:
|
||||
zaloha_obrazku – jedna fyzická záloha na unikátní BLAKE3 hash
|
||||
zdrojove_soubory – všechny nalezené zdrojové soubory (i duplikáty)
|
||||
|
||||
Bezpečné pro opakované spuštění (pokračuje tam, kde skončilo).
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import shutil
|
||||
import socket
|
||||
import logging
|
||||
from pathlib import Path
|
||||
from datetime import datetime
|
||||
|
||||
import blake3
|
||||
import psycopg2
|
||||
from psycopg2.extras import execute_values
|
||||
|
||||
# ── Konfigurace ──────────────────────────────────────────────────────────────
|
||||
|
||||
DB_CONFIG = {
|
||||
"host": "192.168.1.76",
|
||||
"port": 5432,
|
||||
"user": "vladimir.buzalka",
|
||||
"password": "Vlado7309208104++",
|
||||
"database": "fotky_buzalkovi",
|
||||
}
|
||||
|
||||
SOURCE_BASE = Path("/mnt/user")
|
||||
JPEG_EXTENSIONS = {".jpg", ".jpeg"}
|
||||
|
||||
# Cílová zálohovací složka podle hostname — vždy fyzicky na Tower1
|
||||
# Klíč = socket.gethostname(), hodnota = lokální cesta k záloze
|
||||
ZALOHA_DIR_MAP = {
|
||||
"Tower1": Path("/mnt/user/ZalohaVsechObrazku"), # Tower1: lokální share
|
||||
"tower": Path("/mnt/remotes/TOWER1.LAN_ZalohaVsechObrazku"), # tower: NFS mount na Tower1
|
||||
}
|
||||
ZALOHA_DIR_DEFAULT = Path("/mnt/remotes/TOWER1.LAN_ZalohaVsechObrazku")
|
||||
|
||||
# Adresáře které se NIKDY nepoužijí jako zdroj (porovnává se jméno složky, kdekoliv ve stromě)
|
||||
EXCLUDED_DIR_NAMES = {
|
||||
"ZalohaVsechObrazku",
|
||||
"ZalohaVšechObrázků",
|
||||
"zalohavsechobrazku", # lowercase varianta pro jistotu
|
||||
}
|
||||
|
||||
LOG_FILE = Path(__file__).parent / "collect_pictures.log"
|
||||
|
||||
# ── Logging ───────────────────────────────────────────────────────────────────
|
||||
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format="%(asctime)s %(levelname)-7s %(message)s",
|
||||
datefmt="%Y-%m-%d %H:%M:%S",
|
||||
handlers=[
|
||||
logging.StreamHandler(sys.stdout),
|
||||
logging.FileHandler(LOG_FILE, encoding="utf-8"),
|
||||
],
|
||||
)
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
# ── SQL ───────────────────────────────────────────────────────────────────────
|
||||
|
||||
SQL_CREATE_TABLES = """
|
||||
CREATE TABLE IF NOT EXISTS zaloha_obrazku (
|
||||
id SERIAL PRIMARY KEY,
|
||||
blake3_hash VARCHAR(64) UNIQUE NOT NULL,
|
||||
cesta_zalohy TEXT NOT NULL,
|
||||
nazev_souboru VARCHAR(512) NOT NULL,
|
||||
velikost BIGINT,
|
||||
datum_kopirovani TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS zdrojove_soubory (
|
||||
id SERIAL PRIMARY KEY,
|
||||
hostname VARCHAR(255) NOT NULL,
|
||||
cesta_zdroje TEXT NOT NULL,
|
||||
nazev_souboru VARCHAR(512) NOT NULL,
|
||||
velikost BIGINT,
|
||||
datum_nalezeni TIMESTAMP DEFAULT NOW(),
|
||||
blake3_hash VARCHAR(64) NOT NULL,
|
||||
zaloha_id INTEGER REFERENCES zaloha_obrazku(id),
|
||||
UNIQUE (hostname, cesta_zdroje)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_zaloha_hash ON zaloha_obrazku (blake3_hash);
|
||||
CREATE INDEX IF NOT EXISTS idx_zdroj_hash ON zdrojove_soubory (blake3_hash);
|
||||
CREATE INDEX IF NOT EXISTS idx_zdroj_zaloha ON zdrojove_soubory (zaloha_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_zdroj_host ON zdrojove_soubory (hostname);
|
||||
"""
|
||||
|
||||
SQL_HASH_EXISTS = "SELECT id, cesta_zalohy FROM zaloha_obrazku WHERE blake3_hash = %s"
|
||||
SQL_SOURCE_EXISTS = "SELECT id FROM zdrojove_soubory WHERE hostname = %s AND cesta_zdroje = %s"
|
||||
|
||||
SQL_INSERT_ZALOHA = """
|
||||
INSERT INTO zaloha_obrazku (blake3_hash, cesta_zalohy, nazev_souboru, velikost)
|
||||
VALUES (%s, %s, %s, %s)
|
||||
RETURNING id
|
||||
"""
|
||||
|
||||
SQL_INSERT_ZDROJ = """
|
||||
INSERT INTO zdrojove_soubory (hostname, cesta_zdroje, nazev_souboru, velikost, blake3_hash, zaloha_id)
|
||||
VALUES (%s, %s, %s, %s, %s, %s)
|
||||
ON CONFLICT (hostname, cesta_zdroje) DO NOTHING
|
||||
"""
|
||||
|
||||
# ── Pomocné funkce ────────────────────────────────────────────────────────────
|
||||
|
||||
def compute_blake3(path: Path, chunk: int = 1 << 20) -> str:
|
||||
h = blake3.blake3()
|
||||
with open(path, "rb") as f:
|
||||
while data := f.read(chunk):
|
||||
h.update(data)
|
||||
return h.hexdigest()
|
||||
|
||||
|
||||
def dest_path_for(source: Path, hostname: str) -> Path:
|
||||
"""
|
||||
Záloha vždy na TOWER1 pod /mnt/user/ZalohaVsechObrazku/{hostname}/...
|
||||
Příklad:
|
||||
tower /mnt/user/Foto/2023/img.jpg → /mnt/user/ZalohaVsechObrazku/tower/Foto/2023/img.jpg
|
||||
tower1 /mnt/user/Foto/2023/img.jpg → /mnt/user/ZalohaVsechObrazku/tower1/Foto/2023/img.jpg
|
||||
"""
|
||||
try:
|
||||
relative = source.relative_to(SOURCE_BASE)
|
||||
except ValueError:
|
||||
relative = Path(source.name)
|
||||
return ZALOHA_DIR / hostname / relative
|
||||
|
||||
|
||||
def copy_to_backup(source: Path, dest: Path) -> None:
|
||||
dest.parent.mkdir(parents=True, exist_ok=True)
|
||||
if dest.exists():
|
||||
# Soubor na cílovém místě už je — nepřepisujeme, záloha platí
|
||||
return
|
||||
shutil.copy2(source, dest)
|
||||
|
||||
|
||||
def is_excluded_dir(name: str) -> bool:
|
||||
"""Vrátí True pokud jméno adresáře patří do seznamu vyloučených (case-insensitive)."""
|
||||
return name.lower() in {n.lower() for n in EXCLUDED_DIR_NAMES}
|
||||
|
||||
|
||||
def iter_jpeg_files(base: Path):
|
||||
"""Generátor: vrací Path na každý JPG/JPEG v base (rekurzivně).
|
||||
Přeskočí ZALOHA_DIR a jakýkoliv adresář jehož jméno je v EXCLUDED_DIR_NAMES.
|
||||
"""
|
||||
for root, dirs, files in os.walk(base, followlinks=False):
|
||||
root_path = Path(root)
|
||||
# Neprocházet zálohovací složku (podle plné cesty)
|
||||
if ZALOHA_DIR in (root_path, *root_path.parents) or root_path == ZALOHA_DIR:
|
||||
dirs.clear()
|
||||
continue
|
||||
# Odfiltrovat vyloučené adresáře ze subadresářů (podle jména, kdekoliv ve stromě)
|
||||
dirs[:] = [
|
||||
d for d in dirs
|
||||
if root_path / d != ZALOHA_DIR and not is_excluded_dir(d)
|
||||
]
|
||||
for fname in files:
|
||||
if Path(fname).suffix.lower() in JPEG_EXTENSIONS:
|
||||
yield root_path / fname
|
||||
|
||||
# ── Hlavní logika ─────────────────────────────────────────────────────────────
|
||||
|
||||
def process(conn, hostname):
|
||||
cur = conn.cursor()
|
||||
log.info(f"Hostname zdroje: {hostname}")
|
||||
|
||||
stats = {"nalezeno": 0, "kopirovano": 0, "duplicit": 0, "chyb": 0, "preskoceno": 0}
|
||||
|
||||
for source in iter_jpeg_files(SOURCE_BASE):
|
||||
stats["nalezeno"] += 1
|
||||
src_str = str(source)
|
||||
|
||||
# Přeskočit zdroje, které už jsou v DB zpracovány (pro tento hostname)
|
||||
cur.execute(SQL_SOURCE_EXISTS, (hostname, src_str))
|
||||
if cur.fetchone():
|
||||
stats["preskoceno"] += 1
|
||||
if stats["preskoceno"] % 500 == 0:
|
||||
log.info(f"Přeskočeno (již v DB): {stats['preskoceno']}")
|
||||
continue
|
||||
|
||||
try:
|
||||
velikost = source.stat().st_size
|
||||
hash_val = compute_blake3(source)
|
||||
except (OSError, PermissionError) as e:
|
||||
log.warning(f"CHYBA čtení: {source} → {e}")
|
||||
stats["chyb"] += 1
|
||||
continue
|
||||
|
||||
# Existuje už záloha s tímto hashem?
|
||||
cur.execute(SQL_HASH_EXISTS, (hash_val,))
|
||||
row = cur.fetchone()
|
||||
|
||||
if row:
|
||||
# Duplikát — jen zapíšeme zdroj, nekopírujeme
|
||||
zaloha_id = row[0]
|
||||
cur.execute(SQL_INSERT_ZDROJ, (hostname, src_str, source.name, velikost, hash_val, zaloha_id))
|
||||
conn.commit()
|
||||
stats["duplicit"] += 1
|
||||
log.debug(f"DUPLIKÁT {source.name} (zaloha_id={zaloha_id})")
|
||||
else:
|
||||
# Nový unikátní soubor — zkopírovat a zapsat
|
||||
dest = dest_path_for(source, hostname)
|
||||
try:
|
||||
copy_to_backup(source, dest)
|
||||
except (OSError, shutil.Error) as e:
|
||||
log.warning(f"CHYBA kopírování: {source} → {e}")
|
||||
stats["chyb"] += 1
|
||||
continue
|
||||
|
||||
cur.execute(SQL_INSERT_ZALOHA, (hash_val, str(dest), source.name, velikost))
|
||||
zaloha_id = cur.fetchone()[0]
|
||||
cur.execute(SQL_INSERT_ZDROJ, (hostname, src_str, source.name, velikost, hash_val, zaloha_id))
|
||||
conn.commit()
|
||||
stats["kopirovano"] += 1
|
||||
log.info(f"ZKOPÍROVÁNO [{stats['kopirovano']:>6}] {source}")
|
||||
|
||||
if stats["nalezeno"] % 1000 == 0:
|
||||
log.info(
|
||||
f"Průběh: nalezeno={stats['nalezeno']} "
|
||||
f"nových={stats['kopirovano']} duplikátů={stats['duplicit']} "
|
||||
f"chyb={stats['chyb']} přeskočeno={stats['preskoceno']}"
|
||||
)
|
||||
|
||||
cur.close()
|
||||
return stats
|
||||
|
||||
|
||||
def main():
|
||||
global ZALOHA_DIR
|
||||
hostname = socket.gethostname()
|
||||
ZALOHA_DIR = ZALOHA_DIR_MAP.get(hostname, ZALOHA_DIR_DEFAULT)
|
||||
|
||||
log.info("=" * 60)
|
||||
log.info(f"Spuštění: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
|
||||
log.info(f"Hostname: {hostname}")
|
||||
log.info(f"Zdroj: {SOURCE_BASE}")
|
||||
log.info(f"Záloha: {ZALOHA_DIR}/{hostname}/")
|
||||
|
||||
try:
|
||||
conn = psycopg2.connect(**DB_CONFIG)
|
||||
conn.autocommit = False
|
||||
log.info("PostgreSQL: připojeno")
|
||||
except Exception as e:
|
||||
log.error(f"Nelze se připojit k DB: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
# Vytvoř tabulky pokud neexistují
|
||||
with conn.cursor() as cur:
|
||||
cur.execute(SQL_CREATE_TABLES)
|
||||
conn.commit()
|
||||
log.info("Tabulky: OK")
|
||||
|
||||
try:
|
||||
stats = process(conn, hostname)
|
||||
except KeyboardInterrupt:
|
||||
log.warning("Přerušeno uživatelem (Ctrl+C) — dosavadní záznamy jsou uloženy.")
|
||||
conn.rollback()
|
||||
except Exception as e:
|
||||
log.error(f"Neočekávaná chyba: {e}", exc_info=True)
|
||||
conn.rollback()
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
log.info("-" * 60)
|
||||
log.info(f"Nalezeno JPG/JPEG: {stats['nalezeno']}")
|
||||
log.info(f"Zkopírováno nových: {stats['kopirovano']}")
|
||||
log.info(f"Duplikátů (hash): {stats['duplicit']}")
|
||||
log.info(f"Přeskočeno (v DB): {stats['preskoceno']}")
|
||||
log.info(f"Chyb: {stats['chyb']}")
|
||||
log.info("Hotovo.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,44 @@
|
||||
import psycopg2
|
||||
|
||||
DB_CONFIG = {
|
||||
"host": "192.168.1.76",
|
||||
"port": 5432,
|
||||
"user": "vladimir.buzalka",
|
||||
"password": "Vlado7309208104++",
|
||||
"database": "fotky_buzalkovi",
|
||||
}
|
||||
|
||||
SQL = """
|
||||
CREATE TABLE IF NOT EXISTS zaloha_obrazku (
|
||||
id SERIAL PRIMARY KEY,
|
||||
blake3_hash VARCHAR(64) UNIQUE NOT NULL,
|
||||
cesta_zalohy TEXT NOT NULL,
|
||||
nazev_souboru VARCHAR(512) NOT NULL,
|
||||
velikost BIGINT,
|
||||
datum_kopirovani TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS zdrojove_soubory (
|
||||
id SERIAL PRIMARY KEY,
|
||||
hostname VARCHAR(255) NOT NULL,
|
||||
cesta_zdroje TEXT NOT NULL,
|
||||
nazev_souboru VARCHAR(512) NOT NULL,
|
||||
velikost BIGINT,
|
||||
datum_nalezeni TIMESTAMP DEFAULT NOW(),
|
||||
blake3_hash VARCHAR(64) NOT NULL,
|
||||
zaloha_id INTEGER REFERENCES zaloha_obrazku(id),
|
||||
UNIQUE (hostname, cesta_zdroje)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_zaloha_hash ON zaloha_obrazku (blake3_hash);
|
||||
CREATE INDEX IF NOT EXISTS idx_zdroj_hash ON zdrojove_soubory (blake3_hash);
|
||||
CREATE INDEX IF NOT EXISTS idx_zdroj_zaloha ON zdrojove_soubory (zaloha_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_zdroj_host ON zdrojove_soubory (hostname);
|
||||
"""
|
||||
|
||||
conn = psycopg2.connect(**DB_CONFIG)
|
||||
conn.autocommit = True
|
||||
with conn.cursor() as cur:
|
||||
cur.execute(SQL)
|
||||
conn.close()
|
||||
print("Tabulky vytvoreny.")
|
||||
@@ -0,0 +1,17 @@
|
||||
import paramiko
|
||||
|
||||
client = paramiko.SSHClient()
|
||||
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
client.connect("192.168.1.76", username="root", password="7309208104",
|
||||
look_for_keys=False, allow_agent=False)
|
||||
|
||||
def run(cmd):
|
||||
_, stdout, stderr = client.exec_command(cmd)
|
||||
out = stdout.read().decode().strip()
|
||||
if out: print(out)
|
||||
|
||||
run("mount | grep -i zaloha")
|
||||
run("ls /mnt/remotes/ 2>/dev/null | grep -i zaloha")
|
||||
run("ls /mnt/user/ 2>/dev/null | grep -i zaloha")
|
||||
|
||||
client.close()
|
||||
@@ -0,0 +1,32 @@
|
||||
import paramiko
|
||||
|
||||
client = paramiko.SSHClient()
|
||||
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
client.connect("192.168.1.50", username="root", password="Vlado7309208104++",
|
||||
look_for_keys=False, allow_agent=False)
|
||||
|
||||
def run(cmd):
|
||||
_, stdout, stderr = client.exec_command(cmd)
|
||||
out = stdout.read().decode().strip()
|
||||
err = stderr.read().decode().strip()
|
||||
if out: print(out)
|
||||
if err: print(f"[err] {err}")
|
||||
|
||||
print("--- Hostname ---")
|
||||
run("hostname")
|
||||
|
||||
print("\n--- Python ---")
|
||||
run("python3 --version")
|
||||
run("pip3 --version")
|
||||
|
||||
print("\n--- Balicky (blake3, psycopg2) ---")
|
||||
run("pip3 list 2>/dev/null | grep -iE 'blake3|psycopg2'")
|
||||
|
||||
print("\n--- ZalohaVsechObrazku ---")
|
||||
run("ls /mnt/user/ | grep -i zaloha")
|
||||
run("ls /mnt/remotes/ 2>/dev/null | grep -i zaloha")
|
||||
|
||||
print("\n--- User Scripts ---")
|
||||
run("ls /boot/config/plugins/user.scripts/scripts/ 2>/dev/null | head -5")
|
||||
|
||||
client.close()
|
||||
@@ -0,0 +1,49 @@
|
||||
import paramiko
|
||||
import stat
|
||||
|
||||
HOST = "192.168.1.76"
|
||||
USER = "root"
|
||||
PASSWORD = "7309208104"
|
||||
SCRIPT_NAME = "CollectPictures"
|
||||
REMOTE_BASE = f"/boot/config/plugins/user.scripts/scripts/{SCRIPT_NAME}"
|
||||
REMOTE_PY = f"{REMOTE_BASE}/collect_pictures.py"
|
||||
REMOTE_SCRIPT = f"{REMOTE_BASE}/script"
|
||||
LOCAL_SCRIPT = "00 PictureCollector/collect_pictures.py"
|
||||
|
||||
# -u = unbuffered stdout, každý řádek se okamžitě zobrazí v konzoli Unraid
|
||||
BASH_WRAPPER = """#!/bin/bash
|
||||
python3 -u /boot/config/plugins/user.scripts/scripts/CollectPictures/collect_pictures.py
|
||||
"""
|
||||
|
||||
client = paramiko.SSHClient()
|
||||
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
client.connect(HOST, username=USER, password=PASSWORD, look_for_keys=False, allow_agent=False)
|
||||
|
||||
def run(cmd):
|
||||
_, stdout, stderr = client.exec_command(cmd)
|
||||
out = stdout.read().decode().strip()
|
||||
err = stderr.read().decode().strip()
|
||||
if out: print(out)
|
||||
if err: print(f"[err] {err}")
|
||||
|
||||
sftp = client.open_sftp()
|
||||
|
||||
sftp.put(LOCAL_SCRIPT, REMOTE_PY)
|
||||
sftp.chmod(REMOTE_PY, stat.S_IRUSR | stat.S_IWUSR)
|
||||
print(f"Python skript: {REMOTE_PY}")
|
||||
|
||||
with sftp.open(REMOTE_SCRIPT, "w") as f:
|
||||
f.write(BASH_WRAPPER)
|
||||
sftp.chmod(REMOTE_SCRIPT, stat.S_IRWXU | stat.S_IRGRP | stat.S_IXGRP)
|
||||
print(f"Bash wrapper: {REMOTE_SCRIPT}")
|
||||
|
||||
with sftp.open(f"{REMOTE_BASE}/name", "w") as f:
|
||||
f.write("CollectPictures")
|
||||
|
||||
sftp.close()
|
||||
|
||||
print("\n--- Výsledek ---")
|
||||
run(f"cat {REMOTE_SCRIPT}")
|
||||
|
||||
client.close()
|
||||
print("Hotovo.")
|
||||
@@ -0,0 +1,49 @@
|
||||
import paramiko
|
||||
import stat
|
||||
|
||||
HOST = "192.168.1.50"
|
||||
USER = "root"
|
||||
PASSWORD = "Vlado7309208104++"
|
||||
SCRIPT_NAME = "CollectPictures"
|
||||
REMOTE_BASE = f"/boot/config/plugins/user.scripts/scripts/{SCRIPT_NAME}"
|
||||
REMOTE_PY = f"{REMOTE_BASE}/collect_pictures.py"
|
||||
REMOTE_SCRIPT = f"{REMOTE_BASE}/script"
|
||||
LOCAL_SCRIPT = "00 PictureCollector/collect_pictures.py"
|
||||
|
||||
BASH_WRAPPER = """#!/bin/bash
|
||||
python3 -u /boot/config/plugins/user.scripts/scripts/CollectPictures/collect_pictures.py
|
||||
"""
|
||||
|
||||
client = paramiko.SSHClient()
|
||||
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
client.connect(HOST, username=USER, password=PASSWORD, look_for_keys=False, allow_agent=False)
|
||||
|
||||
def run(cmd):
|
||||
_, stdout, stderr = client.exec_command(cmd)
|
||||
out = stdout.read().decode().strip()
|
||||
err = stderr.read().decode().strip()
|
||||
if out: print(out)
|
||||
if err: print(f"[err] {err}")
|
||||
|
||||
run(f"mkdir -p {REMOTE_BASE}")
|
||||
|
||||
sftp = client.open_sftp()
|
||||
sftp.put(LOCAL_SCRIPT, REMOTE_PY)
|
||||
sftp.chmod(REMOTE_PY, stat.S_IRUSR | stat.S_IWUSR)
|
||||
print(f"Python skript: {REMOTE_PY}")
|
||||
|
||||
with sftp.open(REMOTE_SCRIPT, "w") as f:
|
||||
f.write(BASH_WRAPPER)
|
||||
sftp.chmod(REMOTE_SCRIPT, stat.S_IRWXU | stat.S_IRGRP | stat.S_IXGRP)
|
||||
print(f"Bash wrapper: {REMOTE_SCRIPT}")
|
||||
|
||||
with sftp.open(f"{REMOTE_BASE}/name", "w") as f:
|
||||
f.write("CollectPictures")
|
||||
sftp.close()
|
||||
|
||||
print("\n--- Vysledek ---")
|
||||
run(f"ls -la {REMOTE_BASE}/")
|
||||
run(f"cat {REMOTE_SCRIPT}")
|
||||
|
||||
client.close()
|
||||
print("Hotovo.")
|
||||
@@ -0,0 +1,18 @@
|
||||
import paramiko
|
||||
|
||||
client = paramiko.SSHClient()
|
||||
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
client.connect("192.168.1.76", username="root", password="7309208104",
|
||||
look_for_keys=False, allow_agent=False)
|
||||
|
||||
def run(cmd):
|
||||
_, stdout, stderr = client.exec_command(cmd)
|
||||
out = stdout.read().decode().strip()
|
||||
err = stderr.read().decode().strip()
|
||||
if out: print(out)
|
||||
if err: print(f"[err] {err}")
|
||||
|
||||
run("find /boot/config/plugins/user.scripts/scripts -maxdepth 2 -type d 2>/dev/null || echo 'NENALEZENO'")
|
||||
run("ls /boot/config/plugins/user.scripts/scripts/ 2>/dev/null || echo 'prazdne nebo neexistuje'")
|
||||
|
||||
client.close()
|
||||
@@ -0,0 +1,49 @@
|
||||
import paramiko
|
||||
import sys
|
||||
|
||||
HOST = "192.168.1.76"
|
||||
USER = "root"
|
||||
PASSWORD = "7309208104"
|
||||
|
||||
client = paramiko.SSHClient()
|
||||
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
client.connect(HOST, username=USER, password=PASSWORD, look_for_keys=False, allow_agent=False)
|
||||
print(f"Připojeno na {HOST}")
|
||||
|
||||
def run(cmd, show=True):
|
||||
stdin, stdout, stderr = client.exec_command(cmd, get_pty=False)
|
||||
out = stdout.read().decode().strip()
|
||||
err = stderr.read().decode().strip()
|
||||
if show:
|
||||
if out:
|
||||
print(out)
|
||||
if err:
|
||||
print(f"[stderr] {err}", file=sys.stderr)
|
||||
return out, err
|
||||
|
||||
# 1. Ověř Python
|
||||
print("\n--- Python ---")
|
||||
run("python3 --version")
|
||||
run("pip3 --version || pip --version")
|
||||
|
||||
# 2. Co už je nainstalováno
|
||||
print("\n--- Nainstalované balíčky (blake3, psycopg2) ---")
|
||||
out, _ = run("pip3 list 2>/dev/null || pip list 2>/dev/null")
|
||||
for line in out.splitlines():
|
||||
if any(pkg in line.lower() for pkg in ("blake3", "psycopg2")):
|
||||
print(f" FOUND: {line}")
|
||||
|
||||
# 3. Instalace chybějících
|
||||
print("\n--- Instalace blake3 ---")
|
||||
run("pip3 install blake3 || pip install blake3")
|
||||
|
||||
print("\n--- Instalace psycopg2-binary ---")
|
||||
run("pip3 install psycopg2-binary || pip install psycopg2-binary")
|
||||
|
||||
# 4. Ověření
|
||||
print("\n--- Ověření importů ---")
|
||||
run("python3 -c \"import blake3; print('blake3 OK:', blake3.__version__)\"")
|
||||
run("python3 -c \"import psycopg2; print('psycopg2 OK:', psycopg2.__version__)\"")
|
||||
|
||||
client.close()
|
||||
print("\nHotovo.")
|
||||
@@ -0,0 +1,22 @@
|
||||
import paramiko
|
||||
|
||||
client = paramiko.SSHClient()
|
||||
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
client.connect("192.168.1.50", username="root", password="Vlado7309208104++",
|
||||
look_for_keys=False, allow_agent=False)
|
||||
|
||||
def run(cmd):
|
||||
_, stdout, stderr = client.exec_command(cmd)
|
||||
out = stdout.read().decode().strip()
|
||||
err = stderr.read().decode().strip()
|
||||
if out: print(out)
|
||||
if err: print(f"[err] {err}")
|
||||
|
||||
print("--- Instalace psycopg2-binary ---")
|
||||
run("pip3 install psycopg2-binary")
|
||||
|
||||
print("\n--- Overeni ---")
|
||||
run("python3 -c \"import psycopg2; print('psycopg2 OK:', psycopg2.__version__)\"")
|
||||
run("python3 -c \"import blake3; print('blake3 OK:', blake3.__version__)\"")
|
||||
|
||||
client.close()
|
||||
@@ -0,0 +1,48 @@
|
||||
import psycopg2
|
||||
|
||||
conn = psycopg2.connect(host="192.168.1.76", port=5432, user="vladimir.buzalka",
|
||||
password="Vlado7309208104++", database="fotky_buzalkovi")
|
||||
cur = conn.cursor()
|
||||
|
||||
cur.execute("""
|
||||
SELECT
|
||||
COUNT(*) AS unikatnich_souboru,
|
||||
SUM(velikost) AS celkova_velikost,
|
||||
AVG(velikost) AS prumerna_velikost,
|
||||
MIN(velikost) AS nejmensi,
|
||||
MAX(velikost) AS nejvetsi
|
||||
FROM zaloha_obrazku
|
||||
""")
|
||||
z = cur.fetchone()
|
||||
|
||||
cur.execute("SELECT COUNT(*), COUNT(DISTINCT hostname) FROM zdrojove_soubory")
|
||||
s = cur.fetchone()
|
||||
|
||||
cur.execute("SELECT hostname, COUNT(*) FROM zdrojove_soubory GROUP BY hostname ORDER BY hostname")
|
||||
hosts = cur.fetchall()
|
||||
|
||||
conn.close()
|
||||
|
||||
def fmt(b):
|
||||
if b is None: return "N/A"
|
||||
for unit in ("B", "KB", "MB", "GB", "TB"):
|
||||
if b < 1024: return f"{b:.1f} {unit}"
|
||||
b /= 1024
|
||||
return f"{b:.1f} PB"
|
||||
|
||||
print("=" * 45)
|
||||
print(" ZÁLOHA — statistiky")
|
||||
print("=" * 45)
|
||||
print(f" Unikátních souborů (záloha): {z[0]:>10,}")
|
||||
print(f" Celková velikost: {fmt(z[1]):>10}")
|
||||
print(f" Průměrná velikost: {fmt(z[2]):>10}")
|
||||
print(f" Nejmenší soubor: {fmt(z[3]):>10}")
|
||||
print(f" Největší soubor: {fmt(z[4]):>10}")
|
||||
print()
|
||||
print(f" Zdrojových záznamů celkem: {s[0]:>10,}")
|
||||
print(f" Počet zdrojových serverů: {s[1]:>10,}")
|
||||
print()
|
||||
print(" Záznamy podle hostname:")
|
||||
for host, count in hosts:
|
||||
print(f" {host:<20} {count:>8,} souborů")
|
||||
print("=" * 45)
|
||||
@@ -0,0 +1,27 @@
|
||||
import psycopg2
|
||||
|
||||
conn = psycopg2.connect(host="192.168.1.76", port=5432, user="vladimir.buzalka",
|
||||
password="Vlado7309208104++", database="fotky_buzalkovi")
|
||||
cur = conn.cursor()
|
||||
|
||||
cur.execute("SELECT table_name FROM information_schema.tables WHERE table_schema='public' ORDER BY table_name")
|
||||
print("Tabulky:")
|
||||
for r in cur.fetchall():
|
||||
print(f" {r[0]}")
|
||||
|
||||
cur.execute("SELECT indexname FROM pg_indexes WHERE schemaname='public' ORDER BY indexname")
|
||||
print("Indexy:")
|
||||
for r in cur.fetchall():
|
||||
print(f" {r[0]}")
|
||||
|
||||
cur.execute("""
|
||||
SELECT column_name, data_type, is_nullable
|
||||
FROM information_schema.columns
|
||||
WHERE table_schema='public' AND table_name='zdrojove_soubory'
|
||||
ORDER BY ordinal_position
|
||||
""")
|
||||
print("\nSloupce zdrojove_soubory:")
|
||||
for r in cur.fetchall():
|
||||
print(f" {r[0]:20s} {r[1]:20s} nullable={r[2]}")
|
||||
|
||||
conn.close()
|
||||
Reference in New Issue
Block a user