Move blockchain app READMEs to centralized documentation
Some checks failed
API Endpoint Tests / test-api-endpoints (push) Successful in 10s
Blockchain Synchronization Verification / sync-verification (push) Failing after 3s
CLI Tests / test-cli (push) Failing after 4s
Documentation Validation / validate-docs (push) Successful in 8s
Documentation Validation / validate-policies-strict (push) Successful in 4s
Integration Tests / test-service-integration (push) Successful in 38s
Multi-Node Blockchain Health Monitoring / health-check (push) Successful in 2s
P2P Network Verification / p2p-verification (push) Successful in 3s
Security Scanning / security-scan (push) Successful in 40s
Smart Contract Tests / test-solidity (map[name:aitbc-token path:packages/solidity/aitbc-token]) (push) Successful in 15s
Smart Contract Tests / lint-solidity (push) Successful in 8s

- Relocate blockchain-event-bridge README content to docs/apps/blockchain/blockchain-event-bridge.md
- Relocate blockchain-explorer README content to docs/apps/blockchain/blockchain-explorer.md
- Replace app READMEs with redirect notices pointing to new documentation location
- Consolidate documentation in central docs/ directory for better organization
This commit is contained in:
aitbc
2026-04-23 12:24:48 +02:00
parent cd240485c6
commit 522655ef92
55 changed files with 7033 additions and 1536 deletions

View File

@@ -0,0 +1,349 @@
"""
Blockchain Event Bridge CLI Commands for AITBC
Commands for managing blockchain event bridge service
"""
import click
import json
import requests
import subprocess
from datetime import datetime
from typing import Dict, Any, List, Optional
@click.group()
def bridge():
"""Blockchain event bridge management commands"""
pass
@bridge.command()
@click.option('--test-mode', is_flag=True, help='Run in test mode')
def health(test_mode):
"""Health check for blockchain event bridge service"""
try:
if test_mode:
# Mock data for testing
mock_health = {
"status": "healthy",
"service": "blockchain-event-bridge",
"version": "0.1.0",
"uptime_seconds": 86400,
"timestamp": datetime.utcnow().isoformat()
}
click.echo("🏥 Blockchain Event Bridge Health:")
click.echo("=" * 50)
click.echo(f"✅ Status: {mock_health['status']}")
click.echo(f"📦 Service: {mock_health['service']}")
click.echo(f"📦 Version: {mock_health['version']}")
click.echo(f"⏱️ Uptime: {mock_health['uptime_seconds']}s")
click.echo(f"🕐 Timestamp: {mock_health['timestamp']}")
return
# Fetch from bridge service
config = get_config()
response = requests.get(
f"{config.bridge_url}/health",
timeout=10
)
if response.status_code == 200:
health = response.json()
click.echo("🏥 Blockchain Event Bridge Health:")
click.echo("=" * 50)
click.echo(f"✅ Status: {health.get('status', 'unknown')}")
click.echo(f"📦 Service: {health.get('service', 'unknown')}")
click.echo(f"📦 Version: {health.get('version', 'unknown')}")
click.echo(f"⏱️ Uptime: {health.get('uptime_seconds', 0)}s")
click.echo(f"🕐 Timestamp: {health.get('timestamp', 'unknown')}")
else:
click.echo(f"❌ Health check failed: {response.text}", err=True)
except Exception as e:
click.echo(f"❌ Error checking health: {str(e)}", err=True)
@bridge.command()
@click.option('--test-mode', is_flag=True, help='Run in test mode')
def metrics(test_mode):
"""Get Prometheus metrics from blockchain event bridge service"""
try:
if test_mode:
# Mock data for testing
mock_metrics = """
# HELP bridge_events_total Total number of blockchain events processed
# TYPE bridge_events_total counter
bridge_events_total{type="block"} 12345
bridge_events_total{type="transaction"} 67890
bridge_events_total{type="contract"} 23456
# HELP bridge_events_processed_total Total number of events successfully processed
# TYPE bridge_events_processed_total counter
bridge_events_processed_total 103691
# HELP bridge_events_failed_total Total number of events that failed processing
# TYPE bridge_events_failed_total counter
bridge_events_failed_total 123
# HELP bridge_processing_duration_seconds Event processing duration
# TYPE bridge_processing_duration_seconds histogram
bridge_processing_duration_seconds_bucket{le="0.1"} 50000
bridge_processing_duration_seconds_bucket{le="1.0"} 100000
bridge_processing_duration_seconds_sum 45000.5
bridge_processing_duration_seconds_count 103691
""".strip()
click.echo("📊 Prometheus Metrics:")
click.echo("=" * 50)
click.echo(mock_metrics)
return
# Fetch from bridge service
config = get_config()
response = requests.get(
f"{config.bridge_url}/metrics",
timeout=10
)
if response.status_code == 200:
metrics = response.text
click.echo("📊 Prometheus Metrics:")
click.echo("=" * 50)
click.echo(metrics)
else:
click.echo(f"❌ Failed to get metrics: {response.text}", err=True)
except Exception as e:
click.echo(f"❌ Error getting metrics: {str(e)}", err=True)
@bridge.command()
@click.option('--test-mode', is_flag=True, help='Run in test mode')
def status(test_mode):
"""Get detailed status of blockchain event bridge service"""
try:
if test_mode:
# Mock data for testing
mock_status = {
"service": "blockchain-event-bridge",
"status": "running",
"version": "0.1.0",
"subscriptions": {
"blocks": {
"enabled": True,
"topic": "blocks",
"last_block": 123456
},
"transactions": {
"enabled": True,
"topic": "transactions",
"last_transaction": "0xabc123..."
},
"contract_events": {
"enabled": True,
"contracts": [
"AgentStaking",
"PerformanceVerifier",
"AgentServiceMarketplace"
],
"last_event": "0xdef456..."
}
},
"triggers": {
"agent_daemon": {
"enabled": True,
"events_triggered": 5432
},
"coordinator_api": {
"enabled": True,
"events_triggered": 8765
},
"marketplace": {
"enabled": True,
"events_triggered": 3210
}
},
"metrics": {
"events_processed": 103691,
"events_failed": 123,
"success_rate": 99.88
}
}
click.echo("📊 Blockchain Event Bridge Status:")
click.echo("=" * 50)
click.echo(f"📦 Service: {mock_status['service']}")
click.echo(f"✅ Status: {mock_status['status']}")
click.echo(f"📦 Version: {mock_status['version']}")
click.echo("")
click.echo("🔔 Subscriptions:")
for sub_type, sub_data in mock_status['subscriptions'].items():
click.echo(f" {sub_type}:")
click.echo(f" Enabled: {sub_data['enabled']}")
if 'topic' in sub_data:
click.echo(f" Topic: {sub_data['topic']}")
if 'last_block' in sub_data:
click.echo(f" Last Block: {sub_data['last_block']}")
if 'contracts' in sub_data:
click.echo(f" Contracts: {', '.join(sub_data['contracts'])}")
click.echo("")
click.echo("🎯 Triggers:")
for trigger_type, trigger_data in mock_status['triggers'].items():
click.echo(f" {trigger_type}:")
click.echo(f" Enabled: {trigger_data['enabled']}")
click.echo(f" Events Triggered: {trigger_data['events_triggered']}")
click.echo("")
click.echo("📊 Metrics:")
click.echo(f" Events Processed: {mock_status['metrics']['events_processed']}")
click.echo(f" Events Failed: {mock_status['metrics']['events_failed']}")
click.echo(f" Success Rate: {mock_status['metrics']['success_rate']}%")
return
# Fetch from bridge service
config = get_config()
response = requests.get(
f"{config.bridge_url}/",
timeout=10
)
if response.status_code == 200:
status = response.json()
click.echo("📊 Blockchain Event Bridge Status:")
click.echo("=" * 50)
click.echo(f"📦 Service: {status.get('service', 'unknown')}")
click.echo(f"✅ Status: {status.get('status', 'unknown')}")
click.echo(f"📦 Version: {status.get('version', 'unknown')}")
if 'subscriptions' in status:
click.echo("")
click.echo("🔔 Subscriptions:")
for sub_type, sub_data in status['subscriptions'].items():
click.echo(f" {sub_type}:")
click.echo(f" Enabled: {sub_data.get('enabled', False)}")
else:
click.echo(f"❌ Failed to get status: {response.text}", err=True)
except Exception as e:
click.echo(f"❌ Error getting status: {str(e)}", err=True)
@bridge.command()
@click.option('--test-mode', is_flag=True, help='Run in test mode')
def config(test_mode):
"""Show current configuration of blockchain event bridge service"""
try:
if test_mode:
# Mock data for testing
mock_config = {
"blockchain_rpc_url": "http://localhost:8006",
"gossip_backend": "redis",
"gossip_broadcast_url": "redis://localhost:6379",
"coordinator_api_url": "http://localhost:8011",
"coordinator_api_key": "***",
"subscriptions": {
"blocks": True,
"transactions": True
},
"triggers": {
"agent_daemon": True,
"coordinator_api": True,
"marketplace": True
},
"polling": {
"enabled": False,
"interval_seconds": 60
}
}
click.echo("⚙️ Blockchain Event Bridge Configuration:")
click.echo("=" * 50)
click.echo(f"🔗 Blockchain RPC URL: {mock_config['blockchain_rpc_url']}")
click.echo(f"💬 Gossip Backend: {mock_config['gossip_backend']}")
if mock_config.get('gossip_broadcast_url'):
click.echo(f"📡 Gossip Broadcast URL: {mock_config['gossip_broadcast_url']}")
click.echo(f"🎯 Coordinator API URL: {mock_config['coordinator_api_url']}")
click.echo(f"🔑 Coordinator API Key: {mock_config['coordinator_api_key']}")
click.echo("")
click.echo("🔔 Subscriptions:")
for sub, enabled in mock_config['subscriptions'].items():
status = "" if enabled else ""
click.echo(f" {status} {sub}")
click.echo("")
click.echo("🎯 Triggers:")
for trigger, enabled in mock_config['triggers'].items():
status = "" if enabled else ""
click.echo(f" {status} {trigger}")
click.echo("")
click.echo("⏱️ Polling:")
click.echo(f" Enabled: {mock_config['polling']['enabled']}")
click.echo(f" Interval: {mock_config['polling']['interval_seconds']}s")
return
# Fetch from bridge service
config = get_config()
response = requests.get(
f"{config.bridge_url}/config",
timeout=10
)
if response.status_code == 200:
service_config = response.json()
click.echo("⚙️ Blockchain Event Bridge Configuration:")
click.echo("=" * 50)
click.echo(f"🔗 Blockchain RPC URL: {service_config.get('blockchain_rpc_url', 'unknown')}")
click.echo(f"💬 Gossip Backend: {service_config.get('gossip_backend', 'unknown')}")
if service_config.get('gossip_broadcast_url'):
click.echo(f"📡 Gossip Broadcast URL: {service_config['gossip_broadcast_url']}")
click.echo(f"🎯 Coordinator API URL: {service_config.get('coordinator_api_url', 'unknown')}")
else:
click.echo(f"❌ Failed to get config: {response.text}", err=True)
except Exception as e:
click.echo(f"❌ Error getting config: {str(e)}", err=True)
@bridge.command()
@click.option('--test-mode', is_flag=True, help='Run in test mode')
def restart(test_mode):
"""Restart blockchain event bridge service (via systemd)"""
try:
if test_mode:
click.echo("🔄 Blockchain event bridge restart triggered (test mode)")
click.echo("✅ Restart completed successfully")
return
# Restart via systemd
try:
result = subprocess.run(
["sudo", "systemctl", "restart", "aitbc-blockchain-event-bridge"],
capture_output=True,
text=True,
timeout=30
)
if result.returncode == 0:
click.echo("🔄 Blockchain event bridge restart triggered")
click.echo("✅ Restart completed successfully")
else:
click.echo(f"❌ Restart failed: {result.stderr}", err=True)
except subprocess.TimeoutExpired:
click.echo("❌ Restart timeout - service may be starting", err=True)
except FileNotFoundError:
click.echo("❌ systemctl not found - cannot restart service", err=True)
except Exception as e:
click.echo(f"❌ Error restarting service: {str(e)}", err=True)
# Helper function to get config
def get_config():
"""Get CLI configuration"""
try:
from config import get_config
return get_config()
except ImportError:
# Fallback for testing
from types import SimpleNamespace
return SimpleNamespace(
bridge_url="http://localhost:8204",
api_key="test-api-key"
)
if __name__ == "__main__":
bridge()

486
cli/commands/pool_hub.py Normal file
View File

@@ -0,0 +1,486 @@
"""
Pool Hub CLI Commands for AITBC
Commands for SLA monitoring, capacity planning, and billing integration
"""
import click
import json
import requests
from datetime import datetime
from typing import Dict, Any, List, Optional
@click.group()
def pool_hub():
"""Pool hub management commands for SLA monitoring and billing"""
pass
@pool_hub.command()
@click.argument('miner_id', required=False)
@click.option('--test-mode', is_flag=True, help='Run in test mode')
def sla_metrics(miner_id, test_mode):
"""Get SLA metrics for a miner or all miners"""
try:
if test_mode:
# Mock data for testing
if miner_id:
mock_metrics = {
"miner_id": miner_id,
"uptime_percentage": 97.5,
"response_time_ms": 850,
"job_completion_rate": 92.3,
"capacity_availability": 85.0,
"thresholds": {
"uptime": 95.0,
"response_time": 1000,
"completion_rate": 90.0,
"capacity": 80.0
},
"violations": [
{
"type": "response_time",
"threshold": 1000,
"actual": 1200,
"timestamp": "2024-03-15T14:30:00Z"
}
]
}
click.echo(f"📊 SLA Metrics for {miner_id}:")
click.echo("=" * 50)
click.echo(f"⏱️ Uptime: {mock_metrics['uptime_percentage']}% (threshold: {mock_metrics['thresholds']['uptime']}%)")
click.echo(f"⚡ Response Time: {mock_metrics['response_time_ms']}ms (threshold: {mock_metrics['thresholds']['response_time']}ms)")
click.echo(f"✅ Job Completion Rate: {mock_metrics['job_completion_rate']}% (threshold: {mock_metrics['thresholds']['completion_rate']}%)")
click.echo(f"📦 Capacity Availability: {mock_metrics['capacity_availability']}% (threshold: {mock_metrics['thresholds']['capacity']}%)")
if mock_metrics['violations']:
click.echo("")
click.echo("⚠️ Violations:")
for v in mock_metrics['violations']:
click.echo(f" {v['type']}: {v['actual']} vs threshold {v['threshold']} at {v['timestamp']}")
else:
mock_metrics = {
"total_miners": 45,
"average_uptime": 96.2,
"average_response_time": 780,
"average_completion_rate": 94.1,
"average_capacity": 88.5,
"miners_below_threshold": 3
}
click.echo("📊 SLA Metrics (All Miners):")
click.echo("=" * 50)
click.echo(f"👥 Total Miners: {mock_metrics['total_miners']}")
click.echo(f"⏱️ Average Uptime: {mock_metrics['average_uptime']}%")
click.echo(f"⚡ Average Response Time: {mock_metrics['average_response_time']}ms")
click.echo(f"✅ Average Completion Rate: {mock_metrics['average_completion_rate']}%")
click.echo(f"📦 Average Capacity: {mock_metrics['average_capacity']}%")
click.echo(f"⚠️ Miners Below Threshold: {mock_metrics['miners_below_threshold']}")
return
# Fetch from pool-hub service
config = get_config()
if miner_id:
response = requests.get(
f"{config.pool_hub_url}/sla/metrics/{miner_id}",
timeout=30
)
else:
response = requests.get(
f"{config.pool_hub_url}/sla/metrics",
timeout=30
)
if response.status_code == 200:
metrics = response.json()
if miner_id:
click.echo(f"📊 SLA Metrics for {miner_id}:")
click.echo("=" * 50)
click.echo(f"⏱️ Uptime: {metrics.get('uptime_percentage', 0)}%")
click.echo(f"⚡ Response Time: {metrics.get('response_time_ms', 0)}ms")
click.echo(f"✅ Job Completion Rate: {metrics.get('job_completion_rate', 0)}%")
click.echo(f"📦 Capacity Availability: {metrics.get('capacity_availability', 0)}%")
else:
click.echo("📊 SLA Metrics (All Miners):")
click.echo("=" * 50)
click.echo(f"👥 Total Miners: {metrics.get('total_miners', 0)}")
click.echo(f"⏱️ Average Uptime: {metrics.get('average_uptime', 0)}%")
click.echo(f"⚡ Average Response Time: {metrics.get('average_response_time', 0)}ms")
click.echo(f"✅ Average Completion Rate: {metrics.get('average_completion_rate', 0)}%")
else:
click.echo(f"❌ Failed to get SLA metrics: {response.text}", err=True)
except Exception as e:
click.echo(f"❌ Error getting SLA metrics: {str(e)}", err=True)
@pool_hub.command()
@click.option('--test-mode', is_flag=True, help='Run in test mode')
def sla_violations(test_mode):
"""Get SLA violations across all miners"""
try:
if test_mode:
# Mock data for testing
mock_violations = [
{
"miner_id": "miner_001",
"type": "response_time",
"threshold": 1000,
"actual": 1200,
"timestamp": "2024-03-15T14:30:00Z"
},
{
"miner_id": "miner_002",
"type": "uptime",
"threshold": 95.0,
"actual": 92.5,
"timestamp": "2024-03-15T13:45:00Z"
}
]
click.echo("⚠️ SLA Violations:")
click.echo("=" * 50)
for v in mock_violations:
click.echo(f"👤 Miner: {v['miner_id']}")
click.echo(f" Type: {v['type']}")
click.echo(f" Threshold: {v['threshold']}")
click.echo(f" Actual: {v['actual']}")
click.echo(f" Timestamp: {v['timestamp']}")
click.echo("")
return
# Fetch from pool-hub service
config = get_config()
response = requests.get(
f"{config.pool_hub_url}/sla/violations",
timeout=30
)
if response.status_code == 200:
violations = response.json()
click.echo("⚠️ SLA Violations:")
click.echo("=" * 50)
for v in violations:
click.echo(f"👤 Miner: {v['miner_id']}")
click.echo(f" Type: {v['type']}")
click.echo(f" Threshold: {v['threshold']}")
click.echo(f" Actual: {v['actual']}")
click.echo(f" Timestamp: {v['timestamp']}")
click.echo("")
else:
click.echo(f"❌ Failed to get violations: {response.text}", err=True)
except Exception as e:
click.echo(f"❌ Error getting violations: {str(e)}", err=True)
@pool_hub.command()
@click.option('--test-mode', is_flag=True, help='Run in test mode')
def capacity_snapshots(test_mode):
"""Get capacity planning snapshots"""
try:
if test_mode:
# Mock data for testing
mock_snapshots = [
{
"timestamp": "2024-03-15T00:00:00Z",
"total_capacity": 1250,
"available_capacity": 320,
"utilization": 74.4,
"active_miners": 42
},
{
"timestamp": "2024-03-14T00:00:00Z",
"total_capacity": 1200,
"available_capacity": 350,
"utilization": 70.8,
"active_miners": 40
}
]
click.echo("📊 Capacity Snapshots:")
click.echo("=" * 50)
for s in mock_snapshots:
click.echo(f"🕐 Timestamp: {s['timestamp']}")
click.echo(f" Total Capacity: {s['total_capacity']} GPU")
click.echo(f" Available: {s['available_capacity']} GPU")
click.echo(f" Utilization: {s['utilization']}%")
click.echo(f" Active Miners: {s['active_miners']}")
click.echo("")
return
# Fetch from pool-hub service
config = get_config()
response = requests.get(
f"{config.pool_hub_url}/sla/capacity/snapshots",
timeout=30
)
if response.status_code == 200:
snapshots = response.json()
click.echo("📊 Capacity Snapshots:")
click.echo("=" * 50)
for s in snapshots:
click.echo(f"🕐 Timestamp: {s['timestamp']}")
click.echo(f" Total Capacity: {s['total_capacity']} GPU")
click.echo(f" Available: {s['available_capacity']} GPU")
click.echo(f" Utilization: {s['utilization']}%")
click.echo(f" Active Miners: {s['active_miners']}")
click.echo("")
else:
click.echo(f"❌ Failed to get snapshots: {response.text}", err=True)
except Exception as e:
click.echo(f"❌ Error getting snapshots: {str(e)}", err=True)
@pool_hub.command()
@click.option('--test-mode', is_flag=True, help='Run in test mode')
def capacity_forecast(test_mode):
"""Get capacity forecast"""
try:
if test_mode:
# Mock data for testing
mock_forecast = {
"forecast_days": 7,
"current_capacity": 1250,
"projected_capacity": 1400,
"growth_rate": 12.0,
"daily_projections": [
{"day": 1, "capacity": 1280},
{"day": 2, "capacity": 1310},
{"day": 3, "capacity": 1340},
{"day": 7, "capacity": 1400}
]
}
click.echo("🔮 Capacity Forecast:")
click.echo("=" * 50)
click.echo(f"📅 Forecast Period: {mock_forecast['forecast_days']} days")
click.echo(f"📊 Current Capacity: {mock_forecast['current_capacity']} GPU")
click.echo(f"📈 Projected Capacity: {mock_forecast['projected_capacity']} GPU")
click.echo(f"📊 Growth Rate: {mock_forecast['growth_rate']}%")
click.echo("")
click.echo("Daily Projections:")
for p in mock_forecast['daily_projections']:
click.echo(f" Day {p['day']}: {p['capacity']} GPU")
return
# Fetch from pool-hub service
config = get_config()
response = requests.get(
f"{config.pool_hub_url}/sla/capacity/forecast",
timeout=30
)
if response.status_code == 200:
forecast = response.json()
click.echo("🔮 Capacity Forecast:")
click.echo("=" * 50)
click.echo(f"📅 Forecast Period: {forecast['forecast_days']} days")
click.echo(f"📊 Current Capacity: {forecast['current_capacity']} GPU")
click.echo(f"📈 Projected Capacity: {forecast['projected_capacity']} GPU")
click.echo(f"📊 Growth Rate: {forecast['growth_rate']}%")
click.echo("")
click.echo("Daily Projections:")
for p in forecast['daily_projections']:
click.echo(f" Day {p['day']}: {p['capacity']} GPU")
else:
click.echo(f"❌ Failed to get forecast: {response.text}", err=True)
except Exception as e:
click.echo(f"❌ Error getting forecast: {str(e)}", err=True)
@pool_hub.command()
@click.option('--test-mode', is_flag=True, help='Run in test mode')
def capacity_recommendations(test_mode):
"""Get scaling recommendations"""
try:
if test_mode:
# Mock data for testing
mock_recommendations = [
{
"type": "scale_up",
"reason": "High utilization (>80%)",
"action": "Add 50 GPU capacity",
"priority": "high"
},
{
"type": "optimize",
"reason": "Imbalanced workload distribution",
"action": "Rebalance miners across regions",
"priority": "medium"
}
]
click.echo("💡 Capacity Recommendations:")
click.echo("=" * 50)
for r in mock_recommendations:
click.echo(f"📌 Type: {r['type']}")
click.echo(f" Reason: {r['reason']}")
click.echo(f" Action: {r['action']}")
click.echo(f" Priority: {r['priority']}")
click.echo("")
return
# Fetch from pool-hub service
config = get_config()
response = requests.get(
f"{config.pool_hub_url}/sla/capacity/recommendations",
timeout=30
)
if response.status_code == 200:
recommendations = response.json()
click.echo("💡 Capacity Recommendations:")
click.echo("=" * 50)
for r in recommendations:
click.echo(f"📌 Type: {r['type']}")
click.echo(f" Reason: {r['reason']}")
click.echo(f" Action: {r['action']}")
click.echo(f" Priority: {r['priority']}")
click.echo("")
else:
click.echo(f"❌ Failed to get recommendations: {response.text}", err=True)
except Exception as e:
click.echo(f"❌ Error getting recommendations: {str(e)}", err=True)
@pool_hub.command()
@click.option('--test-mode', is_flag=True, help='Run in test mode')
def billing_usage(test_mode):
"""Get billing usage data"""
try:
if test_mode:
# Mock data for testing
mock_usage = {
"period_start": "2024-03-01T00:00:00Z",
"period_end": "2024-03-31T23:59:59Z",
"total_gpu_hours": 45678,
"total_api_calls": 1234567,
"total_compute_hours": 23456,
"total_cost": 12500.50,
"by_miner": [
{"miner_id": "miner_001", "gpu_hours": 12000, "cost": 3280.50},
{"miner_id": "miner_002", "gpu_hours": 8900, "cost": 2435.00}
]
}
click.echo("💰 Billing Usage:")
click.echo("=" * 50)
click.echo(f"📅 Period: {mock_usage['period_start']} to {mock_usage['period_end']}")
click.echo(f"⚡ Total GPU Hours: {mock_usage['total_gpu_hours']}")
click.echo(f"📞 Total API Calls: {mock_usage['total_api_calls']}")
click.echo(f"🖥️ Total Compute Hours: {mock_usage['total_compute_hours']}")
click.echo(f"💵 Total Cost: ${mock_usage['total_cost']:.2f}")
click.echo("")
click.echo("By Miner:")
for m in mock_usage['by_miner']:
click.echo(f" {m['miner_id']}: {m['gpu_hours']} GPUh, ${m['cost']:.2f}")
return
# Fetch from pool-hub service
config = get_config()
response = requests.get(
f"{config.pool_hub_url}/sla/billing/usage",
timeout=30
)
if response.status_code == 200:
usage = response.json()
click.echo("💰 Billing Usage:")
click.echo("=" * 50)
click.echo(f"📅 Period: {usage['period_start']} to {usage['period_end']}")
click.echo(f"⚡ Total GPU Hours: {usage['total_gpu_hours']}")
click.echo(f"📞 Total API Calls: {usage['total_api_calls']}")
click.echo(f"🖥️ Total Compute Hours: {usage['total_compute_hours']}")
click.echo(f"💵 Total Cost: ${usage['total_cost']:.2f}")
click.echo("")
click.echo("By Miner:")
for m in usage['by_miner']:
click.echo(f" {m['miner_id']}: {m['gpu_hours']} GPUh, ${m['cost']:.2f}")
else:
click.echo(f"❌ Failed to get billing usage: {response.text}", err=True)
except Exception as e:
click.echo(f"❌ Error getting billing usage: {str(e)}", err=True)
@pool_hub.command()
@click.option('--test-mode', is_flag=True, help='Run in test mode')
def billing_sync(test_mode):
"""Trigger billing sync with coordinator-api"""
try:
if test_mode:
click.echo("🔄 Billing sync triggered (test mode)")
click.echo("✅ Sync completed successfully")
return
# Trigger sync with pool-hub service
config = get_config()
response = requests.post(
f"{config.pool_hub_url}/sla/billing/sync",
timeout=60
)
if response.status_code == 200:
result = response.json()
click.echo("🔄 Billing sync triggered")
click.echo(f"✅ Sync completed: {result.get('message', 'Success')}")
else:
click.echo(f"❌ Billing sync failed: {response.text}", err=True)
except Exception as e:
click.echo(f"❌ Error triggering billing sync: {str(e)}", err=True)
@pool_hub.command()
@click.option('--test-mode', is_flag=True, help='Run in test mode')
def collect_metrics(test_mode):
"""Trigger SLA metrics collection"""
try:
if test_mode:
click.echo("📊 SLA metrics collection triggered (test mode)")
click.echo("✅ Collection completed successfully")
return
# Trigger collection with pool-hub service
config = get_config()
response = requests.post(
f"{config.pool_hub_url}/sla/metrics/collect",
timeout=60
)
if response.status_code == 200:
result = response.json()
click.echo("📊 SLA metrics collection triggered")
click.echo(f"✅ Collection completed: {result.get('message', 'Success')}")
else:
click.echo(f"❌ Metrics collection failed: {response.text}", err=True)
except Exception as e:
click.echo(f"❌ Error triggering metrics collection: {str(e)}", err=True)
# Helper function to get config
def get_config():
"""Get CLI configuration"""
try:
from config import get_config
return get_config()
except ImportError:
# Fallback for testing
from types import SimpleNamespace
return SimpleNamespace(
pool_hub_url="http://localhost:8012",
api_key="test-api-key"
)
if __name__ == "__main__":
pool_hub()

View File

@@ -1425,6 +1425,333 @@ def run_cli(argv, core):
print(f"Error getting account: {e}")
sys.exit(1)
def handle_pool_hub_sla_metrics(args):
"""Get SLA metrics for a miner or all miners"""
try:
from commands.pool_hub import get_config as get_pool_hub_config
config = get_pool_hub_config()
if args.test_mode:
print("📊 SLA Metrics (test mode):")
print("⏱️ Uptime: 97.5%")
print("⚡ Response Time: 850ms")
print("✅ Job Completion Rate: 92.3%")
return
pool_hub_url = getattr(config, "pool_hub_url", "http://localhost:8012")
miner_id = getattr(args, "miner_id", None)
if miner_id:
response = requests.get(f"{pool_hub_url}/sla/metrics/{miner_id}", timeout=30)
else:
response = requests.get(f"{pool_hub_url}/sla/metrics", timeout=30)
if response.status_code == 200:
metrics = response.json()
print("📊 SLA Metrics:")
for key, value in metrics.items():
print(f" {key}: {value}")
else:
print(f"❌ Failed to get SLA metrics: {response.text}")
except Exception as e:
print(f"❌ Error getting SLA metrics: {e}")
def handle_pool_hub_sla_violations(args):
"""Get SLA violations across all miners"""
try:
from commands.pool_hub import get_config as get_pool_hub_config
config = get_pool_hub_config()
if args.test_mode:
print("⚠️ SLA Violations (test mode):")
print(" miner_001: response_time violation")
return
pool_hub_url = getattr(config, "pool_hub_url", "http://localhost:8012")
response = requests.get(f"{pool_hub_url}/sla/violations", timeout=30)
if response.status_code == 200:
violations = response.json()
print("⚠️ SLA Violations:")
for v in violations:
print(f" {v}")
else:
print(f"❌ Failed to get violations: {response.text}")
except Exception as e:
print(f"❌ Error getting violations: {e}")
def handle_pool_hub_capacity_snapshots(args):
"""Get capacity planning snapshots"""
try:
from commands.pool_hub import get_config as get_pool_hub_config
config = get_pool_hub_config()
if args.test_mode:
print("📊 Capacity Snapshots (test mode):")
print(" Total Capacity: 1250 GPU")
print(" Available: 320 GPU")
return
pool_hub_url = getattr(config, "pool_hub_url", "http://localhost:8012")
response = requests.get(f"{pool_hub_url}/sla/capacity/snapshots", timeout=30)
if response.status_code == 200:
snapshots = response.json()
print("📊 Capacity Snapshots:")
for s in snapshots:
print(f" {s}")
else:
print(f"❌ Failed to get snapshots: {response.text}")
except Exception as e:
print(f"❌ Error getting snapshots: {e}")
def handle_pool_hub_capacity_forecast(args):
"""Get capacity forecast"""
try:
from commands.pool_hub import get_config as get_pool_hub_config
config = get_pool_hub_config()
if args.test_mode:
print("🔮 Capacity Forecast (test mode):")
print(" Projected Capacity: 1400 GPU")
print(" Growth Rate: 12%")
return
pool_hub_url = getattr(config, "pool_hub_url", "http://localhost:8012")
response = requests.get(f"{pool_hub_url}/sla/capacity/forecast", timeout=30)
if response.status_code == 200:
forecast = response.json()
print("🔮 Capacity Forecast:")
for key, value in forecast.items():
print(f" {key}: {value}")
else:
print(f"❌ Failed to get forecast: {response.text}")
except Exception as e:
print(f"❌ Error getting forecast: {e}")
def handle_pool_hub_capacity_recommendations(args):
"""Get scaling recommendations"""
try:
from commands.pool_hub import get_config as get_pool_hub_config
config = get_pool_hub_config()
if args.test_mode:
print("💡 Capacity Recommendations (test mode):")
print(" Type: scale_up")
print(" Action: Add 50 GPU capacity")
return
pool_hub_url = getattr(config, "pool_hub_url", "http://localhost:8012")
response = requests.get(f"{pool_hub_url}/sla/capacity/recommendations", timeout=30)
if response.status_code == 200:
recommendations = response.json()
print("💡 Capacity Recommendations:")
for r in recommendations:
print(f" {r}")
else:
print(f"❌ Failed to get recommendations: {response.text}")
except Exception as e:
print(f"❌ Error getting recommendations: {e}")
def handle_pool_hub_billing_usage(args):
"""Get billing usage data"""
try:
from commands.pool_hub import get_config as get_pool_hub_config
config = get_pool_hub_config()
if args.test_mode:
print("💰 Billing Usage (test mode):")
print(" Total GPU Hours: 45678")
print(" Total Cost: $12500.50")
return
pool_hub_url = getattr(config, "pool_hub_url", "http://localhost:8012")
response = requests.get(f"{pool_hub_url}/sla/billing/usage", timeout=30)
if response.status_code == 200:
usage = response.json()
print("💰 Billing Usage:")
for key, value in usage.items():
print(f" {key}: {value}")
else:
print(f"❌ Failed to get billing usage: {response.text}")
except Exception as e:
print(f"❌ Error getting billing usage: {e}")
def handle_pool_hub_billing_sync(args):
"""Trigger billing sync with coordinator-api"""
try:
from commands.pool_hub import get_config as get_pool_hub_config
config = get_pool_hub_config()
if args.test_mode:
print("🔄 Billing sync triggered (test mode)")
print("✅ Sync completed successfully")
return
pool_hub_url = getattr(config, "pool_hub_url", "http://localhost:8012")
response = requests.post(f"{pool_hub_url}/sla/billing/sync", timeout=60)
if response.status_code == 200:
result = response.json()
print("🔄 Billing sync triggered")
print(f"{result.get('message', 'Success')}")
else:
print(f"❌ Billing sync failed: {response.text}")
except Exception as e:
print(f"❌ Error triggering billing sync: {e}")
def handle_pool_hub_collect_metrics(args):
"""Trigger SLA metrics collection"""
try:
from commands.pool_hub import get_config as get_pool_hub_config
config = get_pool_hub_config()
if args.test_mode:
print("📊 SLA metrics collection triggered (test mode)")
print("✅ Collection completed successfully")
return
pool_hub_url = getattr(config, "pool_hub_url", "http://localhost:8012")
response = requests.post(f"{pool_hub_url}/sla/metrics/collect", timeout=60)
if response.status_code == 200:
result = response.json()
print("📊 SLA metrics collection triggered")
print(f"{result.get('message', 'Success')}")
else:
print(f"❌ Metrics collection failed: {response.text}")
except Exception as e:
print(f"❌ Error triggering metrics collection: {e}")
def handle_bridge_health(args):
"""Health check for blockchain event bridge service"""
try:
from commands.blockchain_event_bridge import get_config as get_bridge_config
config = get_bridge_config()
if args.test_mode:
print("🏥 Blockchain Event Bridge Health (test mode):")
print("✅ Status: healthy")
print("📦 Service: blockchain-event-bridge")
return
bridge_url = getattr(config, "bridge_url", "http://localhost:8204")
response = requests.get(f"{bridge_url}/health", timeout=10)
if response.status_code == 200:
health = response.json()
print("🏥 Blockchain Event Bridge Health:")
for key, value in health.items():
print(f" {key}: {value}")
else:
print(f"❌ Health check failed: {response.text}")
except Exception as e:
print(f"❌ Error checking health: {e}")
def handle_bridge_metrics(args):
"""Get Prometheus metrics from blockchain event bridge service"""
try:
from commands.blockchain_event_bridge import get_config as get_bridge_config
config = get_bridge_config()
if args.test_mode:
print("📊 Prometheus Metrics (test mode):")
print(" bridge_events_total: 103691")
print(" bridge_events_processed_total: 103691")
return
bridge_url = getattr(config, "bridge_url", "http://localhost:8204")
response = requests.get(f"{bridge_url}/metrics", timeout=10)
if response.status_code == 200:
metrics = response.text
print("📊 Prometheus Metrics:")
print(metrics)
else:
print(f"❌ Failed to get metrics: {response.text}")
except Exception as e:
print(f"❌ Error getting metrics: {e}")
def handle_bridge_status(args):
"""Get detailed status of blockchain event bridge service"""
try:
from commands.blockchain_event_bridge import get_config as get_bridge_config
config = get_bridge_config()
if args.test_mode:
print("📊 Blockchain Event Bridge Status (test mode):")
print("✅ Status: running")
print("🔔 Subscriptions: blocks, transactions, contract_events")
return
bridge_url = getattr(config, "bridge_url", "http://localhost:8204")
response = requests.get(f"{bridge_url}/", timeout=10)
if response.status_code == 200:
status = response.json()
print("📊 Blockchain Event Bridge Status:")
for key, value in status.items():
print(f" {key}: {value}")
else:
print(f"❌ Failed to get status: {response.text}")
except Exception as e:
print(f"❌ Error getting status: {e}")
def handle_bridge_config(args):
"""Show current configuration of blockchain event bridge service"""
try:
from commands.blockchain_event_bridge import get_config as get_bridge_config
config = get_bridge_config()
if args.test_mode:
print("⚙️ Blockchain Event Bridge Configuration (test mode):")
print("🔗 Blockchain RPC URL: http://localhost:8006")
print("💬 Gossip Backend: redis")
return
bridge_url = getattr(config, "bridge_url", "http://localhost:8204")
response = requests.get(f"{bridge_url}/config", timeout=10)
if response.status_code == 200:
service_config = response.json()
print("⚙️ Blockchain Event Bridge Configuration:")
for key, value in service_config.items():
print(f" {key}: {value}")
else:
print(f"❌ Failed to get config: {response.text}")
except Exception as e:
print(f"❌ Error getting config: {e}")
def handle_bridge_restart(args):
"""Restart blockchain event bridge service (via systemd)"""
try:
if args.test_mode:
print("🔄 Blockchain event bridge restart triggered (test mode)")
print("✅ Restart completed successfully")
return
result = subprocess.run(
["sudo", "systemctl", "restart", "aitbc-blockchain-event-bridge"],
capture_output=True,
text=True,
timeout=30
)
if result.returncode == 0:
print("🔄 Blockchain event bridge restart triggered")
print("✅ Restart completed successfully")
else:
print(f"❌ Restart failed: {result.stderr}")
except subprocess.TimeoutExpired:
print("❌ Restart timeout - service may be starting")
except FileNotFoundError:
print("❌ systemctl not found - cannot restart service")
except Exception as e:
print(f"❌ Error restarting service: {e}")
def handle_blockchain_transactions(args):
rpc_url = args.rpc_url or default_rpc_url
chain_id = getattr(args, "chain_id", None)
@@ -2171,6 +2498,67 @@ def run_cli(argv, core):
simulate_ai_jobs_parser.add_argument("--duration-range", default="30-300")
simulate_ai_jobs_parser.set_defaults(handler=handle_simulate_action)
pool_hub_parser = subparsers.add_parser("pool-hub", help="Pool hub management for SLA monitoring and billing")
pool_hub_parser.set_defaults(handler=lambda parsed, parser=pool_hub_parser: parser.print_help())
pool_hub_subparsers = pool_hub_parser.add_subparsers(dest="pool_hub_action")
pool_hub_sla_metrics_parser = pool_hub_subparsers.add_parser("sla-metrics", help="Get SLA metrics for miner or all miners")
pool_hub_sla_metrics_parser.add_argument("miner_id", nargs="?")
pool_hub_sla_metrics_parser.add_argument("--test-mode", action="store_true")
pool_hub_sla_metrics_parser.set_defaults(handler=handle_pool_hub_sla_metrics)
pool_hub_sla_violations_parser = pool_hub_subparsers.add_parser("sla-violations", help="Get SLA violations")
pool_hub_sla_violations_parser.add_argument("--test-mode", action="store_true")
pool_hub_sla_violations_parser.set_defaults(handler=handle_pool_hub_sla_violations)
pool_hub_capacity_snapshots_parser = pool_hub_subparsers.add_parser("capacity-snapshots", help="Get capacity planning snapshots")
pool_hub_capacity_snapshots_parser.add_argument("--test-mode", action="store_true")
pool_hub_capacity_snapshots_parser.set_defaults(handler=handle_pool_hub_capacity_snapshots)
pool_hub_capacity_forecast_parser = pool_hub_subparsers.add_parser("capacity-forecast", help="Get capacity forecast")
pool_hub_capacity_forecast_parser.add_argument("--test-mode", action="store_true")
pool_hub_capacity_forecast_parser.set_defaults(handler=handle_pool_hub_capacity_forecast)
pool_hub_capacity_recommendations_parser = pool_hub_subparsers.add_parser("capacity-recommendations", help="Get scaling recommendations")
pool_hub_capacity_recommendations_parser.add_argument("--test-mode", action="store_true")
pool_hub_capacity_recommendations_parser.set_defaults(handler=handle_pool_hub_capacity_recommendations)
pool_hub_billing_usage_parser = pool_hub_subparsers.add_parser("billing-usage", help="Get billing usage data")
pool_hub_billing_usage_parser.add_argument("--test-mode", action="store_true")
pool_hub_billing_usage_parser.set_defaults(handler=handle_pool_hub_billing_usage)
pool_hub_billing_sync_parser = pool_hub_subparsers.add_parser("billing-sync", help="Trigger billing sync with coordinator-api")
pool_hub_billing_sync_parser.add_argument("--test-mode", action="store_true")
pool_hub_billing_sync_parser.set_defaults(handler=handle_pool_hub_billing_sync)
pool_hub_collect_metrics_parser = pool_hub_subparsers.add_parser("collect-metrics", help="Trigger SLA metrics collection")
pool_hub_collect_metrics_parser.add_argument("--test-mode", action="store_true")
pool_hub_collect_metrics_parser.set_defaults(handler=handle_pool_hub_collect_metrics)
bridge_parser = subparsers.add_parser("bridge", help="Blockchain event bridge management")
bridge_parser.set_defaults(handler=lambda parsed, parser=bridge_parser: parser.print_help())
bridge_subparsers = bridge_parser.add_subparsers(dest="bridge_action")
bridge_health_parser = bridge_subparsers.add_parser("health", help="Health check for blockchain event bridge service")
bridge_health_parser.add_argument("--test-mode", action="store_true")
bridge_health_parser.set_defaults(handler=handle_bridge_health)
bridge_metrics_parser = bridge_subparsers.add_parser("metrics", help="Get Prometheus metrics from blockchain event bridge service")
bridge_metrics_parser.add_argument("--test-mode", action="store_true")
bridge_metrics_parser.set_defaults(handler=handle_bridge_metrics)
bridge_status_parser = bridge_subparsers.add_parser("status", help="Get detailed status of blockchain event bridge service")
bridge_status_parser.add_argument("--test-mode", action="store_true")
bridge_status_parser.set_defaults(handler=handle_bridge_status)
bridge_config_parser = bridge_subparsers.add_parser("config", help="Show current configuration of blockchain event bridge service")
bridge_config_parser.add_argument("--test-mode", action="store_true")
bridge_config_parser.set_defaults(handler=handle_bridge_config)
bridge_restart_parser = bridge_subparsers.add_parser("restart", help="Restart blockchain event bridge service (via systemd)")
bridge_restart_parser.add_argument("--test-mode", action="store_true")
bridge_restart_parser.set_defaults(handler=handle_bridge_restart)
parsed_args = parser.parse_args(normalize_legacy_args(list(sys.argv[1:] if argv is None else argv)))
if not getattr(parsed_args, "command", None):
parser.print_help()