From 7872b6e1fc2ebacd1f2fb0551a5b608915955e59 Mon Sep 17 00:00:00 2001 From: gru Date: Sun, 8 Jun 2025 00:06:31 +0200 Subject: [PATCH] Update node_exporter_manager.py --- node_exporter_manager.py | 157 +++++++++++++++------------------------ 1 file changed, 60 insertions(+), 97 deletions(-) diff --git a/node_exporter_manager.py b/node_exporter_manager.py index 10cbd35..c551324 100644 --- a/node_exporter_manager.py +++ b/node_exporter_manager.py @@ -11,7 +11,7 @@ import pwd from pathlib import Path import bcrypt -# Stałe ścieżki i konfiguracja +# Stałe BIN_TARGET = '/usr/local/bin/node_exporter' SERVICE_FILE = '/etc/systemd/system/node_exporter.service' LOG_FILE = '/var/log/node_exporter_installer.log' @@ -20,14 +20,13 @@ USER_HOME = '/var/lib/node_exporter' CONFIG_DIR = '/etc/node_exporter' CONFIG_PATH = os.path.join(CONFIG_DIR, 'config.yml') -# Konfiguracja logowania logging.basicConfig( filename=LOG_FILE, level=logging.INFO, format='%(asctime)s [%(levelname)s] %(message)s' ) -# ----------------- FUNKCJE POMOCNICZE ----------------- +# ------------------ FUNKCJE ------------------ def run_cmd(cmd, check=True): try: @@ -37,6 +36,17 @@ def run_cmd(cmd, check=True): logging.error(f"Błąd komendy: {cmd} — {e.stderr}") raise +def detect_architecture(): + arch = run_cmd(['uname', '-m']) + if arch in ['x86_64', 'amd64']: + return 'linux-amd64' + elif arch in ['aarch64', 'arm64']: + return 'linux-arm64' + elif arch.startswith('armv7') or arch.startswith('armv6'): + return 'linux-armv7' + else: + raise Exception(f"Nieobsługiwana architektura: {arch}") + def get_latest_version(): try: r = requests.get('https://api.github.com/repos/prometheus/node_exporter/releases/latest', timeout=10) @@ -60,8 +70,6 @@ def get_local_version(): return None def download_and_extract(url, download_path='/tmp'): - import sys - filename = os.path.join(download_path, url.split('/')[-1]) extract_dirname = filename.replace('.tar.gz', '') extract_path = Path(extract_dirname) @@ -87,9 +95,6 @@ def download_and_extract(url, download_path='/tmp'): return extract_path - -# ----------------- INSTALACJA I KONFIGURACJA ----------------- - def install_binary(extracted_dir): src = Path(extracted_dir) / 'node_exporter' if Path(BIN_TARGET).exists(): @@ -110,7 +115,6 @@ def create_user(): home_path = Path(USER_HOME) home_path.mkdir(parents=True, exist_ok=True) shutil.chown(home_path, user=USER_NAME, group=USER_NAME) - logging.info(f"Utworzono katalog {USER_HOME} i przypisano właściciela.") def setup_service(): service_content = f"""[Unit] @@ -130,36 +134,33 @@ WantedBy=default.target """ with open(SERVICE_FILE, 'w') as f: f.write(service_content) - logging.info("Zapisano konfigurację usługi systemd") run_cmd(['systemctl', 'daemon-reload']) run_cmd(['systemctl', 'enable', '--now', 'node_exporter']) - logging.info("Włączono i uruchomiono usługę node_exporter") def uninstall(): if Path(SERVICE_FILE).exists(): run_cmd(['systemctl', 'disable', '--now', 'node_exporter']) os.remove(SERVICE_FILE) - logging.info("Usunięto plik usługi i zatrzymano node_exporter") if Path(BIN_TARGET).exists(): os.remove(BIN_TARGET) - logging.info("Usunięto binarkę node_exporter") try: pwd.getpwnam(USER_NAME) run_cmd(['userdel', USER_NAME]) - logging.info("Usunięto użytkownika node_exporter") except KeyError: - logging.info("Użytkownik już nie istnieje") + pass if Path(USER_HOME).exists(): shutil.rmtree(USER_HOME) - logging.info("Usunięto katalog /var/lib/node_exporter") def install(): if Path(BIN_TARGET).exists(): print("Node Exporter już zainstalowany. Użyj --update.") return + + arch = detect_architecture() version, release = get_latest_version() - url = next(asset['browser_download_url'] for asset in release['assets'] if 'linux-amd64.tar.gz' in asset['browser_download_url']) + url = next(asset['browser_download_url'] for asset in release['assets'] if arch in asset['browser_download_url']) + extracted = download_and_extract(url) install_binary(extracted) create_user() @@ -172,10 +173,13 @@ def update(): if local_version == latest_version: print(f"Node Exporter już aktualny ({local_version})") return + print(f"Aktualizacja z {local_version} do {latest_version}...") + arch = detect_architecture() run_cmd(['systemctl', 'stop', 'node_exporter']) - url = next(asset['browser_download_url'] for asset in release['assets'] if 'linux-amd64.tar.gz' in asset['browser_download_url']) + url = next(asset['browser_download_url'] for asset in release['assets'] if arch in asset['browser_download_url']) + extracted = download_and_extract(url) install_binary(extracted) run_cmd(['systemctl', 'start', 'node_exporter']) @@ -185,15 +189,9 @@ def setup(): source_path = Path(__file__).resolve() target_path = Path('/usr/local/bin/node_exporter_manager.py') - if source_path == target_path: - print("ℹ️ Skrypt już działa z docelowej lokalizacji.") - elif not target_path.exists(): + if source_path != target_path: shutil.copy(source_path, target_path) target_path.chmod(0o755) - logging.info(f"Zainstalowano skrypt jako {target_path}") - print(f"✅ Skrypt zainstalowany w {target_path}") - else: - print(f"ℹ️ Skrypt już zainstalowany w {target_path}") cron_line = f"15 3 * * * {target_path} --update >> /var/log/node_exporter_cron.log 2>&1" try: @@ -205,10 +203,6 @@ def setup(): f.write(cron_result.strip() + '\n' + cron_line + '\n') run_cmd(['crontab', '/tmp/node_exporter_cron']) os.remove('/tmp/node_exporter_cron') - logging.info("Dodano wpis do crontaba") - print("✅ Zadanie cron dodane") - else: - print("ℹ️ Wpis cron już istnieje.") logrotate_path = '/etc/logrotate.d/node_exporter_manager' logrotate_config = f"""{LOG_FILE} /var/log/node_exporter_cron.log {{ @@ -222,42 +216,8 @@ def setup(): """ with open(logrotate_path, 'w') as f: f.write(logrotate_config) - logging.info("Skonfigurowano logrotate") - print(f"✅ Logrotate dodany w {logrotate_path}") -# ----------------- GŁÓWNY BLOK ----------------- - -if __name__ == '__main__': - if os.geteuid() != 0: - print("Ten skrypt musi być uruchomiony jako root.") - sys.exit(1) - - if len(sys.argv) != 2 or sys.argv[1] not in ['--install', '--update', '--uninstall', '--setup']: - print(""" -Użycie: - node_exporter_manager.py --install # Instaluje node_exporter i uruchamia usługę - node_exporter_manager.py --update # Aktualizuje node_exporter do najnowszej wersji (jeśli potrzeba) - node_exporter_manager.py --uninstall # Usuwa node_exporter, usługę, użytkownika - node_exporter_manager.py --setup # Instaluje ten skrypt do /usr/local/bin, dodaje CRON i logrotate - node_exporter_manager.py --install-secured # Instalacja z TLS + basic auth (certyfikat + config.yml) - node_exporter_manager.py --set-password=user:haslo # Zmiana hasła w config.yml -""") - sys.exit(1) - - try: - { - '--install': install, - '--update': update, - '--uninstall': uninstall, - '--setup': setup - }[sys.argv[1]]() - except Exception as e: - logging.error(f"Błąd krytyczny: {e}") - print(f"Wystąpił błąd: {e}") - sys.exit(1) - - -# --- DODANE PONIŻEJ --- +# ------------------ ZABEZPIECZENIA ------------------ def setup_secured_config(): os.makedirs(CONFIG_DIR, exist_ok=True) @@ -324,38 +284,41 @@ def change_password(user, password): print(f"Zmieniono hasło dla użytkownika '{user}'") - if len(sys.argv) != 2 or sys.argv[1] not in ['--install', '--update', '--uninstall', '--setup']: - print(""" -Użycie: - node_exporter_manager.py --install # Instaluje node_exporter i uruchamia usługę - node_exporter_manager.py --update # Aktualizuje node_exporter do najnowszej wersji (jeśli potrzeba) - node_exporter_manager.py --uninstall # Usuwa node_exporter, usługę, użytkownika - node_exporter_manager.py --setup # Instaluje ten skrypt do /usr/local/bin, dodaje CRON i logrotate - node_exporter_manager.py --install-secured # Instalacja z TLS + basic auth (certyfikat + config.yml) - node_exporter_manager.py --set-password=user:haslo # Zmiana hasła w config.yml -""") - sys.exit(1) +# ------------------ MAIN ------------------ -try: - if sys.argv[1] == '--install': - install() - elif sys.argv[1] == '--update': - update() - elif sys.argv[1] == '--uninstall': - uninstall() - elif sys.argv[1] == '--setup': - setup() - elif sys.argv[1] == '--install-secured': - install() - setup_secured_config() - elif sys.argv[1].startswith('--set-password='): - user_pass = sys.argv[1].split('=')[1] - if ":" not in user_pass: - print("Użyj formatu --set-password=user:haslo") +if __name__ == '__main__': + if os.geteuid() != 0: + print("Ten skrypt musi być uruchomiony jako root.") + sys.exit(1) + + if len(sys.argv) != 2: + print("Użycie: node_exporter_manager.py --install | --update | --setup | --uninstall | --install-secured | --set-password=user:pass") + sys.exit(1) + + try: + arg = sys.argv[1] + if arg == '--install': + install() + elif arg == '--update': + update() + elif arg == '--uninstall': + uninstall() + elif arg == '--setup': + setup() + elif arg == '--install-secured': + install() + setup_secured_config() + elif arg.startswith('--set-password='): + user_pass = arg.split('=')[1] + if ":" not in user_pass: + print("Użyj formatu --set-password=user:haslo") + sys.exit(1) + u, p = user_pass.split(":", 1) + change_password(u, p) + else: + print("Nieznana opcja.") sys.exit(1) - u, p = user_pass.split(":", 1) - change_password(u, p) -except Exception as e: - logging.error(f"Błąd krytyczny: {e}") - print(f"Wystąpił błąd: {e}") - sys.exit(1) \ No newline at end of file + except Exception as e: + logging.error(f"Błąd krytyczny: {e}") + print(f"Wystąpił błąd: {e}") + sys.exit(1)