feat: implement AITBC mesh network deployment infrastructure

 Phase 0: Pre-implementation checklist completed
- Environment configurations (dev/staging/production)
- Directory structure setup (logs, backups, monitoring)
- Virtual environment with dependencies

 Master deployment script created
- Single command deployment with validation
- Progress tracking and rollback capability
- Health checks and deployment reporting

 Validation script created
- Module import validation
- Basic functionality testing
- Configuration and script verification

 Implementation fixes
- Fixed dataclass import in consensus keys
- Fixed async function syntax in tests
- Updated deployment script for virtual environment

🚀 Ready for deployment: ./scripts/deploy-mesh-network.sh dev
This commit is contained in:
aitbc
2026-04-02 12:08:15 +02:00
parent d68aa9a234
commit c876b0aa20
206 changed files with 47861 additions and 1 deletions

View File

@@ -0,0 +1,452 @@
"""
Network Topology Optimization
Optimizes peer connection strategies for network performance
"""
import asyncio
import networkx as nx
import time
from typing import Dict, List, Set, Tuple, Optional
from dataclasses import dataclass
from enum import Enum
from .discovery import PeerNode, P2PDiscovery
from .health import PeerHealthMonitor, HealthStatus
class TopologyStrategy(Enum):
SMALL_WORLD = "small_world"
SCALE_FREE = "scale_free"
MESH = "mesh"
HYBRID = "hybrid"
@dataclass
class ConnectionWeight:
source: str
target: str
weight: float
latency: float
bandwidth: float
reliability: float
class NetworkTopology:
"""Manages and optimizes network topology"""
def __init__(self, discovery: P2PDiscovery, health_monitor: PeerHealthMonitor):
self.discovery = discovery
self.health_monitor = health_monitor
self.graph = nx.Graph()
self.strategy = TopologyStrategy.HYBRID
self.optimization_interval = 300 # 5 minutes
self.max_degree = 8
self.min_degree = 3
self.running = False
# Topology metrics
self.avg_path_length = 0
self.clustering_coefficient = 0
self.network_efficiency = 0
async def start_optimization(self):
"""Start topology optimization service"""
self.running = True
log_info("Starting network topology optimization")
# Initialize graph
await self._build_initial_graph()
while self.running:
try:
await self._optimize_topology()
await self._calculate_metrics()
await asyncio.sleep(self.optimization_interval)
except Exception as e:
log_error(f"Topology optimization error: {e}")
await asyncio.sleep(30)
async def stop_optimization(self):
"""Stop topology optimization service"""
self.running = False
log_info("Stopping network topology optimization")
async def _build_initial_graph(self):
"""Build initial network graph from current peers"""
self.graph.clear()
# Add all peers as nodes
for peer in self.discovery.get_peer_list():
self.graph.add_node(peer.node_id, **{
'address': peer.address,
'port': peer.port,
'reputation': peer.reputation,
'capabilities': peer.capabilities
})
# Add edges based on current connections
await self._add_connection_edges()
async def _add_connection_edges(self):
"""Add edges for current peer connections"""
peers = self.discovery.get_peer_list()
# In a real implementation, this would use actual connection data
# For now, create a mesh topology
for i, peer1 in enumerate(peers):
for peer2 in peers[i+1:]:
if self._should_connect(peer1, peer2):
weight = await self._calculate_connection_weight(peer1, peer2)
self.graph.add_edge(peer1.node_id, peer2.node_id, weight=weight)
def _should_connect(self, peer1: PeerNode, peer2: PeerNode) -> bool:
"""Determine if two peers should be connected"""
# Check degree constraints
if (self.graph.degree(peer1.node_id) >= self.max_degree or
self.graph.degree(peer2.node_id) >= self.max_degree):
return False
# Check strategy-specific rules
if self.strategy == TopologyStrategy.SMALL_WORLD:
return self._small_world_should_connect(peer1, peer2)
elif self.strategy == TopologyStrategy.SCALE_FREE:
return self._scale_free_should_connect(peer1, peer2)
elif self.strategy == TopologyStrategy.MESH:
return self._mesh_should_connect(peer1, peer2)
elif self.strategy == TopologyStrategy.HYBRID:
return self._hybrid_should_connect(peer1, peer2)
return False
def _small_world_should_connect(self, peer1: PeerNode, peer2: PeerNode) -> bool:
"""Small world topology connection logic"""
# Connect to nearby peers and some random long-range connections
import random
if random.random() < 0.1: # 10% random connections
return True
# Connect based on geographic or network proximity (simplified)
return random.random() < 0.3 # 30% of nearby connections
def _scale_free_should_connect(self, peer1: PeerNode, peer2: PeerNode) -> bool:
"""Scale-free topology connection logic"""
# Prefer connecting to high-degree nodes (rich-get-richer)
degree1 = self.graph.degree(peer1.node_id)
degree2 = self.graph.degree(peer2.node_id)
# Higher probability for nodes with higher degree
connection_probability = (degree1 + degree2) / (2 * self.max_degree)
return random.random() < connection_probability
def _mesh_should_connect(self, peer1: PeerNode, peer2: PeerNode) -> bool:
"""Full mesh topology connection logic"""
# Connect to all peers (within degree limits)
return True
def _hybrid_should_connect(self, peer1: PeerNode, peer2: PeerNode) -> bool:
"""Hybrid topology connection logic"""
# Combine multiple strategies
import random
# 40% small world, 30% scale-free, 30% mesh
strategy_choice = random.random()
if strategy_choice < 0.4:
return self._small_world_should_connect(peer1, peer2)
elif strategy_choice < 0.7:
return self._scale_free_should_connect(peer1, peer2)
else:
return self._mesh_should_connect(peer1, peer2)
async def _calculate_connection_weight(self, peer1: PeerNode, peer2: PeerNode) -> float:
"""Calculate connection weight between two peers"""
# Get health metrics
health1 = self.health_monitor.get_health_status(peer1.node_id)
health2 = self.health_monitor.get_health_status(peer2.node_id)
# Calculate weight based on health, reputation, and performance
weight = 1.0
if health1 and health2:
# Factor in health scores
weight *= (health1.health_score + health2.health_score) / 2
# Factor in reputation
weight *= (peer1.reputation + peer2.reputation) / 2
# Factor in latency (inverse relationship)
if health1 and health1.latency_ms > 0:
weight *= min(1.0, 1000 / health1.latency_ms)
return max(0.1, weight) # Minimum weight of 0.1
async def _optimize_topology(self):
"""Optimize network topology"""
log_info("Optimizing network topology")
# Analyze current topology
await self._analyze_topology()
# Identify optimization opportunities
improvements = await self._identify_improvements()
# Apply improvements
for improvement in improvements:
await self._apply_improvement(improvement)
async def _analyze_topology(self):
"""Analyze current network topology"""
if len(self.graph.nodes()) == 0:
return
# Calculate basic metrics
if nx.is_connected(self.graph):
self.avg_path_length = nx.average_shortest_path_length(self.graph, weight='weight')
else:
self.avg_path_length = float('inf')
self.clustering_coefficient = nx.average_clustering(self.graph)
# Calculate network efficiency
self.network_efficiency = nx.global_efficiency(self.graph)
log_info(f"Topology metrics - Path length: {self.avg_path_length:.2f}, "
f"Clustering: {self.clustering_coefficient:.2f}, "
f"Efficiency: {self.network_efficiency:.2f}")
async def _identify_improvements(self) -> List[Dict]:
"""Identify topology improvements"""
improvements = []
# Check for disconnected nodes
if not nx.is_connected(self.graph):
components = list(nx.connected_components(self.graph))
if len(components) > 1:
improvements.append({
'type': 'connect_components',
'components': components
})
# Check degree distribution
degrees = dict(self.graph.degree())
low_degree_nodes = [node for node, degree in degrees.items() if degree < self.min_degree]
high_degree_nodes = [node for node, degree in degrees.items() if degree > self.max_degree]
if low_degree_nodes:
improvements.append({
'type': 'increase_degree',
'nodes': low_degree_nodes
})
if high_degree_nodes:
improvements.append({
'type': 'decrease_degree',
'nodes': high_degree_nodes
})
# Check for inefficient paths
if self.avg_path_length > 6: # Too many hops
improvements.append({
'type': 'add_shortcuts',
'target_path_length': 4
})
return improvements
async def _apply_improvement(self, improvement: Dict):
"""Apply topology improvement"""
improvement_type = improvement['type']
if improvement_type == 'connect_components':
await self._connect_components(improvement['components'])
elif improvement_type == 'increase_degree':
await self._increase_node_degree(improvement['nodes'])
elif improvement_type == 'decrease_degree':
await self._decrease_node_degree(improvement['nodes'])
elif improvement_type == 'add_shortcuts':
await self._add_shortcuts(improvement['target_path_length'])
async def _connect_components(self, components: List[Set[str]]):
"""Connect disconnected components"""
log_info(f"Connecting {len(components)} disconnected components")
# Connect components by adding edges between representative nodes
for i in range(len(components) - 1):
component1 = list(components[i])
component2 = list(components[i + 1])
# Select best nodes to connect
node1 = self._select_best_connection_node(component1)
node2 = self._select_best_connection_node(component2)
# Add connection
if node1 and node2:
peer1 = self.discovery.peers.get(node1)
peer2 = self.discovery.peers.get(node2)
if peer1 and peer2:
await self._establish_connection(peer1, peer2)
async def _increase_node_degree(self, nodes: List[str]):
"""Increase degree of low-degree nodes"""
for node_id in nodes:
peer = self.discovery.peers.get(node_id)
if not peer:
continue
# Find best candidates for connection
candidates = await self._find_connection_candidates(peer, max_connections=2)
for candidate_peer in candidates:
await self._establish_connection(peer, candidate_peer)
async def _decrease_node_degree(self, nodes: List[str]):
"""Decrease degree of high-degree nodes"""
for node_id in nodes:
# Remove lowest quality connections
edges = list(self.graph.edges(node_id, data=True))
# Sort by weight (lowest first)
edges.sort(key=lambda x: x[2].get('weight', 1.0))
# Remove excess connections
excess_count = self.graph.degree(node_id) - self.max_degree
for i in range(min(excess_count, len(edges))):
edge = edges[i]
await self._remove_connection(edge[0], edge[1])
async def _add_shortcuts(self, target_path_length: float):
"""Add shortcut connections to reduce path length"""
# Find pairs of nodes with long shortest paths
all_pairs = dict(nx.all_pairs_shortest_path_length(self.graph))
long_paths = []
for node1, paths in all_pairs.items():
for node2, distance in paths.items():
if node1 != node2 and distance > target_path_length:
long_paths.append((node1, node2, distance))
# Sort by path length (longest first)
long_paths.sort(key=lambda x: x[2], reverse=True)
# Add shortcuts for longest paths
for node1_id, node2_id, _ in long_paths[:5]: # Limit to 5 shortcuts
peer1 = self.discovery.peers.get(node1_id)
peer2 = self.discovery.peers.get(node2_id)
if peer1 and peer2 and not self.graph.has_edge(node1_id, node2_id):
await self._establish_connection(peer1, peer2)
def _select_best_connection_node(self, nodes: List[str]) -> Optional[str]:
"""Select best node for inter-component connection"""
best_node = None
best_score = 0
for node_id in nodes:
peer = self.discovery.peers.get(node_id)
if not peer:
continue
# Score based on reputation and health
health = self.health_monitor.get_health_status(node_id)
score = peer.reputation
if health:
score *= health.health_score
if score > best_score:
best_score = score
best_node = node_id
return best_node
async def _find_connection_candidates(self, peer: PeerNode, max_connections: int = 3) -> List[PeerNode]:
"""Find best candidates for new connections"""
candidates = []
for candidate_peer in self.discovery.get_peer_list():
if (candidate_peer.node_id == peer.node_id or
self.graph.has_edge(peer.node_id, candidate_peer.node_id)):
continue
# Score candidate
score = await self._calculate_connection_weight(peer, candidate_peer)
candidates.append((candidate_peer, score))
# Sort by score and return top candidates
candidates.sort(key=lambda x: x[1], reverse=True)
return [candidate for candidate, _ in candidates[:max_connections]]
async def _establish_connection(self, peer1: PeerNode, peer2: PeerNode):
"""Establish connection between two peers"""
try:
# In a real implementation, this would establish actual network connection
weight = await self._calculate_connection_weight(peer1, peer2)
self.graph.add_edge(peer1.node_id, peer2.node_id, weight=weight)
log_info(f"Established connection between {peer1.node_id} and {peer2.node_id}")
except Exception as e:
log_error(f"Failed to establish connection between {peer1.node_id} and {peer2.node_id}: {e}")
async def _remove_connection(self, node1_id: str, node2_id: str):
"""Remove connection between two nodes"""
try:
if self.graph.has_edge(node1_id, node2_id):
self.graph.remove_edge(node1_id, node2_id)
log_info(f"Removed connection between {node1_id} and {node2_id}")
except Exception as e:
log_error(f"Failed to remove connection between {node1_id} and {node2_id}: {e}")
def get_topology_metrics(self) -> Dict:
"""Get current topology metrics"""
return {
'node_count': len(self.graph.nodes()),
'edge_count': len(self.graph.edges()),
'avg_degree': sum(dict(self.graph.degree()).values()) / len(self.graph.nodes()) if self.graph.nodes() else 0,
'avg_path_length': self.avg_path_length,
'clustering_coefficient': self.clustering_coefficient,
'network_efficiency': self.network_efficiency,
'is_connected': nx.is_connected(self.graph),
'strategy': self.strategy.value
}
def get_visualization_data(self) -> Dict:
"""Get data for network visualization"""
nodes = []
edges = []
for node_id in self.graph.nodes():
node_data = self.graph.nodes[node_id]
peer = self.discovery.peers.get(node_id)
nodes.append({
'id': node_id,
'address': node_data.get('address', ''),
'reputation': node_data.get('reputation', 0),
'degree': self.graph.degree(node_id)
})
for edge in self.graph.edges(data=True):
edges.append({
'source': edge[0],
'target': edge[1],
'weight': edge[2].get('weight', 1.0)
})
return {
'nodes': nodes,
'edges': edges
}
# Global topology manager
topology_manager: Optional[NetworkTopology] = None
def get_topology_manager() -> Optional[NetworkTopology]:
"""Get global topology manager"""
return topology_manager
def create_topology_manager(discovery: P2PDiscovery, health_monitor: PeerHealthMonitor) -> NetworkTopology:
"""Create and set global topology manager"""
global topology_manager
topology_manager = NetworkTopology(discovery, health_monitor)
return topology_manager