refactor: move temperature backfill to a release-safe module
Mix tasks aren't usable from the prod release image — `/app/bin/towerops`
ships without `mix`, and `Mix.shell()` raises at runtime. Move the work
to `Towerops.Maintenance.FixMikrotikTemperatureScaling` (plain module
with `run/0`, IO.puts + Logger.info) and keep the Mix.Task as a thin
dev-only delegator.
Run in prod via:
/app/bin/towerops eval 'Towerops.Maintenance.FixMikrotikTemperatureScaling.run()'
This commit is contained in:
parent
d051968ae6
commit
f8c3b00ba0
2 changed files with 61 additions and 42 deletions
|
|
@ -2,57 +2,25 @@ defmodule Mix.Tasks.Towerops.FixMikrotikTemperatureScaling do
|
|||
@shortdoc "Renormalise stored temperature sensor readings using SensorScale"
|
||||
|
||||
@moduledoc """
|
||||
One-shot backfill that re-runs `Towerops.Snmp.SensorScale.normalize/2`
|
||||
over every stored temperature reading. Catches historical mis-scaled
|
||||
rows from MikroTik `mtxrGaugeValue` (some models report deci-degrees)
|
||||
and any other sensor whose YAML profile didn't include the right
|
||||
divisor at discovery time.
|
||||
|
||||
Run from inside a release pod:
|
||||
|
||||
/app/bin/towerops eval 'Mix.Tasks.Towerops.FixMikrotikTemperatureScaling.run([])'
|
||||
|
||||
Or via mix during dev:
|
||||
Dev-only convenience wrapper. Delegates to
|
||||
`Towerops.Maintenance.FixMikrotikTemperatureScaling.run/0` so you can
|
||||
run the backfill from a local checkout via:
|
||||
|
||||
mix towerops.fix_mikrotik_temperature_scaling
|
||||
|
||||
In prod the release has no `mix` binary — invoke the plain module
|
||||
directly:
|
||||
|
||||
/app/bin/towerops eval 'Towerops.Maintenance.FixMikrotikTemperatureScaling.run()'
|
||||
"""
|
||||
|
||||
use Mix.Task
|
||||
|
||||
import Ecto.Query
|
||||
|
||||
alias Towerops.Repo
|
||||
alias Towerops.Snmp.Sensor
|
||||
alias Towerops.Snmp.SensorScale
|
||||
alias Towerops.Maintenance.FixMikrotikTemperatureScaling
|
||||
|
||||
@impl Mix.Task
|
||||
def run(_argv) do
|
||||
{:ok, _} = Application.ensure_all_started(:towerops)
|
||||
|
||||
sensors =
|
||||
Repo.all(
|
||||
from(s in Sensor, where: s.sensor_type == "temperature" and not is_nil(s.last_value) and s.last_value > 150.0)
|
||||
)
|
||||
|
||||
{fixed, skipped} =
|
||||
Enum.reduce(sensors, {0, 0}, fn sensor, {fixed, skipped} ->
|
||||
normalized = SensorScale.normalize(sensor.sensor_type, sensor.last_value)
|
||||
|
||||
if normalized == sensor.last_value do
|
||||
{fixed, skipped + 1}
|
||||
else
|
||||
{:ok, _} =
|
||||
sensor
|
||||
|> Sensor.changeset(%{last_value: Float.round(normalized / 1.0, 2)})
|
||||
|> Repo.update()
|
||||
|
||||
{fixed + 1, skipped}
|
||||
end
|
||||
end)
|
||||
|
||||
Mix.shell().info(
|
||||
"Temperature backfill complete: rescaled #{fixed} rows, " <>
|
||||
"left #{skipped} unchanged."
|
||||
)
|
||||
FixMikrotikTemperatureScaling.run()
|
||||
end
|
||||
end
|
||||
|
|
|
|||
51
lib/towerops/maintenance/fix_mikrotik_temperature_scaling.ex
Normal file
51
lib/towerops/maintenance/fix_mikrotik_temperature_scaling.ex
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
defmodule Towerops.Maintenance.FixMikrotikTemperatureScaling do
|
||||
@moduledoc """
|
||||
One-shot backfill that re-runs `Towerops.Snmp.SensorScale.normalize/2`
|
||||
over every stored temperature reading. Catches historical mis-scaled
|
||||
rows from MikroTik `mtxrGaugeValue` (some models report deci-degrees)
|
||||
and any other sensor whose YAML profile didn't include the right
|
||||
divisor at discovery time.
|
||||
|
||||
Plain module rather than a `Mix.Task` so it can run from the prod
|
||||
Elixir release (where `mix` itself isn't installed):
|
||||
|
||||
/app/bin/towerops eval 'Towerops.Maintenance.FixMikrotikTemperatureScaling.run()'
|
||||
"""
|
||||
|
||||
import Ecto.Query
|
||||
|
||||
alias Towerops.Repo
|
||||
alias Towerops.Snmp.Sensor
|
||||
alias Towerops.Snmp.SensorScale
|
||||
|
||||
require Logger
|
||||
|
||||
@spec run() :: :ok
|
||||
def run do
|
||||
sensors =
|
||||
Repo.all(
|
||||
from(s in Sensor, where: s.sensor_type == "temperature" and not is_nil(s.last_value) and s.last_value > 150.0)
|
||||
)
|
||||
|
||||
{fixed, skipped} =
|
||||
Enum.reduce(sensors, {0, 0}, fn sensor, {fixed, skipped} ->
|
||||
normalized = SensorScale.normalize(sensor.sensor_type, sensor.last_value)
|
||||
|
||||
if normalized == sensor.last_value do
|
||||
{fixed, skipped + 1}
|
||||
else
|
||||
{:ok, _} =
|
||||
sensor
|
||||
|> Sensor.changeset(%{last_value: Float.round(normalized / 1.0, 2)})
|
||||
|> Repo.update()
|
||||
|
||||
{fixed + 1, skipped}
|
||||
end
|
||||
end)
|
||||
|
||||
msg = "Temperature backfill complete: rescaled #{fixed} rows, left #{skipped} unchanged."
|
||||
IO.puts(msg)
|
||||
Logger.info(msg)
|
||||
:ok
|
||||
end
|
||||
end
|
||||
Loading…
Add table
Reference in a new issue