178 lines
4.8 KiB
Python
178 lines
4.8 KiB
Python
#!/usr/bin/env python3
|
|
import fcntl
|
|
import os
|
|
import shutil
|
|
import subprocess
|
|
import time
|
|
from datetime import datetime
|
|
from pathlib import Path
|
|
|
|
PYTHON = '/opt/intranet-venv/bin/python'
|
|
ROOT = Path('/opt/intranet/worker')
|
|
STATE_DIR = Path('/data/state')
|
|
PDF_OUTPUT = Path(os.getenv('PDF_OUTPUT', '/data/pdf/terminuebersicht.pdf'))
|
|
INTERVAL = int(os.getenv('CALENDAR_INTERVAL_SECONDS', '60'))
|
|
|
|
|
|
def required(name: str) -> str:
|
|
value = os.getenv(name, '').strip()
|
|
if not value:
|
|
raise RuntimeError(f'Missing environment variable: {name}')
|
|
return value
|
|
|
|
|
|
def run(cmd, cwd=None):
|
|
print('+', ' '.join(map(str, cmd)), flush=True)
|
|
subprocess.run(cmd, cwd=cwd, check=True, env=os.environ.copy())
|
|
|
|
|
|
def prepare_ssh_key() -> Path:
|
|
source = Path(os.getenv('STRATO_SSH_KEY_FILE', '/run/secrets/strato_ed25519'))
|
|
if not source.is_file():
|
|
raise RuntimeError(f'SSH key file missing: {source}')
|
|
target = Path('/tmp/strato_ed25519')
|
|
shutil.copyfile(source, target)
|
|
target.chmod(0o600)
|
|
return target
|
|
|
|
|
|
def calendar_sync():
|
|
# CKalender-XLSX verarbeiten
|
|
run(
|
|
[PYTHON, str(ROOT / 'ckal_upload_client/client.py')],
|
|
cwd=ROOT / 'ckal_upload_client'
|
|
)
|
|
|
|
# PDF erzeugen
|
|
run(
|
|
[PYTHON, str(ROOT / 'pdf_python/create_pdf.py')],
|
|
cwd=ROOT / 'pdf_python'
|
|
)
|
|
|
|
# PDF in Dashboard-PNGs umwandeln
|
|
run(
|
|
['/bin/bash', str(ROOT / 'pdf_python/pdf_to_png.sh')],
|
|
cwd=ROOT / 'pdf_python'
|
|
)
|
|
|
|
# STRATO-Synchronisation kann z. B. im DEV deaktiviert werden
|
|
strato_sync_enabled = os.getenv(
|
|
'STRATO_SYNC_ENABLED',
|
|
'true'
|
|
).strip().lower() in ('1', 'true', 'yes', 'on')
|
|
|
|
if not strato_sync_enabled:
|
|
print('STRATO Sync deaktiviert', flush=True)
|
|
print('Calendar Sync erfolgreich (ohne STRATO)', flush=True)
|
|
return
|
|
|
|
required('STRATO_SSH_USER')
|
|
required('STRATO_REMOTE_PATH')
|
|
|
|
host = os.getenv('STRATO_SSH_HOST', 'ssh.strato.de')
|
|
port = os.getenv('STRATO_SSH_PORT', '22')
|
|
|
|
known_hosts = Path(
|
|
os.getenv(
|
|
'STRATO_KNOWN_HOSTS_FILE',
|
|
'/run/secrets/known_hosts'
|
|
)
|
|
)
|
|
|
|
if not known_hosts.is_file():
|
|
raise RuntimeError(
|
|
f'known_hosts missing: {known_hosts}'
|
|
)
|
|
|
|
key = prepare_ssh_key()
|
|
|
|
if not PDF_OUTPUT.is_file() or PDF_OUTPUT.stat().st_size == 0:
|
|
raise RuntimeError(
|
|
f'PDF missing/empty: {PDF_OUTPUT}'
|
|
)
|
|
|
|
run([
|
|
'/usr/bin/scp',
|
|
'-O',
|
|
'-q',
|
|
'-P', port,
|
|
'-i', str(key),
|
|
'-o', 'IdentitiesOnly=yes',
|
|
'-o', 'BatchMode=yes',
|
|
'-o', 'StrictHostKeyChecking=yes',
|
|
'-o', f'UserKnownHostsFile={known_hosts}',
|
|
str(PDF_OUTPUT),
|
|
f"{required('STRATO_SSH_USER')}@{host}:{required('STRATO_REMOTE_PATH')}",
|
|
])
|
|
|
|
print('Calendar Sync erfolgreich', flush=True)
|
|
|
|
|
|
def brandschutz_once_per_month():
|
|
now = datetime.now()
|
|
|
|
if now.day != 1 or now.hour < 8:
|
|
return
|
|
|
|
brandschutz_enabled = os.getenv(
|
|
'BRANDSCHUTZ_MAIL_ENABLED',
|
|
'true'
|
|
).strip().lower() in ('1', 'true', 'yes', 'on')
|
|
|
|
if not brandschutz_enabled:
|
|
print('Brandschutz-Mail deaktiviert', flush=True)
|
|
return
|
|
|
|
marker = STATE_DIR / 'brandschutz-last-attempt'
|
|
month = now.strftime('%Y-%m')
|
|
|
|
if marker.exists() and marker.read_text().strip() == month:
|
|
return
|
|
|
|
# At-most-once: erst markieren, dann ausführen
|
|
marker.write_text(month + '\n')
|
|
|
|
print(f'Brandschutz-Lauf {month}', flush=True)
|
|
|
|
run([
|
|
'/usr/local/bin/php',
|
|
'/var/www/html/intranet_repo/projects/brandschutz/brandschutz_cron.php'
|
|
])
|
|
|
|
|
|
def main():
|
|
STATE_DIR.mkdir(parents=True, exist_ok=True)
|
|
lock_handle = (STATE_DIR / 'scheduler.lock').open('w')
|
|
try:
|
|
fcntl.flock(lock_handle.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB)
|
|
except BlockingIOError as exc:
|
|
raise RuntimeError('Ein anderer Scheduler läuft bereits.') from exc
|
|
|
|
if INTERVAL < 30:
|
|
raise RuntimeError('CALENDAR_INTERVAL_SECONDS muss >= 30 sein.')
|
|
for name in ('CKAL_USERNAME', 'CKAL_PASSWORD', 'CKAL_CALENDAR_URL'):
|
|
required(name)
|
|
PDF_OUTPUT.parent.mkdir(parents=True, exist_ok=True)
|
|
|
|
next_run = 0.0
|
|
while True:
|
|
try:
|
|
brandschutz_once_per_month()
|
|
except Exception as exc:
|
|
print(f'FEHLER Brandschutz: {exc}', flush=True)
|
|
|
|
now_mono = time.monotonic()
|
|
if now_mono >= next_run:
|
|
try:
|
|
calendar_sync()
|
|
except Exception as exc:
|
|
print(f'FEHLER Calendar Sync: {exc}', flush=True)
|
|
next_run = time.monotonic() + INTERVAL
|
|
|
|
(STATE_DIR / 'heartbeat').write_text(str(int(time.time())) + '\n')
|
|
time.sleep(1)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|