Skip to content

Commit

Permalink
add --size-units
Browse files Browse the repository at this point in the history
  • Loading branch information
badra001 committed Jul 17, 2026
1 parent 46cc877 commit e155231
Showing 1 changed file with 57 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,29 @@
from collections import defaultdict

# --- VERSIONING ---
__version__ = "1.0.1"
__version__ = "1.1.0"

# Strict binary byte scaling dictionary
UNIT_CONVERSION_MAP = {
"B": {"divisor": 1, "label": "Bytes"},
"MB": {"divisor": 1024**2, "label": "MB"},
"GB": {"divisor": 1024**3, "label": "GB"},
"TB": {"divisor": 1024**4, "label": "TB"},
"PB": {"divisor": 1024**5, "label": "PB"}
}

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="AWS S3 Fleet Storage Optimization Assessor Suite v1.0.1")
parser = argparse.ArgumentParser(description="AWS S3 Fleet Storage Unit-Normalized Assessor Suite v1.1.0")
parser.add_argument("--input", help="JSON file (default: latest audit_results.check_s3_buckets.*.json)")
parser.add_argument("--size-units", default="GB", choices=["B", "MB", "GB", "TB", "PB"],
help="Target size scale unit representation to output in reports (Default: GB)")
args = parser.parse_args()

# Locate and resolve input data paths
input_file = args.input or find_latest_file("audit_results.check_s3_buckets.*.json")
if not input_file:
print("Error: No input file located."); sys.exit(1)
Expand All @@ -34,6 +46,12 @@ def main():
txt_filename = f"{os.path.splitext(input_file)[0]}.txt"
csv_filename = f"s3_global_inventory_{timestamp_suffix}.csv"

# Setup the requested unit variables
target_unit = args.size_units.upper()
scale_spec = UNIT_CONVERSION_MAP[target_unit]
divisor = scale_spec["divisor"]
unit_label = scale_spec["label"]

total_buckets = 0
total_global_bytes = 0
total_global_objects = 0
Expand Down Expand Up @@ -74,44 +92,65 @@ def main():
b["versioning_status"], b["kms_key_id"], b["has_lifecycle"]
])

# Write Master Baseline Inventory CSV File
with open(csv_filename, "w", newline="", encoding="utf-8") as cf:
writer = csv.writer(cf)
writer.writerow(["Account ID", "Account Alias", "Bucket Name", "Region", "ARN", "Size Bytes", "Object Count", "Versioning Status", "KMS Key ID", "Has Lifecycle"])
writer.writerows(flat_rows)
writer.writerow(["Account ID", "Account Alias", "Bucket Name", "Region", "ARN", f"Size ({target_unit})", "Object Count", "Versioning Status", "KMS Key ID", "Has Lifecycle"])
for r in flat_rows:
scaled_csv_size = r[5] if target_unit == "B" else round(r[5] / divisor, 4)
writer.writerow([r[0], r[1], r[2], r[3], r[4], scaled_csv_size, r[6], r[7], r[8], r[9]])

# Compile Aligned TXT Dashboard Report Asset
with open(txt_filename, "w", encoding="utf-8") as tf:
tf.write("=" * 145 + "\n")
tf.write(f"AWS ORGANIZATIONAL S3 METRICS STORAGE OPTIMIZATION REPORT (v{__version__}) | Source: {input_base_name}\n")
tf.write("=" * 145 + "\n\n")
tf.write("=" * 155 + "\n")
tf.write(f"AWS ORGANIZATIONAL S3 METRICS STORAGE OPTIMIZATION REPORT | Source: {input_base_name}\n")
tf.write("=" * 155 + "\n\n")

# Section 1
tf.write("[SECTION 1: FLEET METRICS PROFILE OVERVIEW]\n")
tf.write(f" ▶ Total Corporate S3 Buckets Managed : {total_buckets}\n")
tf.write(f" ▶ Total Global Footprint Aggregation : {total_global_bytes:,} Bytes ({total_global_bytes / 1024**3:.2f} GB)\n")

scaled_global_size = total_global_bytes if target_unit == "B" else (total_global_bytes / divisor)
fmt_spec = ",d" if target_unit == "B" else ",.2f"
tf.write(f" ▶ Total Global Footprint Aggregation : {scaled_global_size:{fmt_spec}} {unit_label}\n")
tf.write(f" ▶ Total Allocated Object Count : {total_global_objects:,} Objects\n\n")

# Section 2
tf.write("[SECTION 2: ACCOUNT BILLING SECTOR SUMMARY BREAKDOWN]\n")
fmt_acct = " • Target Account: {:<45} | Buckets: {:>4} | Size (GB): {:>12.3f} | Objects: {:>14,}\n"
fmt_acct = f" • Target Account: {{:<45}} | Buckets: {{:>4}} | Size ({target_unit}): {{:>15{'.0f' if target_unit == 'B' else '.3f',}}} | Objects: {{:>14,}}\n"
for acct, metrics in sorted(account_summary.items()):
tf.write(fmt_acct.format(acct, metrics["count"], metrics["bytes"] / 1024**3, metrics["objects"]))
scaled_acct_size = metrics["bytes"] if target_unit == "B" else (metrics["bytes"] / divisor)
tf.write(fmt_acct.format(acct, metrics["count"], scaled_acct_size, metrics["objects"]))
tf.write("\n")

# Section 3
tf.write("[SECTION 3: SPACE DISTRIBUTION BY STORAGE TYPE TIER]\n")
fmt_tier = " • Storage Tier: {:<30} | Footprint (GB): {:>14.3f} | Share %: {:>8.2f}\n"
fmt_tier = f" • Storage Tier: {{:<30}} | Footprint ({target_unit}): {{:>17{'.0f' if target_unit == 'B' else '.3f',}}} | Share %: {{:>8.2f}}\n"
for s_tier, t_bytes in sorted(storage_class_summary.items(), key=lambda x: x[1], reverse=True):
share = (t_bytes / total_global_bytes * 100) if total_global_bytes > 0 else 0.0
tf.write(fmt_tier.format(s_tier, t_bytes / 1024**3, share))
scaled_tier_size = t_bytes if target_unit == "B" else (t_bytes / divisor)
tf.write(fmt_tier.format(s_tier, scaled_tier_size, share))
tf.write("\n")

# --- FIXED: Separate header string formatting mapping parameters from data integer specifiers ---
# Section 4: Granular Matrix Table
tf.write("[SECTION 4: GRANULAR BUCKET METADATA INVENTORY MATRIX]\n")
fmt_row_header = f" {{:<12}} | {{:<{max_alias_w}}} | {{:<{max_bucket_w}}} | {{:<12}} | {{:>15}} | {{:>12}} | {{:<10}} | {{:<13}}\n"
fmt_row_data = f" {{:<12}} | {{:<{max_alias_w}}} | {{:<{max_bucket_w}}} | {{:<12}} | {{:>15,}} | {{:>12,}} | {{:<10}} | {{:<13}}\n"
size_header_str = f"Size ({target_unit})"

# Build dynamic padding with right-aligned floating precision parameters
fmt_row_header = f" {{:<12}} | {{:<{{max_alias_w}}}} | {{:<{{max_bucket_w}}}} | {{:<12}} | {{:>18}} | {{:>12}} | {{:<10}} | {{:<13}}\n"
fmt_row_data = f" {{:<12}} | {{:<{{max_alias_w}}}} | {{:<{{max_bucket_w}}}} | {{:<12}} | {{:>18{',d' if target_unit == 'B' else ',.2f'}}} | {{:>12,}} | {{:<10}} | {{:<13}}\n"

# Inject column widths into format strings
f_header = fmt_row_header.format("Account ID", size_header_str, "Version", "Lifecycle", max_alias_w=max_alias_w, max_bucket_w=max_bucket_w)
tf.write(f_header.format("Account ID", "Account Alias", "Bucket Name", "Region", size_header_str, "Objects", "Version", "Lifecycle"))

tf.write(fmt_row_header.format("Account ID", "Account Alias", "Bucket Name", "Region", "Size (Bytes)", "Objects", "Version", "Lifecycle"))
tf.write(" " + "-" * (max_alias_w + max_bucket_w + 95) + "\n")
tf.write(" " + "-" * (max_alias_w + max_bucket_w + 100) + "\n")
for r in flat_rows:
tf.write(fmt_row_data.format(r[0], r[1], r[2], r[3], r[5], r[6], r[7], r[9]))
scaled_bucket_size = r[5] if target_unit == "B" else (r[5] / divisor)
f_data = fmt_row_data.format(max_alias_w=max_alias_w, max_bucket_w=max_bucket_w)
tf.write(f_data.format(r[0], r[1], r[2], r[3], scaled_bucket_size, r[6], r[7], r[9]))

print(f"[+] Output unit normalization target mapped successfully to: {target_unit}")
print(f"[+] Multi-Account Analytical Dashboard Saved: {txt_filename}")
print(f"[+] Tabular Base Inventory Shared Asset Generated: {csv_filename}")

Expand Down

0 comments on commit e155231

Please sign in to comment.