feat: deep link diagnostics via Pulse SSH
Adds comprehensive per-port link troubleshooting triggered from the Inspector panel when a port has an LLDP-identified server counterpart. - diagnose.py: DiagnosticsRunner with 15-section SSH command (carrier, operstate, sysfs counters, ethtool, ethtool -i/-a/-g/-S/-m, ip link, ip addr, ip route, dmesg, lldpctl); parsers for all sections; health analyzer with 14 check codes (NO_CARRIER, HALF_DUPLEX, SPEED_MISMATCH, SFP_RX_CRITICAL, CARRIER_FLAPPING, CRC_ERRORS_HIGH, LLDP_MISMATCH, etc.) - monitor.py: PulseClient now tracks last_execution_id so callers can link back to the raw Pulse execution URL - app.py: POST /api/diagnose + GET /api/diagnose/<job_id> with daemon thread background execution and 10-minute in-memory job store - inspector.html: "Run Link Diagnostics" button (shown only when LLDP host is resolvable); full results panel: health banner, physical layer, SFP/DOM with power bars, NIC error counters, collapsible ethtool -S, flow control/ring buffers, driver info, LLDP 2-col validation, collapsible dmesg, switch port summary, "View in Pulse" link - style.css: all .diag-* CSS classes with terminal aesthetic Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -239,6 +239,7 @@ class PulseClient:
|
||||
self.api_key = p.get('api_key', '')
|
||||
self.worker_id = p.get('worker_id', '')
|
||||
self.timeout = p.get('timeout', 45)
|
||||
self.last_execution_id: Optional[str] = None
|
||||
self.session = requests.Session()
|
||||
self.session.headers.update({
|
||||
'X-Gandalf-API-Key': self.api_key,
|
||||
@@ -247,6 +248,7 @@ class PulseClient:
|
||||
|
||||
def run_command(self, command: str) -> Optional[str]:
|
||||
"""Submit *command* to Pulse, poll until done, return stdout or None."""
|
||||
self.last_execution_id = None
|
||||
if not self.url or not self.api_key or not self.worker_id:
|
||||
return None
|
||||
try:
|
||||
@@ -257,6 +259,7 @@ class PulseClient:
|
||||
)
|
||||
resp.raise_for_status()
|
||||
execution_id = resp.json()['execution_id']
|
||||
self.last_execution_id = execution_id
|
||||
except Exception as e:
|
||||
logger.debug(f'Pulse command submit failed: {e}')
|
||||
return None
|
||||
|
||||
Reference in New Issue
Block a user