Commit graph

640 commits

Author SHA1 Message Date
a50c8e6314
Use datestamp as agent version 2026-02-09 13:51:32 -06:00
ae9b393137
Fix agent page jumping by using streams for real-time updates
- Convert agent_tokens from assign to stream to prevent full page re-renders
- Update handle_info callbacks to only update the specific agent that changed
- Only update stats when agent status changes, not device counts
- Prevents IP address from temporarily disappearing during updates
2026-02-09 13:39:59 -06:00
f8d2570bb3
no cluster unless in k8s, fix credo issues 2026-02-09 13:08:51 -06:00
94bcf8ac4e
fix bug on device add/edit page with mikrotik api fields showing 2026-02-09 13:01:47 -06:00
f7b5e63429
live update agent status 2026-02-09 12:58:54 -06:00
623fe07081
wip 2026-02-09 12:44:37 -06:00
2b4cb5c3f5
fix: handle duplicate MAC address entries gracefully
Problem: upsert_mac_address was using Repo.one() which crashes with
Ecto.MultipleResultsError when duplicate MAC address records exist
in the database.

Root Cause: Race condition or historical data corruption created
duplicate entries for the same (device_id, mac_address, vlan_id)
tuple, violating the unique constraint.

Fix:
- Changed Repo.one() to Repo.all() to handle duplicates gracefully
- When duplicates found: keep first entry, delete extras, log warning
- Includes device_id, mac_address, vlan_id, and duplicate IDs in log
- Auto-cleans corrupted data while maintaining service availability

Example Log Output:
  Found 1 duplicate MAC address entries, cleaning up
  device_id=f3ff19d5-b519-4327-9f7d-dc416e494cfa
  mac_address=78:9a:18:52:b1:c6
  vlan_id=nil
  duplicate_ids=[...]

Impact:
- Prevents polling crashes when duplicate MAC entries exist
- Self-healing: automatically cleans up database corruption
- Maintains data integrity while preserving service availability

Related: Issue 2.9 from bugs analysis (ARP/MAC parsing issues)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-09 09:20:08 -06:00
cedbc5abd1
fix: resolve critical data loss and protobuf schema bugs (Phase 1)
This commit addresses Phase 1 (CRITICAL - Data Loss Prevention) issues
from the comprehensive bugs and inconsistencies analysis.

CRITICAL FIXES:

1. Protobuf Schema Inconsistencies
   - Added missing job_id field (field 5) to SnmpResult message
   - Fixed AgentError message structure (job_id, message, timestamp)
   - Removed obsolete transport field from SnmpDevice struct
   - Regenerated protobuf code with protoc-gen-elixir
   - Prevents crashes when agent sends SNMP results or errors

2. Interface/Sensor Discovery Timeout Data Loss
   - Changed timeout behavior to return error instead of empty list
   - Prevents deletion of ALL existing interfaces/sensors on slow networks
   - Discovery fails gracefully instead of destroying historical data
   - Addresses commit 7a57f7c sensor discovery pipeline data loss issue

3. Polling Offset Schedule Mismatch
   - Fixed schedule_next_poll() to use offset only (not interval + offset)
   - Maintains consistent polling intervals across all executions
   - Prevents load bunching that offset was designed to prevent
   - Example: 300s interval with offset=94s now consistently polls every 5m

4. Always Reschedule Race Condition
   - Added should_continue_polling?() and should_continue_monitoring?()
   - Only reschedule if device is still enabled and should be polled by Phoenix
   - Prevents zombie jobs from continuing to poll disabled/deleted/reassigned devices
   - Stops race condition where stop_polling() is bypassed by in-flight jobs

Files Changed:
- priv/proto/agent.proto
- lib/towerops/proto/agent.pb.ex (regenerated)
- lib/towerops_web/channels/agent_channel.ex
- lib/towerops/snmp/discovery.ex
- lib/towerops/workers/device_poller_worker.ex
- lib/towerops/workers/device_monitor_worker.ex
- test/towerops/workers/device_poller_worker_test.exs

Test Results: All passing
- device_poller_worker_test.exs: 8 tests, 0 failures
- device_monitor_worker_test.exs: 3 tests, 0 failures
- discovery_test.exs: 13 tests, 0 failures

Remaining Phases:
- Phase 2 (HIGH): 8 data integrity issues
- Phase 3 (MEDIUM): 10 reliability issues
- Phase 4 (LOW): 3 cleanup issues

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-09 09:16:03 -06:00
b12a03b82f
update 2026-02-09 08:47:53 -06:00
7a57f7c9d2
fix: resolve critical sensor discovery pipeline data loss
Critical fixes to sensor discovery and display pipeline:

- Fix float value rejection: Change is_integer guards to is_number in
  vendor.ex, dynamic.ex to accept decimal sensor values (temperature,
  counters, etc.)
- Fix scale factor calculation: Rewrite base.ex divisor calculation
  using pure integer arithmetic to prevent rounding errors
- Add missing sensor type support: Current, power, fan, load, and
  signal quality sensor categories now filtered and assigned in UI
- Refactor resolve_snmp_community to reduce cyclomatic complexity

Impact:
- Sensors with float values (DHCP leases, connection counts, precise
  temperatures) no longer silently dropped before database
- ENTITY-SENSOR-MIB divisors calculated correctly without float errors
- UI coverage increased from ~35% to ~60% of discovered sensor types

Files modified:
- lib/towerops/snmp/profiles/vendors/vendor.ex
- lib/towerops/snmp/profiles/dynamic.ex
- lib/towerops/snmp/profiles/base.ex
- lib/towerops_web/live/device_live/show.ex
- lib/towerops/devices.ex

Documentation: SENSOR_PIPELINE_FIXES.md
2026-02-09 08:32:24 -06:00
9df53b1629
fix: suppress repetitive SNMP MIB resolution errors
Add logger filter to drop repetitive 'Cannot find module' errors
from net-snmp library. These errors occur when snmptranslate can't
find vendor-specific MIB modules but don't affect functionality
since the system falls back to numeric OIDs.

The errors were flooding production logs (hundreds of identical
messages) during polling of Morningstar devices that reference
TRISTAR MIB modules.

Changes:
- Add drop_snmp_mib_errors/2 filter to LoggerFilters module
- Register filter in production logger configuration
- Filter matches exact error format from snmptranslate stderr

The root cause (missing NIF binary) will be addressed separately
by compiling the towerops_nif.so during Docker build.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-08 14:09:26 -06:00
bc8e8a4261
feat: notify agents when devices are deleted
When a device is deleted, now broadcasts an assignment change event
to the agent (if assigned), causing the agent to receive an updated
job list without the deleted device. This ensures agents stop
processing any in-flight jobs for deleted devices.

Changes:
- Get agent assignment before deleting device
- Broadcast :device_deleted event to agent after deletion
- Agent channel receives broadcast and sends updated job list
- Agents naturally stop processing jobs for devices not in new list

Previously only cancelled Oban jobs but didn't notify agents,
leaving potential for agents to continue processing jobs for
deleted devices.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-08 14:06:09 -06:00
cbff651f83
fix: always redirect to /devices when stopping impersonation
Previously redirected to /dashboard or /orgs based on whether the
superuser had organizations. Now always goes to /devices for
consistency.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-08 14:01:15 -06:00
039bce3938
fix: add PING enum value to generated protobuf module
The protobuf Elixir module was missing the PING (value 4) enum
entry, causing encoding errors when building jobs for agents.

Manually added the field since protoc-gen-elixir has asdf version
issues. The generated module now matches the updated agent.proto
definition.

Fixes: ** (Protobuf.EncodeError) no function clause matching in
Towerops.Agent.JobType

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-08 13:49:07 -06:00
48dc4b852e
feat: add Phoenix support for agent-side ping monitoring
Enables Phoenix to send PING jobs to agents and receive monitoring
check results, fixing the "unknown" status for devices assigned to
local (non-cloud) agents.

Changes:
- Rename JobType::MONITOR to JobType::PING in protobuf for clarity
- Add validate_monitoring_check_message() to Validator module
- Add "monitoring_check" channel handler to receive ping results
- Implement store_monitoring_check() to save results to database
- Add build_ping_job() to generate PING jobs for agents
- Update build_jobs_for_device() to include ping jobs when
  monitoring_enabled=true

Phoenix now sends PING jobs alongside SNMP/MikroTik jobs, and agents
send back MonitoringCheck results that are stored in the
monitoring_checks table with proper agent_token_id tracking.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-08 13:44:21 -06:00
fb8ffde119
fix: prevent site assignment when sites are disabled for org
When an organization has sites disabled (use_sites=false), the API
now automatically removes any site_id from device creation requests,
ensuring devices are assigned directly to the organization instead.

This prevents Terraform (and other API clients) from creating devices
in sites when sites functionality is disabled for the organization.

Fixes issue where Terraform could bypass site restrictions via API.
2026-02-08 13:24:48 -06:00
b823b5e848
feat: display cloud poller name on device page
Show the specific cloud poller name (e.g., dfw, lon) alongside "Cloud"
when a device is assigned to a cloud poller. Displays as "Cloud (dfw)"
instead of just "Cloud" for better visibility of which poller is being used.

When no agent is assigned, continues to show just "Cloud".
2026-02-08 11:40:58 -06:00
10b1168044
refactor: round latency graph values to 1 decimal place
Display latency values with single decimal precision (e.g., 114.1ms)
for improved readability in graphs. This ensures consistency between
the raw chart data and tooltip formatting.
2026-02-08 11:13:39 -06:00
16b833d077
fix: correct LoggerFileBackend API usage to fix Oban startup
The previous commit (90e8964) introduced an incorrect LoggerBackends.add/2
call that prevented Oban from starting, causing all discovery jobs to remain
stuck in "available" state without being processed.

Root cause: LoggerBackends.add expects a tuple {module, id} as the first
argument, not the module alone with configuration options.

Fix: Use the correct API pattern:
1. Add backend with tuple identifier: {LoggerFileBackend, :file_log}
2. Configure separately with Logger.configure_backend/2

This resolves device discovery being completely broken - all background
jobs (discovery, polling, monitoring) now process correctly.
2026-02-08 11:07:45 -06:00
90e89641b7
fix logging backend 2026-02-08 10:55:08 -06:00
6b4c098de8
fix warnings 2026-02-08 10:50:27 -06:00
631d03536e
security: add session inactivity timeout and comprehensive security documentation
Session Security Enhancements:
- Add 30-minute inactivity timeout to UpdateSessionActivity plug
- Automatic logout with user notification after inactivity
- Session timeout check happens on every request
- Graceful logout with flash message
- Refactored for Credo compliance (reduced nesting depth)

Security Documentation:
- Create comprehensive SECURITY.md covering all security features
- Document authentication, authorization, and encryption
- List OWASP Top 10 mitigations
- Include security checklist for developers
- Document recent security fixes and improvements

Verification:
- Password reset already rate-limited (confirmed)
- All 6,145 tests passing
- Zero Credo issues
- Zero security warnings

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-08 10:47:44 -06:00
fb1d4c564f
security: fix critical vulnerabilities and atom exhaustion risks
Critical fixes:
- Add [:safe] option to binary_to_term to prevent RCE attacks
- Implement whitelist validation for String.to_atom conversions
- Add input validation before String.to_existing_atom usage

Changes:
- MIB compiler and cache: Use safe binary deserialization
- SNMP contexts: Whitelist protocol, device type, and source atoms
- API controllers: Validate error message keys before atom conversion
- Reduce function nesting to comply with Credo standards

All 6,145 tests passing with zero Credo issues.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-08 10:30:30 -06:00
d431931cfe
update hammer 2026-02-08 10:08:31 -06:00
8d4333b237
fix superadmin page 2026-02-07 11:58:56 -06:00
3931a9c14a
more tests and fixes 2026-02-07 11:50:18 -06:00
141c775230
fix: discovery retry, disabled assignment cascade, and silent spawn crashes
- Move last_discovery_at update to after successful processing so failed
  discoveries are retried on the next poll cycle instead of being blocked
  for 24 hours
- Add disabled assignment check to agent polling target query so devices
  with enabled=false assignments don't leak through site/org cascade
- Wrap spawned polling data processing in try/rescue so crashes are logged
  instead of silently disappearing
2026-02-07 09:17:28 -06:00
3eb3d4890e
Merge branch 'feature/job-monitoring-dashboard'
# Conflicts:
#	lib/towerops/workers/device_poller_worker.ex
#	lib/towerops/workers/discovery_worker.ex
2026-02-06 19:01:08 -06:00
137d7c83ec
test: add comprehensive integration tests for monitoring dashboard
Added integration tests for real-time updates and metrics display.
Also fixed dialyzer warnings in worker event broadcasting by removing
unreachable error handling branches.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-06 18:41:08 -06:00
070241d15a
feat: add queue depth display to health metrics
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-06 18:37:21 -06:00
1eaf6d9ac5
feat: build recent activity timeline with color-coded events
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-06 18:35:55 -06:00
8bceb6dce2
feat: build comprehensive health metrics panel with success rates
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-06 18:34:43 -06:00
1e45b1609b
feat: build detailed problems section UI for stuck and failed jobs
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-06 18:33:21 -06:00
daa223c49d
feat: enhance active operations display with device context
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-06 18:32:06 -06:00
2fb0101bab
feat: add job monitoring link to admin dashboard
Add Job Monitoring card to admin dashboard with:
- Hero icon for visual identification
- Brief description of monitoring capabilities
- Link to /admin/monitoring dashboard

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-06 18:28:41 -06:00
bb9e6f0b0c
feat: create MonitoringLive base structure with PubSub subscription
Add LiveView for real-time job monitoring dashboard:
- Subscribe to job:lifecycle PubSub topic for live updates
- Display health metrics (completed, failed, avg duration, active jobs)
- Show active operations with real-time updates
- Display problems section (stuck/failed job counts)
- Add router entry under /admin/monitoring

Tests verify PubSub subscription and basic rendering.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-06 18:27:40 -06:00
be073fa4b7
feat: integrate lifecycle event broadcasting into DiscoveryWorker
Add Events.broadcast_job_event calls to track job lifecycle:
- :started when job begins executing
- :completed when job finishes (including :discard status)
- :failed when job crashes with error
- Track duration in seconds for all job executions

Wrap main logic in try/rescue to ensure events broadcast even on crash.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-06 18:24:48 -06:00
a2778d9b7c
feat: integrate lifecycle event broadcasting into DevicePollerWorker
Add Events.broadcast_job_event calls to track job lifecycle:
- :started when job begins executing
- :completed/:failed when job finishes
- Track duration in seconds for all job executions

Broadcast events enable real-time monitoring of polling operations.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-06 18:23:52 -06:00
9f30d366b1 feat: add PubSub event broadcasting for job lifecycle
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-06 16:36:34 -06:00
9b0b000263
feat: add health metrics calculation module
Create JobMonitoring.Metrics module with calculate_all/0 to provide
comprehensive metrics including execution counts, success rates,
queue depths, and average execution times.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-06 16:31:27 -06:00
adbaeef83d
feat: add failed jobs and recent completions queries
Add list_failed_jobs/0 to return retryable and cancelled jobs
and list_recent_completions/1 to return completed, cancelled,
or discarded jobs with configurable limit.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-06 16:29:19 -06:00
d845f372d0
feat: add stuck job detection with per-worker thresholds
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-06 16:21:23 -06:00
92f835bf9d
refactor: add type spec to list_active_jobs 2026-02-06 16:19:54 -06:00
35c7328831
feat: add JobMonitoring context with list_active_jobs query
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-06 16:15:18 -06:00
e3e47fb82f
fix: include SNMPv3 fields in API device responses
The API was not returning SNMPv3 fields (security_level, username,
auth_protocol, auth_password, priv_protocol, priv_password) in device
responses, causing Terraform provider to see inconsistent state between
plan and apply.

Updated both format_device/1 and format_device_details/1 to include
all SNMPv3 fields in JSON responses.
2026-02-06 14:05:15 -06:00
7f7b722965
feat: add SNMPv3 credential logging for debugging
Log SNMPv3 credentials being sent to agents (with passwords redacted) to help diagnose authentication issues. Shows security level, username, auth/priv protocols, and whether passwords are present.
2026-02-06 13:48:35 -06:00
5f04d716ba
fix: handle device deletion during discovery
Previously, if a device was deleted while a discovery job was in progress, the wait_loop would crash with Ecto.NoResultsError when trying to check if discovery had completed.

Now uses get_device_with_details which returns nil for missing devices, and gracefully returns {:error, :device_deleted} instead of crashing.
2026-02-06 13:06:37 -06:00
6a29ee1671
fix: Split agent polling queries to isolate SNMP failures
Changed from one large WALK query with all OIDs to separate WALK queries
per table type. This prevents a failure in one unsupported table (like ARP
on a device that doesn't support it) from killing the entire polling
operation.

Before: Single WALK with neighbor + ARP + MAC + IP + HOST-RESOURCES OIDs
After: Separate WALKs for each table type

This makes polling more resilient - if a device doesn't support ARP tables
or HOST-RESOURCES-MIB, those WALKs will fail individually while neighbors,
MAC tables, and IP addresses can still succeed.

Fixes agent errors where a single SNMP receive error was killing the
entire poller thread and failing all subsequent operations with
'Poller thread died'.
2026-02-06 12:30:52 -06:00
184c2bc999
fix: Use snmp_device.id for sync_processors and sync_storage
Fixes KeyError when processing processors/storage during agent polling.
The sync_processors and sync_storage functions expect device.id to be
the snmp_device id, not the main device id.

Changed from:
  %{device_id: device.id}

To:
  %{id: device.snmp_device.id}

This matches the expected structure for these sync functions which
query where snmp_device_id == device.id.
2026-02-06 12:06:30 -06:00
c6ec9ab891
fix: Handle nil vlan_id in MAC address upsert queries
Fixes ArgumentError when upserting MAC addresses with nil vlan_id.
Ecto doesn't allow direct comparison with nil in queries - must use
is_nil/1 instead.

Changed upsert_mac_address to build dynamic query that uses is_nil(m.vlan_id)
when vlan_id is nil, or direct comparison when it has a value.

Error was:
  Comparison with nil is forbidden as it is unsafe.
  Instead write a query with is_nil/1
2026-02-06 12:00:29 -06:00