- Created 23 tests covering all public functions and edge cases
- Tests for IPv4 and IPv6 address validation
- Tests for timeout behavior and error handling
- Tests for PingBehaviour implementation
- All tests passing (23 tests, 0 failures)
- Module now has improved test coverage
Improves test coverage toward 90% target by adding tests for:
- Organizations context (bulk SNMP/agent operations)
- Sites context (hierarchical structure and bulk operations)
- Profiles context and schemas (device/sensor OID management)
- EctoTypes.JsonAny custom type
- Snmp.Neighbor schema (LLDP/CDP topology)
All new tests validate changeset validations, business logic,
and database constraints to ensure data integrity.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Update MibTranslator to automatically expand configured MIB directories
- snmptranslate doesn't search recursively, so we explicitly list all subdirs
- Simplify config to only specify root priv/mibs directory
- Update both config.exs (production) and dev.exs (development)
- Subdirectories like mikrotik/, cisco/, net-snmp/ are auto-discovered
- Update config.exs to point to /app/priv/mibs in production
- Update dev.exs to include priv/mibs root for standard MIBs
- Remove obsolete :mib_dir (singular) config
- MIB files are now part of the release and included in Docker image
- Paths work in both development and production environments
- Remove PVC volume mount from k8s/deployment.yaml
- Delete k8s/mib-pvc.yaml (no longer needed)
- Update .gitignore to commit MIB files to git
- Add mix import_mibs task to copy MIBs from LibreNMS
- Add all MIB files from priv/mibs/ to git
This fixes the multi-attach volume error in Kubernetes where new pods
couldn't start because the RWO (ReadWriteOnce) PVC was attached to the
old pod. MIBs are now part of the Docker image and can be used by all
pods simultaneously.
- Add monitoring_enabled and check_interval_seconds fields to Device and SnmpDevice protobuf messages
- Add MonitoringCheck protobuf message for ping results
- Update AgentChannel to handle monitoring_check events from agents
- Configure monitoring settings in job payloads sent to agents
- Created new on_mount hook :load_default_organization that automatically loads user's first organization
- Moved sites and devices routes to root path (/ sites, /devices) instead of /orgs/:org_slug
- Updated all navigation links and route references throughout the app
- Dashboard, alerts, and agents still use org-specific routes (/orgs/:org_slug)
- Users can still switch organizations via org selector for other pages
The neighbor discovery was using snmp_device.id instead of the actual
Equipment device.id when creating neighbor records. This caused all
neighbor inserts to fail because the foreign key constraint expected
the Equipment ID, not the SNMP Device ID.
Fixed in two places:
- Discovery: Use snmp_device.device_id (FK to Equipment)
- PollerWorker: Use device.id (Equipment ID) instead of snmp_device.id
Neighbors should now be properly discovered and saved during both
initial discovery and ongoing polling.
- Pass range parameter to SensorChart hook via data-range attribute
- Calculate x-axis min/max dynamically based on selected range (1h, 6h, 12h, 24h, 7d, 30d)
- Show date + time on x-axis labels for ranges > 24 hours
- Show time only for ranges <= 24 hours
- Fixes issue where 7d and 30d graphs showed incorrect time range
- Changed ping module to use microsecond timer resolution instead of millisecond
- Prevents 0ms readings for very fast pings (localhost, local network)
- Updated response_time_ms to float type to store decimal precision
- Migration converts existing integer values to float
- Add timezone to socket assigns in mount_current_scope
- Detect user timezone from browser using Intl.DateTimeFormat
- Auto-save detected timezone to user profile on first connection
- Update all templates to use TimeHelpers with timezone parameter
- Update agent helper functions to accept timezone parameter
- Templates updated:
- Alert list (triggered, acknowledged, resolved)
- Device list (last checked)
- Agent list and show (created, last seen, updated)
- Admin dashboard (impersonation logs)
- Admin org/user lists (created/joined dates)
- Main dashboard (alert times)
Wrapped all Exq.Api calls in individual try/rescue/catch blocks to handle
GenServer exits and exceptions when Exq hasn't connected to Redis yet.
The "GenServer Exq terminating" errors in logs are from Exq itself crashing
when it receives calls before it's ready, not from our code. These will still
appear in logs as Exq restarts, but our telemetry code won't crash.
Changes:
- Added try/rescue/catch around each queue_size call in the loop
- Added try/rescue/catch around processes/busy calls
- Kept outer try/rescue/catch as a final safety net
- rescue blocks must come before catch blocks in Elixir
Fixed two profile import issues:
1. Skip sensors without OIDs:
- Some profiles have sensors with null/missing OID fields
- These can't be used for SNMP polling anyway
- Added check to skip importing sensors without oid or num_oid
- Prevents Ecto.Changeset validation errors
2. Truncate MIB values to 255 characters:
- ERROR 22001 (string_data_right_truncation)
- MIB field is varchar(255) but some values exceed this
- Added truncation to first 255 chars before saving
- Location: lib/towerops/device_profiles/importer.ex:229-236
Both errors were causing profile imports to fail with crashes.
Changed pattern matching to proper error handling in telemetry functions
to prevent crashes when Exq or Redis connections aren't available.
Issues fixed:
1. Exq.Api.queue_size/2 calls were using pattern matching {:ok, size} =
which crashed with FunctionClauseError when Exq wasn't connected to Redis
2. Redix.start_link/1 and Redix.command/2 calls were using pattern matching
which crashed when Redis wasn't available
Changes:
- publish_exq_stats: Use case statements for queue_size calls
- publish_exq_stats: Use with statement for processes/busy calls
- publish_redis_stats: Use with statement for all Redis operations
This allows the app to start and run even when Redis/Exq connections
aren't immediately available, which is especially important during
container startup and rolling deployments.
Changed Valkey service from headless to ClusterIP and updated
REDIS_HOST to use full FQDN for more reliable DNS resolution.
Changes:
- valkey-service.yaml: Changed from headless (clusterIP: None) to ClusterIP
- deployment.yaml: Changed REDIS_HOST from "valkey" to "valkey.towerops.svc.cluster.local"
This ensures stable ClusterIP and explicit DNS resolution for Redis connections.
Fixed two import-related errors:
1. DateTime microseconds error when updating API token last_used_at:
- Error: :utc_datetime expects microseconds to be empty
- Fix: Use DateTime.truncate(:second) to remove microseconds
- Location: lib/towerops/api_tokens.ex:179
2. Float parsing error for integer strings like "0":
- Error: :erlang.binary_to_float("0") - not a textual representation of float
- Fix: Use Float.parse/1 instead of String.to_float/1
- Float.parse/1 handles both "0" and "0.0" correctly
- Returns {float, remainder} on success, :error on failure
- Location: lib/towerops/device_profiles/importer.ex:380
Both errors were causing profile imports to fail with crashes.