Skip to content

Commit b00f29d

Browse files
committed
Add udm-boot status check and install to WAN Steering (#591)
* Add udm-boot status check and install to WAN Steering Without udm-boot, the boot script that restores iptables rules won't run after a reboot or firmware update, silently breaking WAN Steering. Adds the same pattern used by Performance Tweaks and Adaptive SQM: status indicator in the metrics bar, warning banner with install button. Closes #589 * Fix DI: inject concrete SqmDeploymentService, not the interface SqmDeploymentService is registered as the concrete type in DI, not as ISqmDeploymentService.
1 parent 3d27a18 commit b00f29d

2 files changed

Lines changed: 57 additions & 0 deletions

File tree

src/NetworkOptimizer.Web/Components/Pages/WanSteering.razor

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
@inject IGatewaySshService GatewaySshService
77
@inject ILogger<WanSteering> Logger
88
@inject PullToRefreshState PtrState
9+
@inject SqmDeploymentService SqmDeploymentService
910
@rendermode InteractiveServer
1011
@using NetworkOptimizer.Storage.Models
1112
@using NetworkOptimizer.Web.Services.Ssh
@@ -84,6 +85,20 @@
8485
@(_status.BinaryDeployed ? "Deployed" : "Not Deployed")
8586
</div>
8687
</div>
88+
<div class="metric">
89+
<div class="metric-label">UDM Boot</div>
90+
<div class="metric-value">
91+
<span class="status-indicator @(_status.UdmBootInstalled ? "status-active" : "status-inactive")"></span>
92+
@if (_status.UdmBootInstalled)
93+
{
94+
<span>@(_status.UdmBootEnabled ? "Enabled" : "Installed")</span>
95+
}
96+
else
97+
{
98+
<span>Not Installed</span>
99+
}
100+
</div>
101+
</div>
87102
@if (_deployedAt.HasValue && (DateTime.UtcNow - _deployedAt.Value).TotalSeconds < 45)
88103
{
89104
<div class="metric">
@@ -124,6 +139,23 @@
124139
Gateway is running @(_parsedStatus.Version) but the app is @_appVersion. Redeploy to update.
125140
</div>
126141
}
142+
@if (_status.UdmBootInstalled != true)
143+
{
144+
<div class="alert alert-warning" style="margin-top: 1rem;">
145+
<strong>UDM Boot Required:</strong> WAN Steering uses a boot script in <code>/data/on_boot.d/</code> that requires udm-boot to persist across reboots. Without it, iptables rules will silently stop being applied after a reboot or firmware update.
146+
<button class="btn btn-sm btn-primary" style="margin-left: 0.5rem;" @onclick="InstallUdmBoot" disabled="@_isInstallingUdmBoot">
147+
@if (_isInstallingUdmBoot)
148+
{
149+
<span class="spinner spinner-sm"></span>
150+
<span>Installing...</span>
151+
}
152+
else
153+
{
154+
<span>Install UDM Boot</span>
155+
}
156+
</button>
157+
</div>
158+
}
127159
}
128160
</div>
129161
</div>
@@ -499,6 +531,7 @@
499531
private bool _isLoading = true;
500532
private bool _isDeploying;
501533
private bool _isDiscovering = true;
534+
private bool _isInstallingUdmBoot;
502535
private bool _gatewayConfigured;
503536
private string? _deployMessage;
504537
private bool _deploySuccess;
@@ -622,6 +655,24 @@
622655
}
623656
}
624657

658+
private async Task InstallUdmBoot()
659+
{
660+
_isInstallingUdmBoot = true;
661+
StateHasChanged();
662+
663+
try
664+
{
665+
var result = await SqmDeploymentService.InstallUdmBootAsync();
666+
if (result.success)
667+
await RefreshStatusAsync();
668+
}
669+
finally
670+
{
671+
_isInstallingUdmBoot = false;
672+
StateHasChanged();
673+
}
674+
}
675+
625676
private async Task DiscoverWansAsync()
626677
{
627678
_isDiscovering = true;

src/NetworkOptimizer.Web/Services/WanSteerDeploymentService.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ public async Task<WanSteerStatus> GetStatusAsync()
4747
try
4848
{
4949
var combinedCommand =
50+
"echo '---UDM_BOOT---'; test -f /etc/systemd/system/udm-boot.service && echo 'installed' || echo 'missing'; " +
51+
"echo '---UDM_BOOT_ENABLED---'; systemctl is-enabled udm-boot 2>/dev/null || echo 'disabled'; " +
5052
"echo '---PROCESS---'; pgrep -x wansteer > /dev/null 2>&1 && echo running || echo stopped; " +
5153
"echo '---STATUS---'; cat /tmp/wan-steer-status.json 2>/dev/null || echo '{}'; echo; " +
5254
"echo '---VERSION---'; /data/wan-steer/wansteer -version 2>/dev/null || echo 'not installed'; " +
@@ -55,6 +57,8 @@ public async Task<WanSteerStatus> GetStatusAsync()
5557
var result = await _gatewaySsh.RunCommandAsync(combinedCommand, TimeSpan.FromSeconds(15));
5658
var sections = ParseDelimitedOutput(result.output);
5759

60+
status.UdmBootInstalled = GetSection(sections, "UDM_BOOT").Trim() == "installed";
61+
status.UdmBootEnabled = GetSection(sections, "UDM_BOOT_ENABLED").Trim() == "enabled";
5862
status.IsRunning = result.success && GetSection(sections, "PROCESS").Trim() == "running";
5963
status.StatusJson = GetSection(sections, "STATUS").Trim();
6064
if (status.StatusJson == "{}") status.StatusJson = null;
@@ -558,6 +562,8 @@ internal static string GetSection(Dictionary<string, string> sections, string ke
558562

559563
public class WanSteerStatus
560564
{
565+
public bool UdmBootInstalled { get; set; }
566+
public bool UdmBootEnabled { get; set; }
561567
public bool IsRunning { get; set; }
562568
public string? Version { get; set; }
563569
public string? StatusJson { get; set; }

0 commit comments

Comments
 (0)