Skip to content

Commit

Permalink
widen report
Browse files Browse the repository at this point in the history
  • Loading branch information
badra001 committed Mar 10, 2026
1 parent dd46f17 commit a5d1de8
Showing 1 changed file with 92 additions and 36 deletions.
128 changes: 92 additions & 36 deletions local-app/python-tools/cross-organization/assess_check_scheduling.py
Original file line number Diff line number Diff line change
@@ -1,34 +1,41 @@
#!/usr/bin/env python
#!/bin/env python3

import json, argparse, sys, os, glob
from collections import Counter, defaultdict

# --- VERSIONING ---
__version__ = "1.3.0"
__version__ = "1.4.0"

def find_latest_file(pattern):
files = glob.glob(pattern)
return max(files, key=os.path.getctime) if files else None

def main():
parser = argparse.ArgumentParser(description="PowerSchedule Assessor - v1.3.0")
parser = argparse.ArgumentParser(description="PowerSchedule Assessor - v1.4.0")
parser.add_argument("--input", help="JSON audit file")
args = parser.parse_args()

input_file = args.input or find_latest_file("audit_results.check_scheduling.*.json")
if not input_file: print("Error: No file found."); sys.exit(1)
if not input_file:
print("Error: No scheduling audit file found."); sys.exit(1)

with open(input_file, 'r') as f: data = json.load(f)
with open(input_file, 'r') as f:
data = json.load(f)

# Core Tracking
env_matrix = defaultdict(Counter)
env_totals = Counter()
type_matrix = defaultdict(Counter)
type_totals = Counter()

# Specific group counters
ec2_groups = Counter({"plain": 0, "asg": 0, "eks": 0})
# Report 3 Matrix: resource_category -> tag_value -> env -> count
# Categories: "plain_ec2", "asg_ec2", "eks_ec2", "rds"
r3_data = defaultdict(lambda: defaultdict(lambda: defaultdict(int)))
category_env_totals = defaultdict(Counter)

asg_names = set()
eks_clusters = set()
total_resources = 0
all_envs = set()

for account in data:
acc_id = account.get('account_id')
Expand All @@ -39,43 +46,92 @@ def main():
tags = val.get("tags", {})
res_type = val.get("type", "unknown")

# EKS Cluster Tracking
# Sub-type categorizations
if res_type == "eks_node": cat = "eks_ec2"
elif res_type == "asg_member": cat = "asg_ec2"
elif res_type == "rds": cat = "rds"
else: cat = "plain_ec2"

# EKS/ASG Metadata
if val.get("eks_cluster") and val.get("eks_cluster") != "N/A":
eks_clusters.add(f"{acc_id}:{val.get('eks_cluster')}")

# EC2 Grouping Logic
if res_type in ["plain", "asg_member", "eks_node"]:
if res_type == "eks_node": ec2_groups["eks"] += 1
elif res_type == "asg_member": ec2_groups["asg"] += 1
else: ec2_groups["plain"] += 1
display_type = "ec2"
else:
display_type = res_type

# Metrics for reports
if val.get("asg_name") and val.get("asg_name") != "N/A":
asg_names.add(f"{acc_id}:{val.get('asg_name')}")

# Normalization
env = tags.get('Environment') or tags.get('environment') or "Undefined"
all_envs.add(env)
schedule = tags.get('PowerSchedule', "No Schedule")
sched_lower = schedule.lower().strip()

# Schedule Enabled Logic
is_enabled = sched_lower not in ["always_on", "no schedule"]
enabled_key = "Scheduled: True" if is_enabled else "Scheduled: False"

# Populate Matrices
env_matrix[env][schedule] += 1
env_totals[env] += 1
type_matrix[display_type][schedule] += 1
type_totals[display_type] += 1
type_totals[cat] += 1

r3_data[cat][schedule][env] += 1
r3_data[cat][enabled_key][env] += 1
category_env_totals[cat][env] += 1

sorted_envs = sorted(list(all_envs))
report_width = 130

# ... [Report 1 output logic preserved from v1.2.1] ...
# --- REPORT 1: ENVIRONMENT SUMMARY ---
print(f"\nREPORT 1: BREAKDOWN BY ENVIRONMENT")
print("-" * report_width)
for env in sorted_envs:
print(f"\nEnvironment: {env} (Total: {env_totals[env]})")
for sched, count in sorted(env_matrix[env].items()):
pct = (count / env_totals[env]) * 100
print(f" {sched:<30} | {count:<5} | {pct:>5.1f}%")

# --- REPORT 2: RESOURCE TYPE SUMMARY ---
print(f"\n\nREPORT 2: BREAKDOWN BY RESOURCE TYPE")
print("=" * 40)
for r_type in sorted(type_matrix.keys()):
print(f"\nResource Type: {r_type.upper()} (Total: {type_totals[r_type]})")
if r_type == "ec2":
print(f" -> Plain: {ec2_groups['plain']} | ASG: {ec2_groups['asg']} | EKS: {ec2_groups['eks']}")
# ... [Matrix output logic preserved from v1.2.1] ...

print("\n" + "=" * 120)
print("-" * report_width)
for cat in sorted(type_totals.keys()):
print(f"Resource Group: {cat.upper():<10} | Total: {type_totals[cat]}")

# --- REPORT 3: CROSS-ENVIRONMENT MATRIX ---
print(f"\n\nREPORT 3: SCHEDULING MATRIX BY CATEGORY")
for cat in ["plain_ec2", "asg_ec2", "eks_ec2", "rds"]:
cat_total = type_totals[cat]
if cat_total == 0: continue

print(f"\n{cat.upper()} SCHEDULING DETAIL")
header = f"{'PowerSchedule Tag':<25} | {'Org Total':<12}"
for env in sorted_envs: header += f" | {env[:10]:<12}"
print("-" * len(header))
print(header)
print("-" * len(header))

# Sort tags so 'Scheduled: True/False' are at the bottom
all_tags = sorted([t for t in r3_data[cat].keys() if not t.startswith("Scheduled:")])
all_tags += ["Scheduled: True", "Scheduled: False"]

for tag in all_tags:
row_total = sum(r3_data[cat][tag].values())
row_pct = (row_total / cat_total) * 100
line = f"{tag[:25]:<25} | {row_total:<4} ({row_pct:>3.0f}%)"

for env in sorted_envs:
count = r3_data[cat][tag][env]
env_total = category_env_totals[cat][env]
env_pct = (count / env_total * 100) if env_total > 0 else 0
line += f" | {count:<3} ({env_pct:>3.0f}%)"
print(line)

# --- ORG SUMMARY ---
print("\n" + "=" * report_width)
print(f"ORGANIZATION SUMMARY")
print(f" Total Resources: {total_resources}")
print(f" Total ASGs Identified: {len(asg_names)}")
print(f" Total EKS Clusters: {len(eks_clusters)}") # NEW
print(f" Total EKS Nodes: {ec2_groups['eks']}") # NEW
print("=" * 120)
print(f" Accounts Checked: {len(data)}")
print(f" Total Resources: {total_resources}")
print(f" Total ASGs: {len(asg_names)}")
print(f" Total EKS Clusters: {len(eks_clusters)}")
print(f" Total EKS Nodes: {type_totals['eks_ec2']}")
print("=" * report_width)

if __name__ == "__main__": main()

0 comments on commit a5d1de8

Please sign in to comment.