From 665b50c44a9a2150a411657dc67ca34dfddac375 Mon Sep 17 00:00:00 2001 From: Haroon Khel Date: Thu, 21 May 2026 17:19:58 +0100 Subject: [PATCH 1/2] tools: Machine audit - backend scripts to run on Nagios server --- .gitignore | 2 + ansible/machine_audit/backend/getNodeList.py | 76 +++++++++++ ansible/machine_audit/backend/main.py | 132 +++++++++++++++++++ 3 files changed, 210 insertions(+) create mode 100755 ansible/machine_audit/backend/getNodeList.py create mode 100755 ansible/machine_audit/backend/main.py diff --git a/.gitignore b/.gitignore index 934c2b9842..828d595a94 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,5 @@ ansible/host_vars/* **/vendor_files .vscode/ *.swp +ansible/machine_audit/backend/collectedInfo +ansible/machine_audit/backend/.env diff --git a/ansible/machine_audit/backend/getNodeList.py b/ansible/machine_audit/backend/getNodeList.py new file mode 100755 index 0000000000..312737253f --- /dev/null +++ b/ansible/machine_audit/backend/getNodeList.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python3 + +# Some portions generated by IBM Bob + +import sys +import jenkins +from pathlib import Path + +def getIP(nodeConfig): + find1 = nodeConfig.find("") + 6 + find2 = nodeConfig.find("") + if find1 > 5 and find2 > -1: + ip = nodeConfig[find1:find2] + return ip + else: + return None + +def getNodePort(nodeConfig): + find1 = nodeConfig.find("") + 6 + find2 = nodeConfig.find("") + if find1 > 5 and find2 > -1: + port = nodeConfig[find1:find2] + return port + else: + return None + +def createServer(username, password, url): + try: + server = jenkins.Jenkins(url=url, username=username, + password=password) + return server + + except Exception as e: + print("Error: Could not connect to Jenkins server") + return sys.exit(1) + +def getNodeList(url, username, password): + + try: + # Create collectedInfo directory if it doesn't exist + script_dir = Path(__file__).parent + collected_info_dir = script_dir / "collectedInfo" + collected_info_dir.mkdir(exist_ok=True) + + # Define machine.list file path + machine_list_path = collected_info_dir / "machine.list" + + # Remove existing file if it exists + if machine_list_path.exists(): + machine_list_path.unlink() + + # Connect to Jenkins + server = createServer(username, password, url) + nodes = server.get_nodes() + + # Create new machine.list file and write node information + with open(machine_list_path, 'w') as f: + for node in nodes: + if "build" in node["name"] or "test" in node["name"] or "dockerhost" in node["name"]: + config = server.get_node_config(node["name"]) + ip = getIP(config) + port = getNodePort(config) + f.write(f"{node['name']} {ip} {port}\n") + + print(f"Machine list saved to: {machine_list_path}") + + except jenkins.JenkinsException as e: + print(f"Jenkins error: {e}") + return None + except Exception as e: + print(f"Error: {e}") + return None + +if __name__ == "__main__": + url, username, password = sys.argv[1:4] + getNodeList(url, username, password) diff --git a/ansible/machine_audit/backend/main.py b/ansible/machine_audit/backend/main.py new file mode 100755 index 0000000000..1ad84652e3 --- /dev/null +++ b/ansible/machine_audit/backend/main.py @@ -0,0 +1,132 @@ +#!/usr/bin/env python3 + +# Some portions generated by IBM Bob + +import sys +import subprocess +from pathlib import Path +from getNodeList import getNodeList + +# ANSI color codes +GREEN = '\033[92m' +RED = '\033[91m' +CYAN = '\033[96m' +RESET = '\033[0m' + +UNIX_LOG_PATH="/var/log/machine_info.json" +WINDOWS_LOG_PATH="c:\\machine_info.json" + +def scp_machine_info(node_name, ip, port, collected_info_dir, log_path): + """ + SCP the machine_info.json file from a remote machine. + + Args: + node_name: Name of the node + ip: IP address of the machine + port: SSH port number + collected_info_dir: Directory to store collected files + + Returns: + bool: True if successful, False otherwise + """ + if ip is None or ip == "None" or port is None or port == "None": + print(f" {CYAN}[SKIP]{RESET} {node_name}: IP or port is None") + return False + + local_filename = f"{node_name}_machine_info.json" + local_filepath = collected_info_dir / local_filename + + # Use nagios user to SCP the file + remote_location = f"nagios@{ip}:{log_path}" + scp_command = [ + "scp", + "-P", port, + "-o", "StrictHostKeyChecking=no", + "-o", "ConnectTimeout=5", + "-o", "NumberOfPasswordPrompts=0", + remote_location, + str(local_filepath) + ] + + print(f" [INFO] Copying from {node_name} ({ip}:{port})...") + + try: + result = subprocess.run( + scp_command, + capture_output=True, + text=True, + timeout=30 + ) + + if result.returncode == 0: + print(f" {GREEN}[SUCCESS]{RESET} File saved to {local_filename}") + return True + else: + print(f" {RED}[ERROR]{RESET} SCP failed for {node_name}: {result.stderr.strip()}") + return False + + except subprocess.TimeoutExpired: + print(f" {RED}[ERROR]{RESET} SCP timeout for {node_name}") + return False + except Exception as e: + print(f" {RED}[ERROR]{RESET} Exception for {node_name}: {e}") + return False + + +def main(): + + # Jenkins credentials from command line + url, username, password = sys.argv[1:4] + + # Generate machine list from Jenkins + print("\nFetching node list from Jenkins...") + getNodeList(url, username, password) + + script_dir = Path(__file__).parent + collected_info_dir = script_dir / "collectedInfo" + machine_list_path = collected_info_dir / "machine.list" + + if not machine_list_path.exists(): + print(f"Error: machine.list not found at {machine_list_path}") + return 1 + + # Remove any existing *_machine_info.json files from collectedInfo directory + print("\nCleaning up old machine_info.json files...") + for old_file in collected_info_dir.glob("*_machine_info.json"): + old_file.unlink() + print(f" Removed: {old_file.name}") + + # Read machine list and collect files + print("\nCollecting machine_info.json from each machine:") + successful = 0 + failed = 0 + + with open(machine_list_path, 'r') as f: + for line in f: + line = line.strip() + if not line: + continue + + parts = line.split() + if len(parts) >= 3: + node_name = parts[0] + ip = parts[1] + port = parts[2] + + # SCP only from non windows machines for now + if scp_machine_info(node_name, ip, port, collected_info_dir, UNIX_LOG_PATH): + successful += 1 + else: + failed += 1 + + # Print summary + print(f"\n{'='*60}") + print(f"Collection Summary:") + print(f" Successful: {successful}") + print(f" Failed: {failed}") + print(f"{'='*60}") + + return 0 + +if __name__ == "__main__": + sys.exit(main()) From 07dfcf210b988ad9b1dc14cb8dc000a7d0d044b5 Mon Sep 17 00:00:00 2001 From: Haroon Khel Date: Thu, 21 May 2026 17:30:44 +0100 Subject: [PATCH 2/2] Remove unnecessary comment --- ansible/machine_audit/backend/main.py | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/ansible/machine_audit/backend/main.py b/ansible/machine_audit/backend/main.py index 1ad84652e3..31f17acbfe 100755 --- a/ansible/machine_audit/backend/main.py +++ b/ansible/machine_audit/backend/main.py @@ -17,18 +17,7 @@ WINDOWS_LOG_PATH="c:\\machine_info.json" def scp_machine_info(node_name, ip, port, collected_info_dir, log_path): - """ - SCP the machine_info.json file from a remote machine. - - Args: - node_name: Name of the node - ip: IP address of the machine - port: SSH port number - collected_info_dir: Directory to store collected files - - Returns: - bool: True if successful, False otherwise - """ + if ip is None or ip == "None" or port is None or port == "None": print(f" {CYAN}[SKIP]{RESET} {node_name}: IP or port is None") return False