Filters
Zo zoek je
  • Gewone woorden vinden namen, teksten en voorbeelden.
  • "twee woorden" vindt precies die zin.
  • --naam of -x vindt een optie.
  • #woord zoekt alleen in commando's.
  • @grep vindt lessen en labs die grep gebruiken.
  • Filters: cat:network · in:labs · in:notes · level:rhcsa · ch:8 · root:yes. Zet ! voor een woord om het weg te laten. OR tussen woorden betekent één ervan.
  • ↑ ↓ bewegen · Enter openen · Esc sluiten · / zoeken

Hoofdstuk 10Naam in de cursus: Hands-on project

Build a daily server report

Put it all together: one script that checks servers, reads logs, calls an API, writes a JSON and CSV report, logs what it does, and runs every morning from a timer. Then publish it on GitHub with tests and CI.

  • 4 Lessen
  • 2 Commando's

Dit deel is nog niet vertaald. Je ziet de Engelse tekst.

$ als gewone gebruiker# als root (of met sudo)

10.1

Plan the report

Decide what the report must answer before you write code.

CheckSourcePython part
Disk, memory, loadLocal systemshutil, psutil
Failed servicessystemctl --failedsubprocess
Failed SSH logins/var/log/securere, Counter
Website up and certificate daysHTTPSrequests, ssl
OutputJSON for tools, CSV for peoplejson, csv

10.2

Write the main script

Small functions, one main(), logging, exit code.

server_report.py
#!/usr/bin/env python3
"""Daily server report: disk, services, SSH failures, websites."""
import json
import logging
import re
import shutil
import subprocess
import sys
from collections import Counter
from datetime import date
 
import requests
 
log = logging.getLogger("report")
 
 
def disk(path="/"):
    u = shutil.disk_usage(path)
    return round(u.used / u.total * 100, 1)
 
 
def failed_units():
    out = subprocess.run(["systemctl", "--failed", "--plain", "--no-legend"], capture_output=True, text=True).stdout
    return [line.split()[0] for line in out.splitlines() if line.strip()]
 
 
def ssh_failures(logfile="/var/log/secure", top=5):
    text = open(logfile, encoding="utf-8", errors="replace").read()
    return Counter(re.findall(r"Failed password for \S+ from (\S+)", text)).most_common(top)
 
 
def site(url):
    try:
        r = requests.get(url, timeout=10)
        return {"url": url, "status": r.status_code, "seconds": round(r.elapsed.total_seconds(), 2)}
    except requests.RequestException as e:
        return {"url": url, "status": None, "error": e.__class__.__name__}
 
 
def main():
    logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
    report = {
        "date": str(date.today()),
        "disk_root": disk("/"),
        "failed_units": failed_units(),
        "ssh_failures": ssh_failures(),
        "sites": [site(u) for u in ["https://s0sta.com/", "https://babolatksa.com/"]],
    }
    path = f"/var/lib/report/report-{report['date']}.json"
    with open(path, "w", encoding="utf-8") as f:
        json.dump(report, f, indent=2)
    log.info("report written to %s", path)
    bad = report["failed_units"] or report["disk_root"] > 90 or any(s["status"] != 200 for s in report["sites"])
    return 1 if bad else 0
 
 
if __name__ == "__main__":
    sys.exit(main())

10.3

Run it every morning

A timer at 07:00 on work days; the journal keeps the output.

mkdir -p /var/lib/report
install -m 755 server_report.py /usr/local/bin/server-report
systemctl edit --force --full server-report.service
systemctl edit --force --full server-report.timer
systemctl enable --now server-report.timer
systemctl list-timers server-report.timer

10.4

Publish it

The project is portfolio material when others can read, test and run it.

  • [ ] Repository with README (what, why, how to run), requirements.txt, tests.
  • [ ] GitHub Actions: ruff, pytest on every push.
  • [ ] No secrets in the code; tokens from environment variables.
  • [ ] A LinkedIn post with one screenshot of the report.

Alle commando's in dit hoofdstuk