Commit graph

671 commits

Author SHA1 Message Date
bd24dfcbed
add diagnostic logging for agent result handling 2026-02-11 12:00:04 -06:00
e4623f0c3d
show agent ID instead of org name in admin agents list
Makes it easier to identify and reference specific agents in the admin view
2026-02-11 11:56:09 -06:00
f53332672b
add IP allowlist link to superadmin nav, rename whitelist/blacklist to allowlist/denylist 2026-02-11 11:40:44 -06:00
f2cd3488ee
fix heartbeat validation rejecting git-describe version strings
the version regex only allowed [a-zA-Z0-9.] in semver suffixes,
which rejected hyphens from git-describe output (e.g. 0.1.0-5-gabcdef0).
this caused every heartbeat to fail validation, preventing last_seen_at
from updating, which led to agent status showing "warning" and eventual
channel disconnect after 360s.

also accept bare git SHAs and short identifiers like "dev".
2026-02-11 11:31:19 -06:00
c5d8d73a0c
add row_link to table component for proper anchor tags
Replace phx-click JS.navigate with real <.link navigate> elements
on table rows so users can Ctrl+Click to open in new tabs, use
right-click context menus, and navigate via keyboard. Adds row_link
attr to <.table> core component and applies it to all agent tables.
Converts device list custom table from phx-click to inner links.
2026-02-11 08:46:03 -06:00
77cf289058
Add real-time device list with PubSub and LiveView streams
Broadcast org-scoped PubSub events from Devices context mutations
(create, update, delete, status change). Convert DeviceLive.Index
from assigns to streams with flat row items. Subscribe on mount so
the device list updates automatically across browser tabs.
2026-02-10 17:38:58 -06:00
138151ade6
Replace conditionals with functional pattern matching
Refactor cond/if/nested-case blocks into idiomatic multi-clause
functions across permissions, organizations, credential resolver,
SNMP client, and monitoring modules. Extract shared log_snmp_error
helper and dispatch_snmp for SNMP adapter routing.
2026-02-10 17:11:37 -06:00
4d73e77f3a
Add 404-based brute force protection system
Implements automated detection and blocking of IPs exhibiting scanning behavior (5+ unique 404s within 1 minute).

Key features:
- Progressive ban escalation (5 min → 1 hour → permanent)
- CIDR range and exact IP whitelisting
- Redis-backed 404 path tracking with 60s TTL
- Cloudflare WAF integration for permanent bans
- Admin UI for whitelist and blocked IP management
- Oban cron jobs for cleanup (expired bans, stale violations)
- ETS caching for whitelist performance

Components:
- Plug: ToweropsWeb.Plugs.BruteForceProtection
- Context: Towerops.Security.BruteForce
- Schemas: IpBlock, IpWhitelist
- Workers: CloudflareBanWorker, ExpiredBanCleanupWorker, StaleViolationCleanupWorker
- Admin UI: /admin/security (superuser only)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-10 16:31:48 -06:00
c7df6a8569
Add CI-triggered mass agent update webhook
POST /api/v1/webhooks/agent-release triggers all connected agents
to self-update via their existing WebSocket channels. Authenticated
with a shared secret (AGENT_WEBHOOK_SECRET env var).

- Add ReleaseChecker.invalidate_cache/0 for fresh GitHub fetch
- Add Agents.list_updatable_agents/0 (enabled + seen < 10min)
- Add Agents.broadcast_mass_update/0 orchestration function
- Add WebhookAuth plug with timing-safe secret comparison
- Add AgentReleaseWebhookController with :webhook pipeline
- Configure AGENT_WEBHOOK_SECRET in dev/test/runtime configs
2026-02-10 13:40:32 -06:00
ecd9f78ba8
Add backward compatibility for PING results from older agents
Older agents send PING job results as SnmpResult messages instead of
MonitoringCheck messages, causing FunctionClauseError when
process_job_result/3 tried to pattern match on job_type: :PING.

Added handler that:
- Catches PING results in SnmpResult format
- Logs warning that agent should be upgraded
- Creates monitoring check with success status
- Stores result via store_monitoring_check

This prevents crashes while maintaining backward compatibility until
all production agents are upgraded to use MonitoringCheck format.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-10 13:18:40 -06:00
fbce65070a
Add agent restart and self-update commands
Add server-initiated restart and self-update capabilities for remote
agents. Restart broadcasts on the lifecycle PubSub topic causing the
agent to exit cleanly for Docker to restart. Self-update fetches the
latest release from GitHub, finds the matching binary for the agent's
architecture, and sends the download URL with SHA256 checksum.

- Add restart_agent/1 and update_agent/3 to Agents context
- Handle :restart_requested and :update_requested in AgentChannel
- Add superuser-only Restart and Update buttons on agent show page
- Add ReleaseChecker module for GitHub Releases API with 5-min cache
- Add arch field to AgentHeartbeat protobuf for architecture detection
- Store arch in agent metadata from heartbeat
2026-02-10 13:06:10 -06:00
c066399e18
Allow superadmins to view and edit any agent regardless of org
The admin agents page links to /agents/:id but the show and edit
pages were scoped to the current user's organization, causing 404s
for agents belonging to other orgs.
2026-02-10 12:19:20 -06:00
c1fca6276a
Add real-time updates to admin agents page via PubSub
Broadcast agent lifecycle events (created/deleted) on a separate
"admin:agents" topic so the admin page updates dynamically when
agents are added or removed from any organization.
2026-02-10 11:20:51 -06:00
c6caa81850
Group cloud pollers and org agents separately on admin agents page
Cloud pollers render in their own table at the top, org agents
below with the organization name shown under each agent name.
2026-02-10 11:07:41 -06:00
6a66203e48
Add link to admin agents page from admin dashboard 2026-02-10 11:04:55 -06:00
183f2a89ac
Add admin agents page at /admin/agents
Superuser-only page showing all agents across all organizations
and cloud pollers in a single table with live-ticking timestamps
and real-time status updates via PubSub.
2026-02-10 10:56:10 -06:00
5c59777baa
Live-ticking relative timestamps on agents page 2026-02-10 10:24:21 -06:00
481fc6d4a2
Add absolute format to timestamp component to fix render flash 2026-02-10 10:19:54 -06:00
d1cd567cd5
Show full timestamp below relative time in Last Seen column 2026-02-10 10:07:44 -06:00
e9afac3e9d
Show full timestamp below relative time on Last Seen card 2026-02-10 09:55:34 -06:00
6724a0a4bc
clean up agent view again 2026-02-10 09:51:28 -06:00
07a9366892
clean up agent view 2026-02-10 09:48:09 -06:00
ba56dee1ca
remove date helper 2026-02-10 09:15:41 -06:00
fce1a8b229
Show full version timestamp with friendly date on agents page 2026-02-10 09:04:56 -06:00
94405f0716
fix agents updates 2026-02-10 08:08:34 -06:00
144e04b998
change metadata version to version 2026-02-09 17:39:33 -06:00
95eafd0c47
routeros fixes and latency rounding 2026-02-09 17:30:06 -06:00
4cfab3b1ee
Reliability improvements: agent channel, polling, discovery, batch ops
- Add Task.Supervisor for supervised background tasks in agent channel
- Discovery worker retries transient failures (max 3 attempts)
- Log silent polling timeouts with named task identification
- Track errors in batch ARP/MAC upserts instead of silently ignoring
- Discovery gracefully degrades on interface/sensor timeout (partial results)
- Synchronous job dispatch on agent join to prevent race condition
- Surface SNMP result, monitoring check, and backup processing errors
- Atomic delete-stale + upsert for neighbors/ARP/MAC (transaction)
- Discovery transaction timeout (60s) with duration logging
- Heartbeat timeout detection (5min) closes stale agent channels
- Live poll timeout (60s) notifies waiting LiveView on expiry
- Disconnect agent channel when token is disabled or deleted
- Debounce rapid assignment changes to prevent redundant job dispatches
- Sensor index deduplication via YAML index template substitution
2026-02-09 16:28:12 -06:00
3b8e1ea639
Reliability improvements: Task.Supervisor, discovery retries, polling visibility
- Add Towerops.TaskSupervisor to supervision tree, replace bare spawn/1
  in agent channel with supervised tasks for polling data processing
- Change DiscoveryWorker from max_attempts: 1 to 3 with retryable error
  classification (transient errors retry, permanent errors discard)
- Name all 9 parallel polling tasks and log timeouts/crashes instead of
  silently dropping results via Stream.run()
- Add tests for TaskSupervisor presence and error classification
2026-02-09 16:05:30 -06:00
2911a3c17d
test fixes 2026-02-09 15:30:52 -06:00
41594e5a96
fix bugs on agents screen 2026-02-09 14:03:40 -06:00
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