Files
Sankofa/scripts/proxmox-review-and-plan.py
defiQUG 9daf1fd378 Apply Composer changes: comprehensive API updates, migrations, middleware, and infrastructure improvements
- Add comprehensive database migrations (001-024) for schema evolution
- Enhance API schema with expanded type definitions and resolvers
- Add new middleware: audit logging, rate limiting, MFA enforcement, security, tenant auth
- Implement new services: AI optimization, billing, blockchain, compliance, marketplace
- Add adapter layer for cloud integrations (Cloudflare, Kubernetes, Proxmox, storage)
- Update Crossplane provider with enhanced VM management capabilities
- Add comprehensive test suite for API endpoints and services
- Update frontend components with improved GraphQL subscriptions and real-time updates
- Enhance security configurations and headers (CSP, CORS, etc.)
- Update documentation and configuration files
- Add new CI/CD workflows and validation scripts
- Implement design system improvements and UI enhancements
2025-12-12 18:01:35 -08:00

427 lines
16 KiB
Python
Executable File

#!/usr/bin/env python3
"""
Proxmox Review and Deployment Planning Script
Connects to both Proxmox instances, reviews configurations, checks status,
and generates a deployment plan with detailed task list.
"""
import os
import sys
import json
import requests
from datetime import datetime
from pathlib import Path
from typing import Dict, List, Optional, Tuple
from urllib.parse import urlparse
# Try to import proxmoxer, but fall back to direct API calls if not available
try:
from proxmoxer import ProxmoxAPI
PROXMOXER_AVAILABLE = True
except ImportError:
PROXMOXER_AVAILABLE = False
print("Warning: proxmoxer not installed. Using direct API calls.")
print("Install with: pip install proxmoxer")
# Colors for terminal output
class Colors:
RED = '\033[0;31m'
GREEN = '\033[0;32m'
YELLOW = '\033[1;33m'
BLUE = '\033[0;34m'
CYAN = '\033[0;36m'
NC = '\033[0m' # No Color
def log(message: str, color: str = Colors.BLUE):
"""Print a log message with timestamp."""
timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
print(f"{color}[{timestamp}]{Colors.NC} {message}")
def log_success(message: str):
"""Print a success message."""
log(f"{message}", Colors.GREEN)
def log_warning(message: str):
"""Print a warning message."""
log(f"⚠️ {message}", Colors.YELLOW)
def log_error(message: str):
"""Print an error message."""
log(f"{message}", Colors.RED)
class ProxmoxClient:
"""Proxmox API client."""
def __init__(self, api_url: str, username: str = None, password: str = None,
token: str = None, verify_ssl: bool = True):
self.api_url = api_url.rstrip('/')
self.username = username
self.password = password
self.token = token
self.verify_ssl = verify_ssl
self.session = requests.Session()
self.ticket = None
self.csrf_token = None
if not verify_ssl:
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
def authenticate(self) -> bool:
"""Authenticate to Proxmox API."""
if self.token:
# Token authentication
self.session.headers['Authorization'] = f'PVEAuthCookie={self.token}'
return True
if not self.username or not self.password:
log_error("Username/password or token required")
return False
# Password authentication
auth_url = f"{self.api_url}/api2/json/access/ticket"
try:
response = self.session.post(
auth_url,
data={'username': self.username, 'password': self.password},
verify=self.verify_ssl,
timeout=10
)
response.raise_for_status()
data = response.json()['data']
self.ticket = data['ticket']
self.csrf_token = data.get('CSRFPreventionToken', '')
self.session.headers['Cookie'] = f'PVEAuthCookie={self.ticket}'
if self.csrf_token:
self.session.headers['CSRFPreventionToken'] = self.csrf_token
return True
except Exception as e:
log_error(f"Authentication failed: {e}")
return False
def api_call(self, endpoint: str, method: str = 'GET', data: dict = None) -> Optional[dict]:
"""Make an API call to Proxmox."""
url = f"{self.api_url}/api2/json{endpoint}"
try:
if method == 'GET':
response = self.session.get(url, verify=self.verify_ssl, timeout=10)
elif method == 'POST':
response = self.session.post(url, json=data, verify=self.verify_ssl, timeout=10)
elif method == 'PUT':
response = self.session.put(url, json=data, verify=self.verify_ssl, timeout=10)
elif method == 'DELETE':
response = self.session.delete(url, verify=self.verify_ssl, timeout=10)
else:
log_error(f"Unsupported HTTP method: {method}")
return None
response.raise_for_status()
return response.json().get('data')
except Exception as e:
log_error(f"API call failed ({endpoint}): {e}")
return None
def get_version(self) -> Optional[dict]:
"""Get Proxmox version information."""
return self.api_call('/version')
def get_cluster_status(self) -> Optional[List[dict]]:
"""Get cluster status."""
return self.api_call('/cluster/status')
def get_nodes(self) -> Optional[List[dict]]:
"""Get list of nodes."""
return self.api_call('/nodes')
def get_node_status(self, node: str) -> Optional[dict]:
"""Get status of a specific node."""
return self.api_call(f'/nodes/{node}/status')
def get_storage(self) -> Optional[List[dict]]:
"""Get storage information."""
return self.api_call('/storage')
def get_vms(self, node: str) -> Optional[List[dict]]:
"""Get VMs on a node."""
return self.api_call(f'/nodes/{node}/qemu')
def get_networks(self, node: str) -> Optional[List[dict]]:
"""Get network configuration for a node."""
return self.api_call(f'/nodes/{node}/network')
def load_environment():
"""Load environment variables."""
env_file = Path(__file__).parent.parent / '.env'
env_vars = {}
if env_file.exists():
with open(env_file) as f:
for line in f:
line = line.strip()
if line and not line.startswith('#') and '=' in line:
key, value = line.split('=', 1)
env_vars[key.strip()] = value.strip()
# Set defaults
config = {
'proxmox_1': {
'api_url': env_vars.get('PROXMOX_1_API_URL', 'https://192.168.11.10:8006'),
'user': env_vars.get('PROXMOX_1_USER', 'root'),
'password': env_vars.get('PROXMOX_1_PASS', ''),
'token': env_vars.get('PROXMOX_1_API_TOKEN', ''),
'verify_ssl': env_vars.get('PROXMOX_1_INSECURE_SKIP_TLS_VERIFY', 'false').lower() != 'true'
},
'proxmox_2': {
'api_url': env_vars.get('PROXMOX_2_API_URL', 'https://192.168.11.11:8006'),
'user': env_vars.get('PROXMOX_2_USER', 'root'),
'password': env_vars.get('PROXMOX_2_PASS', ''),
'token': env_vars.get('PROXMOX_2_API_TOKEN', ''),
'verify_ssl': env_vars.get('PROXMOX_2_INSECURE_SKIP_TLS_VERIFY', 'false').lower() != 'true'
}
}
return config
def connect_and_review(client: ProxmoxClient, instance_num: int, output_dir: Path) -> Optional[dict]:
"""Connect to Proxmox and gather information."""
log(f"Connecting to Proxmox Instance {instance_num}...")
if not client.authenticate():
log_error(f"Failed to authenticate to Instance {instance_num}")
return None
log_success(f"Authenticated to Instance {instance_num}")
# Gather information
info = {
'instance': instance_num,
'timestamp': datetime.now().isoformat(),
'version': client.get_version(),
'cluster_status': client.get_cluster_status(),
'nodes': client.get_nodes(),
'storage': client.get_storage()
}
# Get detailed node information
if info['nodes']:
log(f" Found {len(info['nodes'])} nodes")
for node_data in info['nodes']:
node = node_data.get('node', 'unknown')
log(f" - {node}")
# Get node status
node_status = client.get_node_status(node)
if node_status:
info[f'node_{node}_status'] = node_status
# Get VMs
vms = client.get_vms(node)
if vms:
info[f'node_{node}_vms'] = vms
log(f" VMs: {len(vms)}")
# Get networks
networks = client.get_networks(node)
if networks:
info[f'node_{node}_networks'] = networks
# Save to file
output_file = output_dir / f"proxmox-{instance_num}-status-{datetime.now().strftime('%Y%m%d_%H%M%S')}.json"
with open(output_file, 'w') as f:
json.dump(info, f, indent=2)
log_success(f"Status saved to {output_file}")
# Display summary
if info.get('version'):
version = info['version'].get('version', 'unknown')
log(f" Version: {version}")
return info
def review_configurations(project_root: Path, output_dir: Path) -> str:
"""Review and document configurations."""
log("Reviewing configurations...")
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
config_file = output_dir / f"configuration-review-{timestamp}.md"
content = []
content.append("# Proxmox Configuration Review\n")
content.append(f"Generated: {datetime.now().isoformat()}\n")
# Environment configuration
content.append("## Environment Configuration\n")
content.append("### Proxmox Instance 1\n")
content.append("- API URL: From .env (PROXMOX_1_API_URL)\n")
content.append("- User: From .env (PROXMOX_1_USER)\n")
content.append("\n### Proxmox Instance 2\n")
content.append("- API URL: From .env (PROXMOX_2_API_URL)\n")
content.append("- User: From .env (PROXMOX_2_USER)\n")
content.append("\n")
# Provider config
provider_config = project_root / "crossplane-provider-proxmox" / "examples" / "provider-config.yaml"
if provider_config.exists():
content.append("## Crossplane Provider Configuration\n")
content.append("```yaml\n")
with open(provider_config) as f:
content.append(f.read())
content.append("```\n\n")
# Cloudflare tunnel configs
tunnel_configs_dir = project_root / "cloudflare" / "tunnel-configs"
if tunnel_configs_dir.exists():
content.append("## Cloudflare Tunnel Configurations\n")
for config_file in sorted(tunnel_configs_dir.glob("proxmox-site-*.yaml")):
content.append(f"### {config_file.name}\n")
content.append("```yaml\n")
with open(config_file) as f:
content.append(f.read())
content.append("```\n\n")
with open(config_file, 'w') as f:
f.write(''.join(content))
log_success(f"Configuration review saved to {config_file}")
return str(config_file)
def generate_deployment_plan(output_dir: Path, config: dict) -> str:
"""Generate deployment plan."""
log("Generating deployment plan...")
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
plan_file = output_dir / f"deployment-plan-{timestamp}.md"
content = []
content.append("# Proxmox Deployment Plan\n")
content.append(f"Generated: {datetime.now().isoformat()}\n")
content.append("## Current Status\n")
content.append(f"- **Instance 1**: {config['proxmox_1']['api_url']}\n")
content.append(f"- **Instance 2**: {config['proxmox_2']['api_url']}\n")
content.append("\n## Deployment Phases\n")
content.append("### Phase 1: Connection and Validation\n")
content.append("1. Verify connectivity to both instances\n")
content.append("2. Review cluster status and node health\n")
content.append("3. Review storage and network configuration\n")
content.append("\n### Phase 2: Configuration Alignment\n")
content.append("1. Map instances to sites\n")
content.append("2. Set up authentication (API tokens)\n")
content.append("3. Configure Cloudflare tunnels\n")
content.append("\n### Phase 3: Crossplane Provider Deployment\n")
content.append("1. Complete API client implementation\n")
content.append("2. Build and deploy provider\n")
content.append("3. Configure ProviderConfig\n")
content.append("\n### Phase 4: Infrastructure Deployment\n")
content.append("1. Deploy test VMs\n")
content.append("2. Set up monitoring\n")
content.append("3. Configure backups\n")
content.append("\n### Phase 5: Production Readiness\n")
content.append("1. Security hardening\n")
content.append("2. Documentation\n")
content.append("3. Testing and validation\n")
with open(plan_file, 'w') as f:
f.write(''.join(content))
log_success(f"Deployment plan saved to {plan_file}")
return str(plan_file)
def generate_task_list(output_dir: Path, config: dict) -> str:
"""Generate detailed task list."""
log("Generating task list...")
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
task_file = output_dir / f"task-list-{timestamp}.md"
content = []
content.append("# Proxmox Deployment Task List\n")
content.append(f"Generated: {datetime.now().isoformat()}\n")
content.append("## Immediate Tasks (Priority: High)\n")
content.append("### Connection and Authentication\n")
content.append("- [ ] **TASK-001**: Verify connectivity to Instance 1\n")
content.append(f" - URL: {config['proxmox_1']['api_url']}\n")
content.append("- [ ] **TASK-002**: Verify connectivity to Instance 2\n")
content.append(f" - URL: {config['proxmox_2']['api_url']}\n")
content.append("- [ ] **TASK-003**: Test authentication to Instance 1\n")
content.append("- [ ] **TASK-004**: Test authentication to Instance 2\n")
content.append("\n### Configuration Review\n")
content.append("- [ ] **TASK-005**: Review provider-config.yaml\n")
content.append("- [ ] **TASK-006**: Review Cloudflare tunnel configs\n")
content.append("- [ ] **TASK-007**: Map instances to sites\n")
content.append("\n## Short-term Tasks (Priority: Medium)\n")
content.append("### Crossplane Provider\n")
content.append("- [ ] **TASK-008**: Complete Proxmox API client implementation\n")
content.append("- [ ] **TASK-009**: Build and test provider\n")
content.append("- [ ] **TASK-010**: Deploy provider to Kubernetes\n")
content.append("- [ ] **TASK-011**: Create ProviderConfig resource\n")
content.append("\n### Infrastructure Setup\n")
content.append("- [ ] **TASK-012**: Deploy Prometheus exporters\n")
content.append("- [ ] **TASK-013**: Configure Cloudflare tunnels\n")
content.append("- [ ] **TASK-014**: Set up monitoring dashboards\n")
content.append("\n## Long-term Tasks (Priority: Low)\n")
content.append("- [ ] **TASK-015**: Deploy test VMs\n")
content.append("- [ ] **TASK-016**: End-to-end testing\n")
content.append("- [ ] **TASK-017**: Performance testing\n")
content.append("- [ ] **TASK-018**: Create runbooks\n")
content.append("- [ ] **TASK-019**: Set up backups\n")
content.append("- [ ] **TASK-020**: Security audit\n")
with open(task_file, 'w') as f:
f.write(''.join(content))
log_success(f"Task list saved to {task_file}")
return str(task_file)
def main():
"""Main execution."""
log("Starting Proxmox Review and Deployment Planning...")
log("=" * 50)
project_root = Path(__file__).parent.parent
output_dir = project_root / "docs" / "proxmox-review"
output_dir.mkdir(parents=True, exist_ok=True)
config = load_environment()
log("\n=== Phase 1: Connecting to Proxmox Instances ===")
# Connect to Instance 1
client1 = ProxmoxClient(
config['proxmox_1']['api_url'],
config['proxmox_1']['user'],
config['proxmox_1']['password'],
config['proxmox_1']['token'],
config['proxmox_1']['verify_ssl']
)
info1 = connect_and_review(client1, 1, output_dir)
log("")
# Connect to Instance 2
client2 = ProxmoxClient(
config['proxmox_2']['api_url'],
config['proxmox_2']['user'],
config['proxmox_2']['password'],
config['proxmox_2']['token'],
config['proxmox_2']['verify_ssl']
)
info2 = connect_and_review(client2, 2, output_dir)
log("\n=== Phase 2: Reviewing Configurations ===")
review_configurations(project_root, output_dir)
log("\n=== Phase 3: Generating Deployment Plan ===")
generate_deployment_plan(output_dir, config)
log("\n=== Phase 4: Generating Task List ===")
generate_task_list(output_dir, config)
log("\n" + "=" * 50)
log_success("Review and planning completed!")
log(f"\nOutput directory: {output_dir}")
if __name__ == '__main__':
main()