chore: pre-migration snapshot
Layer0, MCP servers, Terraform consolidation
This commit is contained in:
260
scripts/monitoring_dashboard.py
Normal file
260
scripts/monitoring_dashboard.py
Normal file
@@ -0,0 +1,260 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Cloudflare Infrastructure Monitoring Dashboard
|
||||
Provides real-time monitoring of Cloudflare resources and services
|
||||
"""
|
||||
|
||||
import os
|
||||
import json
|
||||
import time
|
||||
import requests
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Dict, List, Any
|
||||
|
||||
|
||||
class CloudflareMonitor:
|
||||
def __init__(self):
|
||||
self.base_url = "https://api.cloudflare.com/client/v4"
|
||||
self.headers = {
|
||||
"Authorization": f"Bearer {os.getenv('CLOUDFLARE_API_TOKEN')}",
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
self.account_id = os.getenv("CLOUDFLARE_ACCOUNT_ID")
|
||||
|
||||
if not self.account_id or not os.getenv("CLOUDFLARE_API_TOKEN"):
|
||||
raise ValueError("Missing Cloudflare credentials in environment")
|
||||
|
||||
def make_request(self, endpoint: str) -> Dict[str, Any]:
|
||||
"""Make API request with error handling"""
|
||||
url = f"{self.base_url}{endpoint}"
|
||||
try:
|
||||
response = requests.get(url, headers=self.headers, timeout=10)
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
except requests.RequestException as e:
|
||||
return {"success": False, "errors": [str(e)]}
|
||||
|
||||
def get_account_info(self) -> Dict[str, Any]:
|
||||
"""Get account information"""
|
||||
return self.make_request(f"/accounts/{self.account_id}")
|
||||
|
||||
def get_zones(self) -> List[Dict[str, Any]]:
|
||||
"""Get all zones"""
|
||||
result = self.make_request(f"/zones?account.id={self.account_id}&per_page=50")
|
||||
return result.get("result", []) if result.get("success") else []
|
||||
|
||||
def get_zone_analytics(self, zone_id: str) -> Dict[str, Any]:
|
||||
"""Get zone analytics for the last hour"""
|
||||
since = (datetime.now() - timedelta(hours=1)).isoformat()
|
||||
return self.make_request(f"/zones/{zone_id}/analytics/dashboard?since={since}")
|
||||
|
||||
def get_waf_rules(self, zone_id: str) -> List[Dict[str, Any]]:
|
||||
"""Get WAF rules for a zone"""
|
||||
result = self.make_request(f"/zones/{zone_id}/firewall/waf/packages")
|
||||
if result.get("success"):
|
||||
packages = result.get("result", [])
|
||||
rules = []
|
||||
for package in packages:
|
||||
rules_result = self.make_request(
|
||||
f"/zones/{zone_id}/firewall/waf/packages/{package['id']}/rules"
|
||||
)
|
||||
if rules_result.get("success"):
|
||||
rules.extend(rules_result.get("result", []))
|
||||
return rules
|
||||
return []
|
||||
|
||||
def get_tunnels(self) -> List[Dict[str, Any]]:
|
||||
"""Get Cloudflare Tunnels"""
|
||||
result = self.make_request(f"/accounts/{self.account_id}/cfd_tunnel")
|
||||
return result.get("result", []) if result.get("success") else []
|
||||
|
||||
def get_dns_records(self, zone_id: str) -> List[Dict[str, Any]]:
|
||||
"""Get DNS records for a zone"""
|
||||
result = self.make_request(f"/zones/{zone_id}/dns_records?per_page=100")
|
||||
return result.get("result", []) if result.get("success") else []
|
||||
|
||||
def get_health_status(self) -> Dict[str, Any]:
|
||||
"""Get overall health status"""
|
||||
status = "healthy"
|
||||
issues = []
|
||||
|
||||
# Check zones
|
||||
zones = self.get_zones()
|
||||
if not zones:
|
||||
issues.append("No zones found")
|
||||
status = "warning"
|
||||
|
||||
# Check account access
|
||||
account_info = self.get_account_info()
|
||||
if not account_info.get("success"):
|
||||
issues.append("Account access failed")
|
||||
status = "critical"
|
||||
|
||||
return {"status": status, "issues": issues}
|
||||
|
||||
|
||||
def format_table(data: List[Dict[str, Any]], headers: List[str]) -> str:
|
||||
"""Format data as a table"""
|
||||
if not data:
|
||||
return "No data available"
|
||||
|
||||
# Calculate column widths
|
||||
col_widths = [len(header) for header in headers]
|
||||
for row in data:
|
||||
for i, header in enumerate(headers):
|
||||
value = str(row.get(header, ""))
|
||||
col_widths[i] = max(col_widths[i], len(value))
|
||||
|
||||
# Create header row
|
||||
header_row = " | ".join(
|
||||
header.ljust(col_widths[i]) for i, header in enumerate(headers)
|
||||
)
|
||||
separator = "-" * len(header_row)
|
||||
|
||||
# Create data rows
|
||||
rows = [header_row, separator]
|
||||
for row in data:
|
||||
row_data = [
|
||||
str(row.get(header, "")).ljust(col_widths[i])
|
||||
for i, header in enumerate(headers)
|
||||
]
|
||||
rows.append(" | ".join(row_data))
|
||||
|
||||
return "\n".join(rows)
|
||||
|
||||
|
||||
def main():
|
||||
print("🌐 Cloudflare Infrastructure Monitoring Dashboard")
|
||||
print("=" * 60)
|
||||
print(f"Timestamp: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
|
||||
print()
|
||||
|
||||
try:
|
||||
monitor = CloudflareMonitor()
|
||||
|
||||
# Health check
|
||||
print("🔍 Health Status")
|
||||
print("-" * 30)
|
||||
health = monitor.get_health_status()
|
||||
status_emoji = {"healthy": "✅", "warning": "⚠️", "critical": "❌"}
|
||||
print(
|
||||
f"Status: {status_emoji.get(health['status'], '❓')} {health['status'].upper()}"
|
||||
)
|
||||
if health["issues"]:
|
||||
for issue in health["issues"]:
|
||||
print(f" - {issue}")
|
||||
print()
|
||||
|
||||
# Account information
|
||||
print("🏢 Account Information")
|
||||
print("-" * 30)
|
||||
account_info = monitor.get_account_info()
|
||||
if account_info.get("success"):
|
||||
account = account_info["result"]
|
||||
print(f"Name: {account.get('name', 'N/A')}")
|
||||
print(f"Type: {account.get('type', 'N/A')}")
|
||||
print(f"Created: {account.get('created_on', 'N/A')}")
|
||||
else:
|
||||
print("Failed to retrieve account information")
|
||||
print()
|
||||
|
||||
# Zones overview
|
||||
print("🌐 Zones Overview")
|
||||
print("-" * 30)
|
||||
zones = monitor.get_zones()
|
||||
zone_data = []
|
||||
for zone in zones[:10]: # Limit to first 10 zones
|
||||
zone_data.append(
|
||||
{
|
||||
"Name": zone.get("name", "N/A"),
|
||||
"Status": zone.get("status", "N/A"),
|
||||
"Plan": zone.get("plan", {}).get("name", "N/A"),
|
||||
"Development": zone.get("development_mode", "N/A"),
|
||||
}
|
||||
)
|
||||
|
||||
print(format_table(zone_data, ["Name", "Status", "Plan", "Development"]))
|
||||
print(f"Total zones: {len(zones)}")
|
||||
print()
|
||||
|
||||
# DNS Records (for first zone)
|
||||
dns_records = []
|
||||
waf_rules = []
|
||||
|
||||
if zones:
|
||||
first_zone = zones[0]
|
||||
print("📋 DNS Records (First Zone)")
|
||||
print("-" * 30)
|
||||
dns_records = monitor.get_dns_records(first_zone["id"])
|
||||
dns_data = []
|
||||
for record in dns_records[:15]: # Limit to first 15 records
|
||||
dns_data.append(
|
||||
{
|
||||
"Type": record.get("type", "N/A"),
|
||||
"Name": record.get("name", "N/A"),
|
||||
"Content": record.get("content", "N/A")[:40] + "..."
|
||||
if len(record.get("content", "")) > 40
|
||||
else record.get("content", "N/A"),
|
||||
}
|
||||
)
|
||||
|
||||
print(format_table(dns_data, ["Type", "Name", "Content"]))
|
||||
print(f"Total DNS records: {len(dns_records)}")
|
||||
print()
|
||||
|
||||
# Tunnels
|
||||
print("🔗 Cloudflare Tunnels")
|
||||
print("-" * 30)
|
||||
tunnels = monitor.get_tunnels()
|
||||
tunnel_data = []
|
||||
for tunnel in tunnels:
|
||||
tunnel_data.append(
|
||||
{
|
||||
"Name": tunnel.get("name", "N/A"),
|
||||
"Status": tunnel.get("status", "N/A"),
|
||||
"Connections": len(tunnel.get("connections", [])),
|
||||
}
|
||||
)
|
||||
|
||||
print(format_table(tunnel_data, ["Name", "Status", "Connections"]))
|
||||
print(f"Total tunnels: {len(tunnels)}")
|
||||
print()
|
||||
|
||||
# WAF Rules (for first zone)
|
||||
if zones:
|
||||
first_zone = zones[0]
|
||||
print("🛡️ WAF Rules (First Zone)")
|
||||
print("-" * 30)
|
||||
waf_rules = monitor.get_waf_rules(first_zone["id"])
|
||||
waf_data = []
|
||||
for rule in waf_rules[:10]: # Limit to first 10 rules
|
||||
waf_data.append(
|
||||
{
|
||||
"ID": rule.get("id", "N/A"),
|
||||
"Description": rule.get("description", "N/A")[:50] + "..."
|
||||
if len(rule.get("description", "")) > 50
|
||||
else rule.get("description", "N/A"),
|
||||
"Mode": rule.get("mode", "N/A"),
|
||||
}
|
||||
)
|
||||
|
||||
print(format_table(waf_data, ["ID", "Description", "Mode"]))
|
||||
print(f"Total WAF rules: {len(waf_rules)}")
|
||||
print()
|
||||
|
||||
# Summary
|
||||
print("📊 Summary")
|
||||
print("-" * 30)
|
||||
print(f"Zones: {len(zones)}")
|
||||
print(f"Tunnels: {len(tunnels)}")
|
||||
if zones:
|
||||
print(f"DNS Records (first zone): {len(dns_records)}")
|
||||
print(f"WAF Rules (first zone): {len(waf_rules)}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Error: {e}")
|
||||
print("Please ensure your Cloudflare credentials are properly configured.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user