Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion FEATURES.md
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,8 @@ Gateway tweaks for UCG-Fiber, UXG-Fiber, UCG-Max, and UXG-Max (Gateway SSH).
- **Re-address Networks** - move networks to new subnets by pattern or per network, with every address in them: reservations, DNS records, port forwards, firewall groups and policies, routes, device static IPs, and Network Optimizer's own settings
- Addresses on a WAN's carrier side or a VPN's far side stay as they are
- The dry run lists every connected client with no DHCP lease from the gateway (Gateway SSH), reserved or not, and offline clients likely set on the device
- Warns when Network Optimizer's host, a management network, or the console moves, and names devices that need a new inform address
- Warns when Network Optimizer's host, a management network, or the console moves
- Devices find the Console at its new address: the Plan moves a UniFi OS Server's Inform Host override with it, and a Device still offline after the move gets set-inform over SSH (Device SSH)
- **Lower Lease Times** a lease ahead so clients move within minutes (the move puts them back), and **Reconnect Clients** after it: wireless clients rejoin, PoE-powered wired clients optionally power-cycle
- Alerts for security regressions, changes no admin made, management-path changes, drift, and UniFi Console admin and update settings
- **Settings** - retention, change detection, commit-confirmed window, approvals
Expand Down
2 changes: 2 additions & 0 deletions src/NetworkOptimizer.Storage/Models/Identity/AuditEvent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,8 @@ public static class AuditActions
public const string ChangeControlPlanReverted = "change_control.plan.reverted";
public const string ChangeControlPlanCancelled = "change_control.plan.cancelled";
public const string ChangeControlLockoutOverridden = "change_control.lockout.overridden";
/// <summary>Network Optimizer ran set-inform over SSH on a device still offline after a re-address.</summary>
public const string ChangeControlDeviceInformSet = "change_control.device.inform_set";
public const string ChangeControlBaselineSet = "change_control.baseline.set";
public const string ChangeControlSettingsChanged = "change_control.settings.changed";
public const string ChangeControlDevicesAdopted = "change_control.devices.adopted";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<p class="cc-card-intro">Moves each network to a new subnet, with every address in it: reservations, DNS records, port forwards, firewall matches, routes, device static IPs, and Network Optimizer's own settings. Each address keeps its host part.</p>
<div class="card-body">
<div class="form-group cc-readdress-pattern">
<label class="form-label">Pattern <span class="tooltip-icon tooltip-icon-sm" data-tooltip="{vlan} is the network's VLAN ID (1 when untagged); {octet3} is its current third octet. Shorthand works too: 10.42.x.x or 10.42.*.0 puts the VLAN ID in the third octet, and no /n keeps each network's prefix.">?</span></label>
<label class="form-label">Pattern <span class="tooltip-icon tooltip-icon-sm" data-tooltip="{vlan} is the network's VLAN ID;{octet3} is its current third octet. Shorthand works too: 10.42.x.x or 10.42.*.0 puts the VLAN ID in the third octet, and no /n keeps each network's prefix.">?</span></label>
<div class="input-with-button">
<input class="form-control" placeholder="10.42.{vlan}.0/24" @bind="_pattern" @bind:event="oninput" @onkeydown="PatternKeyAsync" />
<button class="btn btn-contrast" disabled="@(_busy || string.IsNullOrWhiteSpace(_pattern))" @onclick="ApplyPatternAsync">Apply Pattern</button>
Expand All @@ -37,7 +37,7 @@
@foreach (var m in _view.Moves)
{
<div class="cc-map-card">
<div class="cc-change-card-head"><b>@m.NetworkName</b><span class="muted-text">@(m.Vlan is { } vl ? $"VLAN {vl}" : "Untagged")</span></div>
<div class="cc-change-card-head"><b>@m.NetworkName</b><span class="muted-text">VLAN @(m.Vlan ?? 1)</span></div>
<div class="cc-item-detail">Currently <code>@m.OldCidr</code></div>
<input class="form-control cc-port-select" value="@NewFor(m)" placeholder="New subnet"
@oninput="e => Typed(m.NetworkId, e.Value?.ToString())"
Expand All @@ -53,7 +53,7 @@
{
<tr>
<td>@m.NetworkName</td>
<td>@(m.Vlan?.ToString() ?? "Untagged")</td>
<td>@(m.Vlan ?? 1)</td>
<td><code>@m.OldCidr</code></td>
<td>
<input class="form-control cc-port-select" value="@NewFor(m)" placeholder="@m.OldCidr"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,9 @@ else
<div class="cc-badge-col"><span class="status-badge status-error">Lockout</span></div>
<div>
<div class="cc-item-title">@lockout.Title</div>
<div class="cc-item-detail">@lockout.Detail @(lockout != doc.Lockouts.Last() ? "" : PlanCoordinator.MovesConsolePath(doc) ? "Network devices drop until the UniFi Console is at its new address. Network Optimizer alerts instead of reverting this plan."
: PlanCoordinator.AlertsInsteadOfRevert(doc) ? "Network devices drop while they move to their new addresses. Network Optimizer alerts instead of reverting this plan."
<div class="cc-item-detail">@lockout.Detail @(lockout != doc.Lockouts.Last() ? "" : PlanCoordinator.MovesConsolePath(doc) && PlanCoordinator.ConsoleFollows(doc) ? "Network Devices drop until the Gateway, which runs the UniFi Console, provisions its new addresses. Network Optimizer alerts instead of reverting this Plan."
: PlanCoordinator.MovesConsolePath(doc) ? "Network Devices drop until the UniFi Console is at its new address. Network Optimizer alerts instead of reverting this Plan."
: PlanCoordinator.AlertsInsteadOfRevert(doc) ? "Network Devices drop while they move to their new addresses. Network Optimizer alerts instead of reverting this Plan."
: watchDevices ? $"If a network device goes offline, Network Optimizer reverts the plan within {_view.WatchMinutes} minutes." : "")</div>
@if (lockout == doc.Lockouts.Last() && editable)
{
Expand Down Expand Up @@ -459,13 +460,15 @@ else
<b>Commit-confirmed: @_view.WatchMinutes minutes.</b>
@if (PlanCoordinator.AlertsInsteadOfRevert(doc))
{
<span> Network Optimizer watches @(watchDevices ? "every network device" : "")@(watchDevices && watchInternet ? " and " : "")@(watchInternet ? "internet reachability" : ""), and alerts if either fails. It does not revert this plan: @(PlanCoordinator.MovesConsolePath(doc)
? "network devices reach the console at its old address until the console moves too."
: "network devices drop while they move to their new addresses, and a revert would strand them.")</span>
<span> Network Optimizer watches @(watchDevices ? "every Network Device" : "")@(watchDevices && watchInternet ? " and " : "")@(watchInternet ? "internet reachability" : ""), and alerts if either fails. It does not revert this Plan: @(PlanCoordinator.MovesConsolePath(doc)
? PlanCoordinator.ConsoleFollows(doc)
? "Network Devices drop until the Gateway, which runs the UniFi Console, provisions its new addresses."
: "Network Devices reach the UniFi Console at its old address until it moves too."
: "Network Devices drop while they move to their new addresses, and a revert would strand them.")</span>
}
else
{
<span> Network Optimizer watches @(watchDevices ? "every network device" : "")@(watchDevices && watchInternet ? " and " : "")@(watchInternet ? "internet reachability" : ""). If either fails, it reverts the plan and alerts.</span>
<span> Network Optimizer watches @(watchDevices ? "every Network Device" : "")@(watchDevices && watchInternet ? " and " : "")@(watchInternet ? "internet reachability" : ""). If either fails, it reverts the Plan and alerts.</span>
}
}
else
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,10 @@ private ChangeControlSiteEngine Build(string slug)
? wans.Select(w => w.NetworkGroup).ToHashSet(StringComparer.OrdinalIgnoreCase)
: null;
coordinator.MoveSettings = (moves, remote, ct) => ReaddressSettingsAsync(slug, moves, remote, apply: true, ct);
coordinator.DeviceSshConfigured = async _ => await _sp.GetRequiredService<UniFiSshRegistry>().GetFor(slug).GetSettingsAsync() is { Enabled: true, HasCredentials: true };
coordinator.GatewaySshConfigured = async _ => await _sp.GetRequiredService<GatewaySshRegistry>().GetFor(slug).GetSettingsAsync() is { Enabled: true, HasCredentials: true };
coordinator.RunDeviceSsh = (target, command, timeout, ct) =>
_sp.GetRequiredService<Ssh.DeviceSshRouterRegistry>().GetFor(slug).RunAsync(target, command, timeout, ct);
coordinator.ZonesChanged = _ =>
{
using var scope = _sp.CreateScope();
Expand Down
Loading
Loading