initial version

This commit is contained in:
2026-03-18 18:03:44 -03:00
commit 9d269e902b
136 changed files with 28175 additions and 0 deletions

View File

@@ -0,0 +1,437 @@
#!/usr/bin/env python3
"""
OCI Well-Architected Framework Validation Engine
Validates an OCI architecture against the 5 Well-Architected pillars
and generates a scorecard with auto-detected gaps and recommendations.
Usage:
python validate-architecture.py --profile workload-profile.yaml --architecture architecture.yaml
The validation is automatic — it infers check results from the composed
architecture and workload profile flags. No manual checklist required.
"""
import yaml
import sys
import os
import json
from datetime import datetime
from pathlib import Path
from typing import Any, List
# Pillar YAML files
PILLAR_FILES = [
"security-compliance.yaml",
"reliability-resilience.yaml",
"performance-cost.yaml",
"operational-efficiency.yaml",
"distributed-cloud.yaml",
]
KB_DIR = Path(__file__).parent.parent / "kb" / "well-architected"
def load_yaml(filepath: str) -> dict:
"""Load a YAML file and return its contents."""
with open(filepath, "r") as f:
return yaml.safe_load(f)
def check_applies(applies_when: str, flags: dict) -> bool:
"""Determine if a check applies based on the workload profile flags."""
if applies_when == "always":
return True
# Handle OR conditions
if " OR " in applies_when:
conditions = [c.strip() for c in applies_when.split(" OR ")]
return any(flags.get(c, False) for c in conditions)
# Handle AND conditions
if " AND " in applies_when:
conditions = [c.strip() for c in applies_when.split(" AND ")]
return all(flags.get(c, False) for c in conditions)
# Handle 'or' conditions (lowercase)
if " or " in applies_when:
conditions = [c.strip() for c in applies_when.split(" or ")]
return any(flags.get(c, False) for c in conditions)
# Single flag check
return flags.get(applies_when, False)
def evaluate_check(check: dict, architecture: dict, flags: dict) -> dict:
"""
Evaluate a single check against the architecture.
Returns a result dict with status (pass/gap/not_applicable) and details.
"""
check_id = check["id"]
applies_when = check.get("applies_when", "always")
# Check if this check applies to the workload
if not check_applies(applies_when, flags):
return {
"check_id": check_id,
"status": "not_applicable",
"name": check["name"],
}
# Look for the check_id or related keywords in the architecture
arch_str = json.dumps(architecture).lower()
auto_detect = check.get("auto_detect", {})
pass_keywords = _extract_keywords(auto_detect.get("pass_if", ""))
# Check if architecture satisfies the check
matched = any(kw in arch_str for kw in pass_keywords if kw)
if matched:
return {
"check_id": check_id,
"status": "pass",
"name": check["name"],
}
else:
return {
"check_id": check_id,
"status": "gap",
"name": check["name"],
"severity": check.get("severity", "MEDIUM"),
"finding": auto_detect.get("gap_if", check["description"]),
"recommendation": auto_detect.get("pass_if", ""),
"description": check["description"],
}
def _extract_keywords(text: str) -> List[str]:
"""Extract searchable keywords from auto_detect conditions."""
if not text:
return []
# Extract key service/feature names from the condition text
keywords = []
key_terms = [
# Identity & Access
"iam", "mfa", "instance principal", "resource principal",
"instance_principal", "resource_principal",
"identity domain", "identity_domain", "federation", "idp",
"compartment", "least-privilege", "least_privilege",
"admin_restriction", "break-glass", "break_glass",
# Resource Isolation
"tag", "tags", "defined_namespace", "cost_center", "costcenter",
"security zone", "security_zone",
# Database Security
"private subnet", "private_subnet", "private", "tde",
"vault", "vault_key", "key_management", "customer-managed",
"data safe", "data_safe", "private endpoint", "private_endpoint",
"key_rotation", "rotation", "patching", "patch",
# Network Security
"nsg", "waf", "service gateway", "service_gateway",
"network firewall", "network_firewall",
"tls", "ssl", "ztpr", "zero trust", "zero_trust",
"bastion", "ssh_restricted",
# Monitoring
"cloud guard", "cloud_guard", "audit", "flow log", "flow_log",
"vulnerability scan", "vulnerability_scanning", "siem",
"logging_analytics", "logging analytics",
# Scalability
"auto-scaling", "autoscaling", "auto_scaling",
"capacity reservation", "capacity_reservation",
# Networking
"fastconnect", "fast_connect", "vpn", "vpn_backup",
"drg", "health check", "health_check",
"fault domain", "fault_domain", "fault domains",
"availability domain", "availability_domain",
"redundant", "diverse_path",
# Backup & DR
"backup", "automated_backup", "data guard", "data_guard",
"replication", "cross_region", "cross-region",
"rpo", "rto", "dr region", "dr_region",
"disaster recovery", "disaster_recovery",
"traffic management", "traffic_management",
"dns failover", "dns_failover", "drill",
# Operations
"terraform", "resource manager", "resource_manager",
"ci/cd", "cicd", "ci_cd", "devops",
"blue/green", "blue_green", "canary", "rolling",
"monitoring", "alarm", "alarms", "custom_metric",
"apm", "database management", "database_management",
"ops insights", "ops_insights",
"os management", "os_management", "os_management_hub",
"notification", "runbook", "event", "function",
"environment_parity", "parity",
# Compute
"flex", "flex shape", "flex_shape",
"ampere", "arm", "burstable", "preemptible",
# Cost
"reserved capacity", "reserved_capacity",
"budget", "budgets", "byol", "alert_threshold",
"scheduling", "scheduled",
# Storage
"auto_tiering", "auto-tiering", "tiering",
"retention", "retention_rule",
]
text_lower = text.lower()
for term in key_terms:
if term in text_lower:
keywords.append(term)
return keywords
def validate_pillar(pillar_file: str, architecture: dict, flags: dict) -> dict:
"""Validate architecture against a single pillar."""
pillar_data = load_yaml(str(KB_DIR / pillar_file))
pillar_info = pillar_data["pillar"]
categories = pillar_data.get("categories", [])
# Check if pillar is conditional (e.g., Distributed Cloud)
if pillar_info.get("conditional", False):
applies = pillar_info.get("applies_when", "")
if not check_applies(applies, flags):
return {
"pillar_id": pillar_info["id"],
"pillar_name": pillar_info["name"],
"status": "NOT_APPLICABLE",
"reason": "Conditions not met for this pillar",
"checks_passed": 0,
"checks_total": 0,
"categories": {},
"gaps": [],
}
all_gaps = []
category_results = {}
total_checks = 0
total_passed = 0
for category in categories:
cat_id = category["id"]
cat_passed = 0
cat_total = 0
cat_gaps = []
for check in category.get("checks", []):
result = evaluate_check(check, architecture, flags)
if result["status"] == "not_applicable":
continue
cat_total += 1
if result["status"] == "pass":
cat_passed += 1
else:
gap = {
"check_id": result["check_id"],
"area": category["name"],
"finding": result.get("finding", ""),
"severity": result.get("severity", "MEDIUM"),
"recommendation": result.get("recommendation", ""),
"wa_reference": pillar_info.get("reference", ""),
}
cat_gaps.append(gap)
all_gaps.append(gap)
category_results[cat_id] = {
"passed": cat_passed,
"total": cat_total,
"gaps": cat_gaps,
}
total_checks += cat_total
total_passed += cat_passed
# Determine pillar status
high_gaps = sum(1 for g in all_gaps if g["severity"] == "HIGH")
if high_gaps > 0:
status = "GAPS_IDENTIFIED"
elif all_gaps:
status = "PASS_WITH_RECOMMENDATIONS"
else:
status = "PASS"
return {
"pillar_id": pillar_info["id"],
"pillar_name": pillar_info["name"],
"status": status,
"checks_passed": total_passed,
"checks_total": total_checks,
"categories": category_results,
"gaps": all_gaps,
}
def generate_scorecard(
architecture: dict,
profile: dict,
customer_name: str = "",
arch_name: str = "",
) -> dict:
"""Generate a complete Well-Architected scorecard."""
flags = profile.get("workload_profile", {}).get("flags", {})
pillar_results = {}
total_checks = 0
total_passed = 0
all_gaps = []
for pillar_file in PILLAR_FILES:
result = validate_pillar(pillar_file, architecture, flags)
pillar_results[result["pillar_id"]] = result
total_checks += result["checks_total"]
total_passed += result["checks_passed"]
all_gaps.extend(result.get("gaps", []))
# Overall status
high_gaps = [g for g in all_gaps if g["severity"] == "HIGH"]
medium_gaps = [g for g in all_gaps if g["severity"] == "MEDIUM"]
low_gaps = [g for g in all_gaps if g["severity"] == "LOW"]
if high_gaps:
overall_status = "GAPS_IDENTIFIED"
elif medium_gaps:
overall_status = "PASS_WITH_RECOMMENDATIONS"
else:
overall_status = "PASS"
# Build action items
action_items = {
"high_priority": [
{
"check_id": g["check_id"],
"pillar": g.get("area", ""),
"finding": g["finding"],
"recommendation": g["recommendation"],
}
for g in high_gaps
],
"medium_priority": [
{
"check_id": g["check_id"],
"pillar": g.get("area", ""),
"finding": g["finding"],
"recommendation": g["recommendation"],
}
for g in medium_gaps
],
"low_priority": [
{
"check_id": g["check_id"],
"pillar": g.get("area", ""),
"finding": g["finding"],
"recommendation": g["recommendation"],
}
for g in low_gaps
],
}
scorecard = {
"well_architected_scorecard": {
"overall_status": overall_status,
"generated_date": datetime.now().isoformat(),
"architecture_name": arch_name,
"customer": customer_name,
"summary": {
"total_checks": total_checks,
"total_passed": total_passed,
"total_gaps": len(all_gaps),
"high_severity_gaps": len(high_gaps),
"medium_severity_gaps": len(medium_gaps),
"low_severity_gaps": len(low_gaps),
},
"pillars": {
pid: {
"status": p["status"],
"checks_passed": p["checks_passed"],
"checks_total": p["checks_total"],
"categories": p["categories"],
}
for pid, p in pillar_results.items()
},
"action_items": action_items,
"references": {
"framework": "https://docs.oracle.com/en/solutions/oci-best-practices/index.html",
"security": "https://docs.oracle.com/en/solutions/oci-best-practices/effective-strategies-security-and-compliance1.html",
"reliability": "https://docs.oracle.com/en/solutions/oci-best-practices/reliable-and-resilient-cloud-topology-practices1.html",
"operations": "https://docs.oracle.com/en/solutions/oci-best-practices/best-practices-operating-cloud-deployments-efficiency.html",
"distributed": "https://docs.oracle.com/en/solutions/oci-best-practices/effective-strategies-distributed-cloud-implementation1.html",
},
}
}
return scorecard
def main():
"""CLI entry point."""
import argparse
parser = argparse.ArgumentParser(
description="Validate OCI architecture against Well-Architected Framework"
)
parser.add_argument(
"--profile",
required=True,
help="Path to workload profile YAML",
)
parser.add_argument(
"--architecture",
required=True,
help="Path to architecture YAML",
)
parser.add_argument(
"--output",
default="scorecard-output.yaml",
help="Output scorecard file path",
)
parser.add_argument(
"--format",
choices=["yaml", "json"],
default="yaml",
help="Output format",
)
args = parser.parse_args()
profile = load_yaml(args.profile)
architecture = load_yaml(args.architecture)
customer = (
profile.get("workload_profile", {})
.get("business_context", {})
.get("customer_name", "Unknown")
)
scorecard = generate_scorecard(
architecture=architecture,
profile=profile,
customer_name=customer,
arch_name=args.architecture,
)
if args.format == "json":
output = json.dumps(scorecard, indent=2)
else:
output = yaml.dump(scorecard, default_flow_style=False, sort_keys=False)
with open(args.output, "w") as f:
f.write(output)
# Print summary
sc = scorecard["well_architected_scorecard"]
print(f"\nOCI Well-Architected Scorecard")
print(f"{'=' * 50}")
print(f"Overall Status: {sc['overall_status']}")
print(f"Checks: {sc['summary']['total_passed']}/{sc['summary']['total_checks']} passed")
print(f"Gaps: {sc['summary']['total_gaps']} "
f"(HIGH: {sc['summary']['high_severity_gaps']}, "
f"MEDIUM: {sc['summary']['medium_severity_gaps']}, "
f"LOW: {sc['summary']['low_severity_gaps']})")
print(f"\nPillar Results:")
for pid, p in sc["pillars"].items():
print(f" {pid}: {p['status']} ({p['checks_passed']}/{p['checks_total']})")
print(f"\nScorecard written to: {args.output}")
if __name__ == "__main__":
main()