claude-mpm 4.1.4__py3-none-any.whl → 4.1.6__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- claude_mpm/VERSION +1 -1
- claude_mpm/agents/templates/research.json +39 -13
- claude_mpm/cli/__init__.py +2 -0
- claude_mpm/cli/commands/__init__.py +2 -0
- claude_mpm/cli/commands/configure.py +1221 -0
- claude_mpm/cli/commands/configure_tui.py +1921 -0
- claude_mpm/cli/commands/tickets.py +365 -784
- claude_mpm/cli/parsers/base_parser.py +7 -0
- claude_mpm/cli/parsers/configure_parser.py +119 -0
- claude_mpm/cli/startup_logging.py +39 -12
- claude_mpm/constants.py +1 -0
- claude_mpm/core/output_style_manager.py +24 -0
- claude_mpm/core/socketio_pool.py +35 -3
- claude_mpm/core/unified_agent_registry.py +46 -15
- claude_mpm/dashboard/static/css/connection-status.css +370 -0
- claude_mpm/dashboard/static/js/components/connection-debug.js +654 -0
- claude_mpm/dashboard/static/js/connection-manager.js +536 -0
- claude_mpm/dashboard/templates/index.html +11 -0
- claude_mpm/hooks/claude_hooks/services/__init__.py +3 -1
- claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +190 -0
- claude_mpm/services/agents/deployment/agent_discovery_service.py +12 -3
- claude_mpm/services/agents/deployment/agent_lifecycle_manager.py +172 -233
- claude_mpm/services/agents/deployment/agent_lifecycle_manager_refactored.py +575 -0
- claude_mpm/services/agents/deployment/agent_operation_service.py +573 -0
- claude_mpm/services/agents/deployment/agent_record_service.py +419 -0
- claude_mpm/services/agents/deployment/agent_state_service.py +381 -0
- claude_mpm/services/agents/deployment/multi_source_deployment_service.py +4 -2
- claude_mpm/services/diagnostics/checks/__init__.py +2 -0
- claude_mpm/services/diagnostics/checks/instructions_check.py +418 -0
- claude_mpm/services/diagnostics/diagnostic_runner.py +15 -2
- claude_mpm/services/event_bus/direct_relay.py +173 -0
- claude_mpm/services/infrastructure/__init__.py +31 -5
- claude_mpm/services/infrastructure/monitoring/__init__.py +43 -0
- claude_mpm/services/infrastructure/monitoring/aggregator.py +437 -0
- claude_mpm/services/infrastructure/monitoring/base.py +130 -0
- claude_mpm/services/infrastructure/monitoring/legacy.py +203 -0
- claude_mpm/services/infrastructure/monitoring/network.py +218 -0
- claude_mpm/services/infrastructure/monitoring/process.py +342 -0
- claude_mpm/services/infrastructure/monitoring/resources.py +243 -0
- claude_mpm/services/infrastructure/monitoring/service.py +367 -0
- claude_mpm/services/infrastructure/monitoring.py +67 -1030
- claude_mpm/services/project/analyzer.py +13 -4
- claude_mpm/services/project/analyzer_refactored.py +450 -0
- claude_mpm/services/project/analyzer_v2.py +566 -0
- claude_mpm/services/project/architecture_analyzer.py +461 -0
- claude_mpm/services/project/dependency_analyzer.py +462 -0
- claude_mpm/services/project/language_analyzer.py +265 -0
- claude_mpm/services/project/metrics_collector.py +410 -0
- claude_mpm/services/socketio/handlers/connection_handler.py +345 -0
- claude_mpm/services/socketio/server/broadcaster.py +32 -1
- claude_mpm/services/socketio/server/connection_manager.py +516 -0
- claude_mpm/services/socketio/server/core.py +63 -0
- claude_mpm/services/socketio/server/eventbus_integration.py +20 -9
- claude_mpm/services/socketio/server/main.py +27 -1
- claude_mpm/services/ticket_manager.py +5 -1
- claude_mpm/services/ticket_services/__init__.py +26 -0
- claude_mpm/services/ticket_services/crud_service.py +328 -0
- claude_mpm/services/ticket_services/formatter_service.py +290 -0
- claude_mpm/services/ticket_services/search_service.py +324 -0
- claude_mpm/services/ticket_services/validation_service.py +303 -0
- claude_mpm/services/ticket_services/workflow_service.py +244 -0
- {claude_mpm-4.1.4.dist-info → claude_mpm-4.1.6.dist-info}/METADATA +3 -1
- {claude_mpm-4.1.4.dist-info → claude_mpm-4.1.6.dist-info}/RECORD +67 -46
- claude_mpm/agents/OUTPUT_STYLE.md +0 -73
- claude_mpm/agents/backups/INSTRUCTIONS.md +0 -352
- claude_mpm/agents/templates/OPTIMIZATION_REPORT.md +0 -156
- claude_mpm/agents/templates/backup/data_engineer_agent_20250726_234551.json +0 -79
- claude_mpm/agents/templates/backup/documentation_agent_20250726_234551.json +0 -68
- claude_mpm/agents/templates/backup/engineer_agent_20250726_234551.json +0 -77
- claude_mpm/agents/templates/backup/ops_agent_20250726_234551.json +0 -78
- claude_mpm/agents/templates/backup/qa_agent_20250726_234551.json +0 -67
- claude_mpm/agents/templates/backup/research_agent_2025011_234551.json +0 -88
- claude_mpm/agents/templates/backup/research_agent_20250726_234551.json +0 -72
- claude_mpm/agents/templates/backup/research_memory_efficient.json +0 -88
- claude_mpm/agents/templates/backup/security_agent_20250726_234551.json +0 -78
- claude_mpm/agents/templates/backup/version_control_agent_20250726_234551.json +0 -62
- claude_mpm/agents/templates/vercel_ops_instructions.md +0 -582
- {claude_mpm-4.1.4.dist-info → claude_mpm-4.1.6.dist-info}/WHEEL +0 -0
- {claude_mpm-4.1.4.dist-info → claude_mpm-4.1.6.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.1.4.dist-info → claude_mpm-4.1.6.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.1.4.dist-info → claude_mpm-4.1.6.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
"""Legacy compatibility wrappers for monitoring classes.
|
|
2
|
+
|
|
3
|
+
Provides backward compatibility for existing code using the old monitoring API.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from typing import Any, Dict, List, Optional
|
|
7
|
+
|
|
8
|
+
from .aggregator import MonitoringAggregatorService
|
|
9
|
+
from .base import HealthChecker, HealthMetric
|
|
10
|
+
from .network import NetworkHealthService
|
|
11
|
+
from .process import ProcessHealthService
|
|
12
|
+
from .service import ServiceHealthService
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ProcessResourceChecker(HealthChecker):
|
|
16
|
+
"""Legacy wrapper for ProcessHealthService.
|
|
17
|
+
|
|
18
|
+
Maintains backward compatibility with existing code.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
def __init__(
|
|
22
|
+
self,
|
|
23
|
+
pid: int,
|
|
24
|
+
cpu_threshold: float = 80.0,
|
|
25
|
+
memory_threshold_mb: int = 500,
|
|
26
|
+
fd_threshold: int = 1000,
|
|
27
|
+
):
|
|
28
|
+
"""Initialize legacy process resource checker."""
|
|
29
|
+
self.service = ProcessHealthService(
|
|
30
|
+
pid=pid,
|
|
31
|
+
cpu_threshold=cpu_threshold,
|
|
32
|
+
memory_threshold_mb=memory_threshold_mb,
|
|
33
|
+
fd_threshold=fd_threshold,
|
|
34
|
+
)
|
|
35
|
+
self.pid = pid
|
|
36
|
+
|
|
37
|
+
def get_name(self) -> str:
|
|
38
|
+
"""Get checker name."""
|
|
39
|
+
return f"process_resources_{self.pid}"
|
|
40
|
+
|
|
41
|
+
async def check_health(self) -> List[HealthMetric]:
|
|
42
|
+
"""Perform health check."""
|
|
43
|
+
return await self.service.check_health()
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class NetworkConnectivityChecker(HealthChecker):
|
|
47
|
+
"""Legacy wrapper for NetworkHealthService.
|
|
48
|
+
|
|
49
|
+
Maintains backward compatibility with existing code.
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
def __init__(self, host: str, port: int, timeout: float = 1.0):
|
|
53
|
+
"""Initialize legacy network connectivity checker."""
|
|
54
|
+
self.service = NetworkHealthService(default_timeout=timeout)
|
|
55
|
+
self.service.add_endpoint(host, port, f"{host}_{port}", timeout)
|
|
56
|
+
self.host = host
|
|
57
|
+
self.port = port
|
|
58
|
+
self.timeout = timeout
|
|
59
|
+
|
|
60
|
+
def get_name(self) -> str:
|
|
61
|
+
"""Get checker name."""
|
|
62
|
+
return f"network_connectivity_{self.host}_{self.port}"
|
|
63
|
+
|
|
64
|
+
async def check_health(self) -> List[HealthMetric]:
|
|
65
|
+
"""Perform health check."""
|
|
66
|
+
# Filter to only return metrics for the configured endpoint
|
|
67
|
+
all_metrics = await self.service.check_health()
|
|
68
|
+
relevant_metrics = []
|
|
69
|
+
|
|
70
|
+
for metric in all_metrics:
|
|
71
|
+
# Include socket creation and metrics for our endpoint
|
|
72
|
+
if (
|
|
73
|
+
metric.name == "socket_creation"
|
|
74
|
+
or f"{self.host}_{self.port}" in metric.name
|
|
75
|
+
or "localhost_default" not in metric.name
|
|
76
|
+
):
|
|
77
|
+
# Rename metrics to match legacy format
|
|
78
|
+
if f"port_accessible_{self.host}_{self.port}" in metric.name:
|
|
79
|
+
metric.name = "port_accessible"
|
|
80
|
+
relevant_metrics.append(metric)
|
|
81
|
+
|
|
82
|
+
return relevant_metrics
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
class ServiceHealthChecker(HealthChecker):
|
|
86
|
+
"""Legacy wrapper for ServiceHealthService.
|
|
87
|
+
|
|
88
|
+
Maintains backward compatibility with existing code.
|
|
89
|
+
"""
|
|
90
|
+
|
|
91
|
+
def __init__(
|
|
92
|
+
self,
|
|
93
|
+
service_stats: Dict[str, Any],
|
|
94
|
+
max_clients: int = 1000,
|
|
95
|
+
max_error_rate: float = 0.1,
|
|
96
|
+
):
|
|
97
|
+
"""Initialize legacy service health checker."""
|
|
98
|
+
self.service = ServiceHealthService(
|
|
99
|
+
service_stats=service_stats,
|
|
100
|
+
max_clients=max_clients,
|
|
101
|
+
max_error_rate=max_error_rate,
|
|
102
|
+
)
|
|
103
|
+
self.service_stats = service_stats
|
|
104
|
+
self.max_clients = max_clients
|
|
105
|
+
self.max_error_rate = max_error_rate
|
|
106
|
+
self.last_check_time = self.service.last_check_time
|
|
107
|
+
self.last_events_processed = self.service.last_events_processed
|
|
108
|
+
|
|
109
|
+
def get_name(self) -> str:
|
|
110
|
+
"""Get checker name."""
|
|
111
|
+
return "service_health"
|
|
112
|
+
|
|
113
|
+
async def check_health(self) -> List[HealthMetric]:
|
|
114
|
+
"""Perform health check."""
|
|
115
|
+
metrics = await self.service.check_health()
|
|
116
|
+
# Update exposed state for compatibility
|
|
117
|
+
self.last_check_time = self.service.last_check_time
|
|
118
|
+
self.last_events_processed = self.service.last_events_processed
|
|
119
|
+
return metrics
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
class AdvancedHealthMonitor:
|
|
123
|
+
"""Legacy wrapper for MonitoringAggregatorService.
|
|
124
|
+
|
|
125
|
+
Maintains backward compatibility with existing code.
|
|
126
|
+
"""
|
|
127
|
+
|
|
128
|
+
def __init__(self, config: Optional[Dict[str, Any]] = None):
|
|
129
|
+
"""Initialize legacy advanced health monitor."""
|
|
130
|
+
self.service = MonitoringAggregatorService(config)
|
|
131
|
+
|
|
132
|
+
# Expose service properties for compatibility
|
|
133
|
+
self.config = self.service.config
|
|
134
|
+
self.check_interval = self.service.check_interval
|
|
135
|
+
self.history_size = self.service.history_size
|
|
136
|
+
self.aggregation_window = self.service.aggregation_window
|
|
137
|
+
self.checkers = self.service.checkers
|
|
138
|
+
self.health_history = self.service.health_history
|
|
139
|
+
self.monitoring = self.service.monitoring
|
|
140
|
+
self.monitor_task = self.service.monitor_task
|
|
141
|
+
self.last_check_result = self.service.last_check_result
|
|
142
|
+
self.health_callbacks = self.service.health_callbacks
|
|
143
|
+
self.monitoring_stats = self.service.monitoring_stats
|
|
144
|
+
|
|
145
|
+
# Legacy logger
|
|
146
|
+
import logging
|
|
147
|
+
|
|
148
|
+
self.logger = logging.getLogger(f"{__name__}.AdvancedHealthMonitor")
|
|
149
|
+
|
|
150
|
+
def add_checker(self, checker: HealthChecker) -> None:
|
|
151
|
+
"""Add a health checker."""
|
|
152
|
+
self.service.add_checker(checker)
|
|
153
|
+
self.checkers = self.service.checkers
|
|
154
|
+
|
|
155
|
+
def add_health_callback(self, callback) -> None:
|
|
156
|
+
"""Add health callback."""
|
|
157
|
+
self.service.add_health_callback(callback)
|
|
158
|
+
self.health_callbacks = self.service.health_callbacks
|
|
159
|
+
|
|
160
|
+
async def perform_health_check(self):
|
|
161
|
+
"""Perform health check."""
|
|
162
|
+
result = await self.service.perform_health_check()
|
|
163
|
+
# Update exposed state
|
|
164
|
+
self.health_history = self.service.health_history
|
|
165
|
+
self.last_check_result = self.service.last_check_result
|
|
166
|
+
self.monitoring_stats = self.service.monitoring_stats
|
|
167
|
+
return result
|
|
168
|
+
|
|
169
|
+
def _determine_overall_status(self, metrics):
|
|
170
|
+
"""Determine overall status."""
|
|
171
|
+
return self.service._determine_overall_status(metrics)
|
|
172
|
+
|
|
173
|
+
def start_monitoring(self) -> None:
|
|
174
|
+
"""Start monitoring."""
|
|
175
|
+
self.service.start_monitoring()
|
|
176
|
+
self.monitoring = self.service.monitoring
|
|
177
|
+
self.monitor_task = self.service.monitor_task
|
|
178
|
+
|
|
179
|
+
async def stop_monitoring(self) -> None:
|
|
180
|
+
"""Stop monitoring."""
|
|
181
|
+
await self.service.stop_monitoring()
|
|
182
|
+
self.monitoring = self.service.monitoring
|
|
183
|
+
self.monitor_task = self.service.monitor_task
|
|
184
|
+
|
|
185
|
+
async def _monitoring_loop(self) -> None:
|
|
186
|
+
"""Monitoring loop (should not be called directly)."""
|
|
187
|
+
await self.service._monitoring_loop()
|
|
188
|
+
|
|
189
|
+
def get_current_status(self):
|
|
190
|
+
"""Get current status."""
|
|
191
|
+
return self.service.get_current_status()
|
|
192
|
+
|
|
193
|
+
def get_health_history(self, limit=None):
|
|
194
|
+
"""Get health history."""
|
|
195
|
+
return self.service.get_health_history(limit)
|
|
196
|
+
|
|
197
|
+
def get_aggregated_status(self, window_seconds=None):
|
|
198
|
+
"""Get aggregated status."""
|
|
199
|
+
return self.service.get_aggregated_status(window_seconds)
|
|
200
|
+
|
|
201
|
+
def export_diagnostics(self):
|
|
202
|
+
"""Export diagnostics."""
|
|
203
|
+
return self.service.export_diagnostics()
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
"""Network health monitoring service.
|
|
2
|
+
|
|
3
|
+
Monitors network connectivity, port availability, and socket health.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import socket
|
|
7
|
+
from typing import Dict, List, Optional
|
|
8
|
+
|
|
9
|
+
from .base import BaseMonitoringService, HealthMetric, HealthStatus
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class NetworkHealthService(BaseMonitoringService):
|
|
13
|
+
"""Service for monitoring network health and connectivity.
|
|
14
|
+
|
|
15
|
+
Monitors:
|
|
16
|
+
- Port availability and binding
|
|
17
|
+
- Socket creation capability
|
|
18
|
+
- Network interface status
|
|
19
|
+
- Connection health to specified endpoints
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
def __init__(self, default_timeout: float = 1.0):
|
|
23
|
+
"""Initialize network health service.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
default_timeout: Default connection timeout in seconds
|
|
27
|
+
"""
|
|
28
|
+
super().__init__("NetworkHealth")
|
|
29
|
+
self.default_timeout = default_timeout
|
|
30
|
+
self.monitored_endpoints: List[Dict[str, any]] = []
|
|
31
|
+
|
|
32
|
+
def add_endpoint(
|
|
33
|
+
self,
|
|
34
|
+
host: str,
|
|
35
|
+
port: int,
|
|
36
|
+
name: Optional[str] = None,
|
|
37
|
+
timeout: Optional[float] = None,
|
|
38
|
+
) -> None:
|
|
39
|
+
"""Add an endpoint to monitor.
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
host: Host address to monitor
|
|
43
|
+
port: Port number to monitor
|
|
44
|
+
name: Optional name for the endpoint
|
|
45
|
+
timeout: Optional custom timeout for this endpoint
|
|
46
|
+
"""
|
|
47
|
+
endpoint = {
|
|
48
|
+
"host": host,
|
|
49
|
+
"port": port,
|
|
50
|
+
"name": name or f"{host}:{port}",
|
|
51
|
+
"timeout": timeout or self.default_timeout,
|
|
52
|
+
}
|
|
53
|
+
self.monitored_endpoints.append(endpoint)
|
|
54
|
+
self.logger.info(f"Added endpoint for monitoring: {endpoint['name']}")
|
|
55
|
+
|
|
56
|
+
async def check_health(self) -> List[HealthMetric]:
|
|
57
|
+
"""Check network health for all configured endpoints."""
|
|
58
|
+
metrics = []
|
|
59
|
+
|
|
60
|
+
# Check socket creation capability
|
|
61
|
+
metrics.extend(self._check_socket_creation())
|
|
62
|
+
|
|
63
|
+
# Check all monitored endpoints
|
|
64
|
+
for endpoint in self.monitored_endpoints:
|
|
65
|
+
metrics.extend(
|
|
66
|
+
self._check_endpoint(
|
|
67
|
+
endpoint["host"],
|
|
68
|
+
endpoint["port"],
|
|
69
|
+
endpoint["name"],
|
|
70
|
+
endpoint["timeout"],
|
|
71
|
+
)
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
# Check localhost connectivity (always check)
|
|
75
|
+
if not any(e["host"] == "localhost" for e in self.monitored_endpoints):
|
|
76
|
+
metrics.extend(
|
|
77
|
+
self._check_endpoint("localhost", 8765, "localhost_default", 0.5)
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
return metrics
|
|
81
|
+
|
|
82
|
+
def _check_socket_creation(self) -> List[HealthMetric]:
|
|
83
|
+
"""Check if we can create sockets (resource availability)."""
|
|
84
|
+
metrics = []
|
|
85
|
+
try:
|
|
86
|
+
test_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
87
|
+
test_sock.close()
|
|
88
|
+
metrics.append(
|
|
89
|
+
HealthMetric(
|
|
90
|
+
name="socket_creation",
|
|
91
|
+
value=True,
|
|
92
|
+
status=HealthStatus.HEALTHY,
|
|
93
|
+
message="Socket creation successful",
|
|
94
|
+
)
|
|
95
|
+
)
|
|
96
|
+
except Exception as e:
|
|
97
|
+
metrics.append(
|
|
98
|
+
HealthMetric(
|
|
99
|
+
name="socket_creation",
|
|
100
|
+
value=False,
|
|
101
|
+
status=HealthStatus.CRITICAL,
|
|
102
|
+
message=f"Failed to create socket: {e}",
|
|
103
|
+
)
|
|
104
|
+
)
|
|
105
|
+
return metrics
|
|
106
|
+
|
|
107
|
+
def _check_endpoint(
|
|
108
|
+
self,
|
|
109
|
+
host: str,
|
|
110
|
+
port: int,
|
|
111
|
+
name: str,
|
|
112
|
+
timeout: float,
|
|
113
|
+
) -> List[HealthMetric]:
|
|
114
|
+
"""Check connectivity to a specific endpoint.
|
|
115
|
+
|
|
116
|
+
Args:
|
|
117
|
+
host: Host to check
|
|
118
|
+
port: Port to check
|
|
119
|
+
name: Endpoint name for metrics
|
|
120
|
+
timeout: Connection timeout
|
|
121
|
+
|
|
122
|
+
Returns:
|
|
123
|
+
List of health metrics for the endpoint
|
|
124
|
+
"""
|
|
125
|
+
metrics = []
|
|
126
|
+
metric_name = f"port_accessible_{name}"
|
|
127
|
+
|
|
128
|
+
try:
|
|
129
|
+
# Try to connect to the port
|
|
130
|
+
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
131
|
+
sock.settimeout(timeout)
|
|
132
|
+
result = sock.connect_ex((host, port))
|
|
133
|
+
sock.close()
|
|
134
|
+
|
|
135
|
+
if result == 0:
|
|
136
|
+
metrics.append(
|
|
137
|
+
HealthMetric(
|
|
138
|
+
name=metric_name,
|
|
139
|
+
value=True,
|
|
140
|
+
status=HealthStatus.HEALTHY,
|
|
141
|
+
message=f"Port {port} is accessible on {host}",
|
|
142
|
+
)
|
|
143
|
+
)
|
|
144
|
+
else:
|
|
145
|
+
# Determine if this is critical or warning based on endpoint type
|
|
146
|
+
status = (
|
|
147
|
+
HealthStatus.WARNING
|
|
148
|
+
if "optional" in name.lower()
|
|
149
|
+
else HealthStatus.CRITICAL
|
|
150
|
+
)
|
|
151
|
+
metrics.append(
|
|
152
|
+
HealthMetric(
|
|
153
|
+
name=metric_name,
|
|
154
|
+
value=False,
|
|
155
|
+
status=status,
|
|
156
|
+
message=f"Port {port} is not accessible on {host} (error: {result})",
|
|
157
|
+
)
|
|
158
|
+
)
|
|
159
|
+
except socket.timeout:
|
|
160
|
+
metrics.append(
|
|
161
|
+
HealthMetric(
|
|
162
|
+
name=metric_name,
|
|
163
|
+
value=False,
|
|
164
|
+
status=HealthStatus.WARNING,
|
|
165
|
+
message=f"Connection to {host}:{port} timed out after {timeout}s",
|
|
166
|
+
)
|
|
167
|
+
)
|
|
168
|
+
except Exception as e:
|
|
169
|
+
metrics.append(
|
|
170
|
+
HealthMetric(
|
|
171
|
+
name=metric_name,
|
|
172
|
+
value=False,
|
|
173
|
+
status=HealthStatus.UNKNOWN,
|
|
174
|
+
message=f"Error checking port accessibility: {e}",
|
|
175
|
+
)
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
return metrics
|
|
179
|
+
|
|
180
|
+
def check_port_available(self, port: int, host: str = "0.0.0.0") -> bool:
|
|
181
|
+
"""Check if a port is available for binding.
|
|
182
|
+
|
|
183
|
+
Args:
|
|
184
|
+
port: Port number to check
|
|
185
|
+
host: Host interface to check
|
|
186
|
+
|
|
187
|
+
Returns:
|
|
188
|
+
True if port is available for binding
|
|
189
|
+
"""
|
|
190
|
+
try:
|
|
191
|
+
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
|
|
192
|
+
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
|
193
|
+
sock.bind((host, port))
|
|
194
|
+
return True
|
|
195
|
+
except OSError:
|
|
196
|
+
return False
|
|
197
|
+
|
|
198
|
+
def scan_port_range(
|
|
199
|
+
self,
|
|
200
|
+
start_port: int,
|
|
201
|
+
end_port: int,
|
|
202
|
+
host: str = "0.0.0.0",
|
|
203
|
+
) -> List[int]:
|
|
204
|
+
"""Scan a range of ports to find available ones.
|
|
205
|
+
|
|
206
|
+
Args:
|
|
207
|
+
start_port: Starting port number
|
|
208
|
+
end_port: Ending port number (inclusive)
|
|
209
|
+
host: Host interface to check
|
|
210
|
+
|
|
211
|
+
Returns:
|
|
212
|
+
List of available port numbers
|
|
213
|
+
"""
|
|
214
|
+
available_ports = []
|
|
215
|
+
for port in range(start_port, end_port + 1):
|
|
216
|
+
if self.check_port_available(port, host):
|
|
217
|
+
available_ports.append(port)
|
|
218
|
+
return available_ports
|