Commit graph

239 commits

Author SHA1 Message Date
d1fd1d4a62 remove Mix.env and support ip database import 2026-01-28 13:08:06 -06:00
220f1edce3 cookie improvements 2026-01-28 12:30:28 -06:00
f350a9ade4 test cleanup 2026-01-28 10:31:22 -06:00
fbee38446e reduce log level 2026-01-28 09:27:13 -06:00
b8cb3e4f05 reduce log level 2026-01-28 09:22:11 -06:00
d34c72a0ab track more snmp things 2026-01-28 09:18:31 -06:00
88c003d474 major snmp overhaul 2026-01-28 09:07:13 -06:00
667fde17d7
discovery deduplication 2026-01-27 14:49:06 -06:00
72f69b13ff
agent debug improvements 2026-01-27 14:34:32 -06:00
3250989d79
agent debug improvements 2026-01-27 14:21:50 -06:00
3f3df5938d
agent debug improvements 2026-01-27 14:19:37 -06:00
5fb92dd961
add device/org limit 2026-01-27 14:14:25 -06:00
3bb89670bd
enable agent debugging 2026-01-27 13:25:04 -06:00
328d4659a7
show correct agent on edit screen and fix airfiber/ltu 2026-01-27 12:52:43 -06:00
6c238c52e5
fix a few things and fix device discovery 2026-01-26 17:17:31 -06:00
bb86dec559
add oban pro 2026-01-26 17:07:42 -06:00
b20a58aec4
test fixes 2026-01-26 15:27:59 -06:00
756a6b4cd4
poller agent fix and trigger device rediscovery 2026-01-26 14:51:32 -06:00
0b72b47ae6
better logs for discovery and polling 2026-01-26 13:43:17 -06:00
492dc74c9a
fix network map 2026-01-26 13:39:46 -06:00
fb42e93f7a
cloud poller fixes 2026-01-26 12:00:55 -06:00
5f1bbab420
cloud poller fixes 2026-01-26 11:57:32 -06:00
99414459d3
device reorder 2026-01-26 10:22:12 -06:00
d2abe9a14d
add network map 2026-01-25 15:24:35 -06:00
9c5b4096f9
discovery working 2026-01-25 14:48:57 -06:00
9cd3d57433
live reload yaml in dev 2026-01-25 13:38:17 -06:00
6e86f54aa0
mikrotik sensor fixes 2026-01-25 13:24:28 -06:00
2ef66464b8
refactor: filter harmless Oban shutdown messages during deployments
When Kubernetes performs rolling deployments, the old pod gracefully shuts down
and Oban's internal processes receive EXIT :shutdown signals. These are logged
as "unexpected" even though they're normal during shutdown.

Added custom logger filter to drop these messages in production:
- Oban.Queue.Watchman/Producer receiving EXIT :shutdown
- Oban plugin processes receiving EXIT :shutdown
- Reduces log noise during deployments without hiding real errors

The filter is production-only to preserve full debugging in development.
2026-01-25 08:38:33 -06:00
83868a6028
fix: remove to_charlist conversion for Redix host option
Redix expects host to be a string, not a charlist. The to_charlist/1
conversion was causing FunctionClauseError in Redix.StartOptions.__validate_host__/1.

This matches how Phoenix PubSub Redis and Exq are configured, which both
use the string host directly without conversion.
2026-01-24 17:49:57 -06:00
6c1f344b5e
fix: resolve all dialyzer unmatched return and pattern match warnings
Fixed dialyzer warnings across multiple files by properly handling return
values from functions that are called for side effects.

Changes:
- lib/towerops/agents.ex: Explicitly match broadcast and if-expression returns
- lib/towerops/devices.ex: Match worker start function returns in if-expressions
- lib/towerops/workers/agent_latency_evaluator.ex: Match broadcast return
- lib/towerops/workers/device_monitor_worker.ex: Match perform_check and handle_status_change returns, handle schedule_next_check errors
- lib/towerops/workers/device_poller_worker.ex: Match all PubSub broadcast returns, handle schedule_next_poll errors, remove unreachable pattern
- lib/towerops/workers/stale_agent_worker.ex: Match process_stale_agents and broadcast returns
- lib/towerops_web/channels/agent_channel.ex: Match PubSub.subscribe return, improve get_addr pattern matching, remove unreachable format_ip clause
- lib/towerops_web/live/device_live/form.ex: Match enqueue_discovery return, replace compile-time @dev_env with runtime check

Pattern applied:
- PubSub broadcasts: `_ = Phoenix.PubSub.broadcast(...)`
- If-expressions with side effects: `_ = if condition do ... end`
- Critical operations: Added error logging with case statements

All 3625 tests passing. No dialyzer warnings remaining in lib/towerops or lib/towerops_web.

🤖 Generated with Claude Code

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-24 17:34:37 -06:00
a92d003aec
refactor: replace Task.async_stream with Oban jobs for discovery
Changed discover_all/1 to enqueue individual Oban jobs instead of using
Task.async_stream for better error visibility and resilience.

Benefits:
- Errors are visible in Oban dashboard with full stack traces
- Automatic retries on failure via Oban
- Jobs persist across pod restarts
- No more silent failures from Task crashes
- Better concurrency control via Oban queue configuration

Changes:
- discover_all/1 now enqueues DiscoveryWorker jobs and returns immediately
- Return type changed from %{success, failed, errors} to %{enqueued, failed, errors}
- Updated tests to match new async behavior
- Added DiscoveryWorker alias to Discovery module

All tests passing (3,625 tests, 0 failures).
2026-01-24 17:15:36 -06:00
58883d0d58
fix: add missing RedisHealthCheck module for health endpoint
The health check endpoint was calling Towerops.RedisHealthCheck.check_health/2
but the module didn't exist, causing startup probes to fail with 503 errors.

Added RedisHealthCheck module with:
- check_health/2: Single PING command with timeout for health checks
- wait_for_redis/2: Exponential backoff retry logic for startup

This fixes the failing health checks that were blocking pod rollouts.

All tests passing.
2026-01-24 17:06:33 -06:00
ee8a3220c4
refactor: convert periodic workers to Oban Cron for better resilience
Replaced GenServer-based periodic workers with Oban Cron jobs to improve
pod rollover resilience and simplify architecture.

Worker Changes:
- NeighborCleanupWorker: GenServer → Oban Cron (hourly)
  - Cleans stale neighbors, ARP entries, and MAC addresses
  - Runs every hour via Oban.Plugins.Cron

- StaleAgentWorker: GenServer → Oban Cron (every minute)
  - Detects agents that haven't checked in for 10+ minutes
  - Refactored to reduce nesting (extracted helper functions)
  - Removed stateful tracking (now stateless, re-evaluates each run)

- AgentLatencyEvaluator: GenServer → Oban Cron (every 5 minutes)
  - Latency-based agent reassignment with 20% threshold
  - Removed trigger_evaluation/0 (no longer needed)

- JobHealthCheckWorker: NEW Oban Cron worker (every 10 minutes)
  - Safety net to recover missing monitor/poller jobs
  - Auto-creates jobs for devices with monitoring/SNMP enabled

Infrastructure Changes:
- Removed Monitoring.Supervisor (no longer needed)
- Updated application.ex to remove GenServer workers from supervision tree
- Added Oban.Plugins.Cron to dev.exs and runtime.exs
- All workers now run cluster-wide via PostgreSQL-backed coordination

Test Updates:
- Updated all worker tests to call perform(%Oban.Job{args: %{}})
- Removed GenServer lifecycle tests (start_link, send messages, etc.)
- Removed async sleep calls (no longer needed)

Benefits:
- Better pod rollover resilience (Cron jobs run cluster-wide)
- Simpler architecture (no GenServers for periodic tasks)
- Better observability (all jobs visible in Oban dashboard)
- Safety net for missing jobs (JobHealthCheckWorker)
- Stateless workers (easier to reason about and test)

Documentation:
- Updated CLAUDE.md with Background Job Architecture section
- Documented job types, queues, and resilience features

All tests passing (3,686 tests, 0 failures).
2026-01-24 16:56:28 -06:00
d0946c3cd0
refactor: simplify job architecture from Oban coordinators to direct workers
Removes unnecessary two-layer architecture (Oban Coordinator → GenServer)
in favor of direct Oban workers that perform the actual work.

Changes:
- Replace DevicePollerCoordinator + PollerWorker with DevicePollerWorker
- Replace DeviceMonitorCoordinator + DeviceMonitor with DeviceMonitorWorker
- Simplify Monitoring.Supervisor (removed Registries, DynamicSupervisors)
- Remove all Exq dependencies (workers, supervisor, mix deps)
- Convert DiscoveryWorker from Exq to Oban pattern
- Update Devices context to auto-start/stop monitoring and polling
- Update all LiveView callers to use new Oban enqueue pattern
- Fix all tests to use Oban.Job struct instead of raw IDs

Benefits:
- Simpler codebase (~850 lines removed)
- Better reliability (Oban handles retries, failures)
- Lower memory (no persistent GenServers)
- Better observability (work visible in Oban dashboard)
- Cluster-wide coordination via PostgreSQL
- Extensive PubSub usage for real-time events

Files deleted:
- lib/towerops/workers/device_monitor_coordinator.ex
- lib/towerops/workers/device_poller_coordinator.ex
- lib/towerops/snmp/poller_worker.ex
- lib/towerops/monitoring/device_monitor.ex
- lib/towerops/workers/monitor_worker.ex
- lib/towerops/workers/poll_worker.ex
- lib/towerops/exq_supervisor.ex
- All related test files

Files created:
- lib/towerops/workers/device_poller_worker.ex
- lib/towerops/workers/device_monitor_worker.ex

All 3,686 tests passing.
2026-01-24 16:36:57 -06:00
29593ac734
refactor: migrate from etcd to Oban for distributed job coordination
Replaces etcd-based distributed locking with Oban's PostgreSQL-backed job queue.
This simplifies the architecture by eliminating the need for a separate etcd cluster
while providing better reliability and observability.

Changes:
- Add Oban dependency and migration (oban_jobs table)
- Create DevicePollerCoordinator and DeviceMonitorCoordinator Oban workers
- Remove EtcdCoordinator and EtcdLock modules
- Update application supervisor to start Oban
- Configure Oban with pollers (50 workers) and monitors (50 workers) queues
- Remove etcd StatefulSet from Kubernetes manifests
- Update monitoring supervisor documentation

Benefits:
- Simpler architecture (no etcd cluster to manage)
- PostgreSQL-based (uses existing database)
- Built-in uniqueness prevents duplicate jobs cluster-wide
- Better observability with Oban Web UI
- Automatic job rescue on node crashes
- Easier local development (no etcd required)

What was removed:
- etcd StatefulSet (3 pods)
- EtcdCoordinator module (320 lines)
- EtcdLock module (158 lines)
- eetcd dependency

All 3,686 tests passing.
2026-01-24 16:12:27 -06:00
de0c106212
fix: Redis authentication and etcd connection handling
Fixes two production issues:

1. NOAUTH Redis authentication error:
   - application.ex: Read password from application config
   - exq_supervisor.ex: Read password from application config
   - Both were bypassing runtime.exs config by reading env directly

2. EtcdCoordinator FunctionClauseError:
   - connect_etcd/0 now returns connection name (:towerops_etcd)
   - eetcd functions expect the atom name, not the PID
   - Fixes crash when trying to acquire locks
2026-01-24 15:58:21 -06:00
13f86de4b8
use application config for redis password instead of env 2026-01-24 15:43:27 -06:00
f5ca3f0940
fix: make etcd watcher optional for graceful degradation
The app was crashing when the etcd watcher couldn't be created, even
though 2/3 etcd nodes were healthy and the connection succeeded.

Changes:
- Made watcher creation optional (nil if it fails)
- App now starts successfully even if watcher is unavailable
- Falls back to periodic rebalancing (60s intervals) instead of instant failover
- Changed error level from error to warning when watcher creation fails
- Added helpful context to warning message

This allows the app to run with degraded performance instead of crashing
completely when there are etcd cluster issues.

🤖 Generated with Claude Code

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-24 15:27:28 -06:00
64ad63d3e3
fix: handle etcd watcher creation errors gracefully
The pods were crashing with a MatchError when the etcd watcher failed
to initialize. The code was pattern matching on {:ok, watcher} but
eetcd was returning {:error, :eetcd_conn_unavailable}.

Error:
  ** (MatchError) no match of right hand side value:
  {:error, :eetcd_conn_unavailable}

This was causing CrashLoopBackOff because the application couldn't
start the Monitoring.Supervisor due to EtcdCoordinator initialization
failure.

Fix:
  - Add case statement to handle both {:ok, watcher} and {:error, reason}
  - Log the specific error when watcher creation fails
  - Return proper error tuple to supervisor for clean shutdown
2026-01-24 15:02:46 -06:00
51736eeec3
fix: use charlists for etcd endpoints
The :eetcd Erlang library expects charlists, not binary strings.
This was causing a FunctionClauseError in :lists.reverse/1 because
it was receiving a binary string instead of a charlist.

Error:
  (FunctionClauseError) no function clause matching in :lists.reverse/1
  (stdlib 7.2) lists.erl:291: :lists.reverse("etcd-0.etcd...")

Solution:
  Use ~c sigil to convert endpoint strings to charlists for the
  Erlang library to consume correctly.
2026-01-24 14:39:53 -06:00
0d4c8eb15a
feat: add Redis password authentication support
Add REDIS_PASSWORD environment variable support to both Phoenix.PubSub.Redis
and Exq background job processor. Password is optional - if not set, connects
without authentication.

Changes:
- application.ex: Add password to PubSub Redis config if REDIS_PASSWORD is set
- exq_supervisor.ex: Add password to Exq Redis config if REDIS_PASSWORD is set

Required for connecting to Proxmox-hosted Redis with authentication.
2026-01-24 14:18:55 -06:00
5ee241fc16
feat: implement multi-replica Valkey with Sentinel for high availability
Addresses production Redis disconnection issues by implementing a highly
resilient Valkey (Redis) setup with automatic failover capabilities.

Infrastructure Changes:
- Add Valkey ConfigMap with optimized connection and memory settings
  - TCP keepalive (60s), connection limits (10k clients)
  - Memory management (256MB with LRU eviction)
  - Separate configs for master, replica, and sentinel

- Update Valkey StatefulSet to 3 replicas (1 master + 2 replicas)
  - Auto-configuration via init container (master vs replica)
  - Increased memory limits: 256Mi → 1Gi
  - Improved readiness probes with replication status checks

- Add Valkey Sentinel StatefulSet (3 instances for quorum)
  - Automatic failover detection (5s down-after-milliseconds)
  - Fast failover execution (10s timeout)
  - Monitors master and promotes replicas automatically

- Add Sentinel headless service for pod discovery

Application Resilience:
- Update Phoenix.PubSub.Redis with TCP keepalive and reconnection
  - Connection timeout: 5s
  - Exponential backoff: 500ms → 30s
  - exit_on_disconnection: false

- Update Exq background jobs with same resilience settings
  - TCP keepalive enabled
  - Better connection pool management

Benefits:
- Automatic failover when Valkey pod dies (no manual intervention)
- Zero data loss with replica synchronization
- Fast failure detection and recovery (5-10s total)
- Survives Flannel CNI networking issues
- Apps reconnect automatically during disconnections

Testing:
- All 3,686 tests passing
- Kustomize manifest validated

🤖 Generated with Claude Code
2026-01-24 14:03:50 -06:00
e6054129b2
remote agent/cloud improvements 2026-01-24 09:16:41 -06:00
f084e09ea1
add multiple cloud agents 2026-01-23 18:04:01 -06:00
2b78b1a2d3
snmpkit overhaul and etcd 2026-01-23 16:23:57 -06:00
a4335a047a
allow nonroutable for me 2026-01-23 13:10:12 -06:00
a0ba9285dd
bring in snmpkit 2026-01-23 12:52:17 -06:00
0056d35e9b
ignore more startup errors 2026-01-23 10:08:04 -06:00
3495767b8d
ignore startup errors 2026-01-23 10:06:25 -06:00
80d1864700
mac discovery 2026-01-23 09:22:08 -06:00