Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@ ansible/host_vars/*
**/vendor_files
.vscode/
*.swp
ansible/machine_audit/backend/collectedInfo
ansible/machine_audit/backend/.env
76 changes: 76 additions & 0 deletions ansible/machine_audit/backend/getNodeList.py
Original file line number Diff line number Diff line change
@@ -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("<host>") + 6
find2 = nodeConfig.find("</host>")
if find1 > 5 and find2 > -1:
ip = nodeConfig[find1:find2]
return ip
else:
return None

def getNodePort(nodeConfig):
find1 = nodeConfig.find("<port>") + 6
find2 = nodeConfig.find("</port>")
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)
121 changes: 121 additions & 0 deletions ansible/machine_audit/backend/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
#!/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):

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",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Semgrep will likely flag this as an issue, might be worth trying it without this flag.

"-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]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd recommend adding some validation akin to

if len(sys.argv) < 4: print("Usage: ..."); sys.exit(1)

Python has a habit of crashing if an argument is missing


# 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())