mikrotik version upgrade tracking
This commit is contained in:
parent
7ca07010ab
commit
612bed2dd5
3 changed files with 140 additions and 68 deletions
|
|
@ -84,6 +84,10 @@ defmodule Towerops.Workers.FirmwareVersionFetcherWorker do
|
|||
if version == "" or is_nil(version) do
|
||||
{:error, :invalid_data}
|
||||
else
|
||||
# Extract clean version number from RSS title
|
||||
# "RouterOS 7.21.2 [stable]" -> "7.21.2"
|
||||
clean_version = extract_version_number(version)
|
||||
|
||||
# Parse RFC822 date
|
||||
release_date = parse_rfc822_date(pub_date_string)
|
||||
|
||||
|
|
@ -91,14 +95,15 @@ defmodule Towerops.Workers.FirmwareVersionFetcherWorker do
|
|||
%{
|
||||
vendor: "mikrotik",
|
||||
product_line: "routeros",
|
||||
version: version,
|
||||
version: clean_version,
|
||||
release_date: release_date,
|
||||
download_url: download_url,
|
||||
changelog_url: @changelog_url,
|
||||
fetched_at: DateTime.utc_now(),
|
||||
metadata: %{
|
||||
"rss_title" => channel_title,
|
||||
"rss_description" => description
|
||||
"rss_description" => description,
|
||||
"raw_version" => version
|
||||
}
|
||||
}}
|
||||
end
|
||||
|
|
@ -111,6 +116,20 @@ defmodule Towerops.Workers.FirmwareVersionFetcherWorker do
|
|||
{:error, :parse_error}
|
||||
end
|
||||
|
||||
# Extract clean version number from RSS title
|
||||
# Examples:
|
||||
# "RouterOS 7.21.2 [stable]" -> "7.21.2"
|
||||
# "7.14.1" -> "7.14.1"
|
||||
defp extract_version_number(version_string) do
|
||||
# Match pattern: one or more digits, followed by dot, followed by digits
|
||||
# This extracts semantic version numbers like "7.21.2"
|
||||
case Regex.run(~r/\d+\.\d+(?:\.\d+)?/, version_string) do
|
||||
[version] -> version
|
||||
# Fallback to original if no match
|
||||
_ -> version_string
|
||||
end
|
||||
end
|
||||
|
||||
# Parse RFC822 date format used in RSS feeds
|
||||
# Example: "Wed, 15 Jan 2025 12:00:00 +0000"
|
||||
defp parse_rfc822_date(date_string) when is_binary(date_string) and date_string != "" do
|
||||
|
|
|
|||
|
|
@ -286,12 +286,27 @@ defmodule ToweropsWeb.DeviceLive.Show do
|
|||
end
|
||||
end
|
||||
|
||||
defp determine_vendor("MikroTik"), do: "mikrotik"
|
||||
defp determine_vendor("Cisco"), do: "cisco"
|
||||
defp determine_vendor("Ubiquiti"), do: "ubiquiti"
|
||||
defp determine_vendor(manufacturer) when is_binary(manufacturer) do
|
||||
manufacturer_lower = String.downcase(manufacturer)
|
||||
|
||||
cond do
|
||||
String.contains?(manufacturer_lower, "mikrotik") -> "mikrotik"
|
||||
String.contains?(manufacturer_lower, "cisco") -> "cisco"
|
||||
String.contains?(manufacturer_lower, "ubiquiti") -> "ubiquiti"
|
||||
true -> nil
|
||||
end
|
||||
end
|
||||
|
||||
defp determine_vendor(_), do: nil
|
||||
|
||||
defp determine_product_line("MikroTik"), do: "routeros"
|
||||
defp determine_product_line(manufacturer) when is_binary(manufacturer) do
|
||||
manufacturer_lower = String.downcase(manufacturer)
|
||||
|
||||
if String.contains?(manufacturer_lower, "mikrotik") do
|
||||
"routeros"
|
||||
end
|
||||
end
|
||||
|
||||
defp determine_product_line(_), do: nil
|
||||
|
||||
defp firmware_update_available?(nil, _), do: false
|
||||
|
|
@ -301,7 +316,29 @@ defmodule ToweropsWeb.DeviceLive.Show do
|
|||
current = snmp_device.firmware_version
|
||||
available = available_firmware.version
|
||||
|
||||
current && available && VersionComparator.newer?(current, available)
|
||||
# Extract clean version numbers for comparison
|
||||
# "RouterOS 7.20.6 (Level 6)" -> "7.20.6"
|
||||
# "7.21.2" -> "7.21.2"
|
||||
current_clean = extract_version_number(current)
|
||||
available_clean = extract_version_number(available)
|
||||
|
||||
current_clean && available_clean && VersionComparator.newer?(current_clean, available_clean)
|
||||
end
|
||||
|
||||
# Extract semantic version number from version string
|
||||
# Examples:
|
||||
# "RouterOS 7.20.6 (Level 6)" -> "7.20.6"
|
||||
# "7.21.2" -> "7.21.2"
|
||||
# "v7.14.1" -> "7.14.1"
|
||||
defp extract_version_number(nil), do: nil
|
||||
defp extract_version_number(""), do: nil
|
||||
|
||||
defp extract_version_number(version_string) when is_binary(version_string) do
|
||||
# Match semantic version pattern: X.Y or X.Y.Z
|
||||
case Regex.run(~r/\d+\.\d+(?:\.\d+)?/, version_string) do
|
||||
[version] -> version
|
||||
_ -> nil
|
||||
end
|
||||
end
|
||||
|
||||
defp format_date(nil), do: ""
|
||||
|
|
|
|||
|
|
@ -205,57 +205,6 @@
|
|||
<div class="mt-6">
|
||||
<%= case @active_tab do %>
|
||||
<% "overview" -> %>
|
||||
<!-- Firmware Update Indicator -->
|
||||
<%= if firmware_update_available?(@snmp_device, @available_firmware) do %>
|
||||
<div class="mb-6 rounded-md bg-blue-50 dark:bg-blue-900/20 p-4 border border-blue-200 dark:border-blue-800">
|
||||
<div class="flex">
|
||||
<div class="flex-shrink-0">
|
||||
<.icon name="hero-arrow-up-circle" class="h-5 w-5 text-blue-400" />
|
||||
</div>
|
||||
<div class="ml-3 flex-1">
|
||||
<h3 class="text-sm font-medium text-blue-800 dark:text-blue-200">
|
||||
Firmware Update Available
|
||||
</h3>
|
||||
<div class="mt-2 text-sm text-blue-700 dark:text-blue-300">
|
||||
<p>
|
||||
Current version:
|
||||
<span class="font-mono font-semibold">{@snmp_device.firmware_version}</span>
|
||||
<br /> Latest version:
|
||||
<span class="font-mono font-semibold">{@available_firmware.version}</span>
|
||||
<%= if @available_firmware.release_date do %>
|
||||
<span class="text-xs">
|
||||
(released {format_date(@available_firmware.release_date)})
|
||||
</span>
|
||||
<% end %>
|
||||
</p>
|
||||
</div>
|
||||
<div class="mt-3">
|
||||
<a
|
||||
href={@available_firmware.download_url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="inline-flex items-center rounded-md bg-blue-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-blue-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-600"
|
||||
>
|
||||
Download Firmware
|
||||
<.icon name="hero-arrow-top-right-on-square" class="ml-1.5 h-4 w-4" />
|
||||
</a>
|
||||
<%= if @available_firmware.changelog_url do %>
|
||||
<a
|
||||
href={@available_firmware.changelog_url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="ml-3 inline-flex items-center text-sm font-medium text-blue-600 dark:text-blue-400 hover:text-blue-500"
|
||||
>
|
||||
View Release Notes
|
||||
<.icon name="hero-arrow-top-right-on-square" class="ml-1 h-4 w-4" />
|
||||
</a>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6 items-start">
|
||||
<!-- Left Column: Device Information & Traffic -->
|
||||
<div class="space-y-6">
|
||||
|
|
@ -293,16 +242,83 @@
|
|||
<div class="flex justify-between py-1.5 border-b border-gray-100 dark:border-white/5">
|
||||
<dt class="text-sm text-gray-600 dark:text-gray-400">Operating System</dt>
|
||||
<dd class="text-sm font-medium text-gray-900 dark:text-white">
|
||||
{cond do
|
||||
@snmp_device.manufacturer && @snmp_device.firmware_version ->
|
||||
"#{@snmp_device.manufacturer} #{@snmp_device.firmware_version}"
|
||||
<div class="flex items-center gap-2">
|
||||
<span>
|
||||
{cond do
|
||||
@snmp_device.manufacturer && @snmp_device.firmware_version ->
|
||||
"#{@snmp_device.manufacturer} #{@snmp_device.firmware_version}"
|
||||
|
||||
@snmp_device.manufacturer ->
|
||||
@snmp_device.manufacturer
|
||||
@snmp_device.manufacturer ->
|
||||
@snmp_device.manufacturer
|
||||
|
||||
true ->
|
||||
@snmp_device.sys_descr || "N/A"
|
||||
end}
|
||||
true ->
|
||||
@snmp_device.sys_descr || "N/A"
|
||||
end}
|
||||
</span>
|
||||
<%= if firmware_update_available?(@snmp_device, @available_firmware) do %>
|
||||
<div class="group/firmware relative inline-flex items-center">
|
||||
<.icon
|
||||
name="hero-arrow-up-circle"
|
||||
class="h-5 w-5 text-blue-500 cursor-help dark:text-blue-400"
|
||||
/>
|
||||
<!-- Invisible bridge to prevent gap -->
|
||||
<div class="absolute left-0 top-0 w-full h-8 z-40"></div>
|
||||
<!-- Tooltip popup -->
|
||||
<div class="invisible group-hover/firmware:visible hover:visible absolute left-0 top-7 z-50 w-80 rounded-lg bg-white dark:bg-gray-800 shadow-xl ring-1 ring-black ring-opacity-5 dark:ring-white dark:ring-opacity-10 p-4">
|
||||
<div class="flex items-start gap-3">
|
||||
<.icon
|
||||
name="hero-arrow-up-circle"
|
||||
class="h-5 w-5 text-blue-500 dark:text-blue-400 flex-shrink-0 mt-0.5"
|
||||
/>
|
||||
<div class="flex-1 min-w-0">
|
||||
<h4 class="text-sm font-semibold text-gray-900 dark:text-white mb-2">
|
||||
Firmware Update Available
|
||||
</h4>
|
||||
<div class="space-y-1.5 text-xs text-gray-600 dark:text-gray-400">
|
||||
<div>
|
||||
<span class="font-medium">Current:</span>
|
||||
<span class="font-mono ml-1">{@snmp_device.firmware_version}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="font-medium">Latest:</span>
|
||||
<span class="font-mono ml-1 text-blue-600 dark:text-blue-400">
|
||||
{@available_firmware.version}
|
||||
</span>
|
||||
<%= if @available_firmware.release_date do %>
|
||||
<span class="text-gray-500 dark:text-gray-500">
|
||||
(released {format_date(@available_firmware.release_date)})
|
||||
</span>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-3 flex gap-2">
|
||||
<a
|
||||
href={@available_firmware.download_url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="inline-flex items-center gap-1 rounded px-2 py-1 text-xs font-medium bg-blue-600 text-white hover:bg-blue-700"
|
||||
>
|
||||
Download
|
||||
<.icon name="hero-arrow-top-right-on-square" class="h-3 w-3" />
|
||||
</a>
|
||||
<%= if @available_firmware.changelog_url do %>
|
||||
<a
|
||||
href={@available_firmware.changelog_url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="inline-flex items-center gap-1 rounded px-2 py-1 text-xs font-medium text-blue-600 dark:text-blue-400 hover:bg-blue-50 dark:hover:bg-blue-900/20"
|
||||
>
|
||||
Release Notes
|
||||
<.icon name="hero-arrow-top-right-on-square" class="h-3 w-3" />
|
||||
</a>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
|
||||
|
|
@ -790,7 +806,7 @@
|
|||
</.link>
|
||||
</dt>
|
||||
<dd class="text-sm font-medium text-gray-900 dark:text-white">
|
||||
<%= if sensor.latest_reading do %>
|
||||
<%= if sensor.latest_reading && sensor.latest_reading.value do %>
|
||||
{trunc(sensor.latest_reading.value)}
|
||||
<%= if sensor.sensor_unit && sensor.sensor_unit != "" do %>
|
||||
{sensor.sensor_unit}
|
||||
|
|
@ -826,7 +842,7 @@
|
|||
</.link>
|
||||
</dt>
|
||||
<dd class="text-sm font-medium text-gray-900 dark:text-white">
|
||||
<%= if sensor.latest_reading do %>
|
||||
<%= if sensor.latest_reading && sensor.latest_reading.value do %>
|
||||
<%= if sensor.sensor_type in ["clients", "ccq", "utilization"] do %>
|
||||
{trunc(sensor.latest_reading.value)}
|
||||
<% else %>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue