ProvisionPmgLxc
Orchestrates Proxmox Mail Gateway (PMG) LXC provisioning on a Proxmox host. Mirrors ProvisionNixosVm: static class with production NETSetup.Stages._5_NETSetupLinux.ProvisionPmgLxc.Run(Host,osisa.SystemManagement.Entities.Machine,NETSetup.Entities.NETSetupConfig,ISystemHost) wiring real deps and testable NETSetup.Stages._5_NETSetupLinux.ProvisionPmgLxc.RunCore(osisa.SystemManagement.Entities.Machine,System.String,System.String,System.Func{System.Boolean},System.Action{System.Int32,System.String},System.Action{System.Int32,System.String},System.Action{System.Int32},System.Func{System.Int32,System.String,System.String},ILogger) taking delegate seams.
public static class ProvisionPmgLxc
1. Fields
1.1. DebianTemplateName
Default Debian 12 LXC template name on Proxmox vztmpl repos.
public const string DebianTemplateName
1.2. DebianTemplateUrl
public const string DebianTemplateUrl
1.3. DefaultBridge
Default LXC bridge name on Proxmox hosts.
public const string DefaultBridge
1.4. LxcTemplateCacheDir
Cache directory where pveam downloads LXC templates.
public static readonly AbsolutePath LxcTemplateCacheDir
2. Methods
2.1. BuildPmgInstallScript(string, string)
Renders the apt-based PMG install script. Idempotent on apt level (apt-get install is a no-op when already installed). After install it applies the relay config (relay domain + transport → mail VM:25 + default smarthost) by appending the BuildPmgRelayConfigScript(string, string) body, so a fresh LXC is wired correctly on first boot.
public static string BuildPmgInstallScript(string customerDomain, string mailVmIp)
| Name | Type | Description |
|---|---|---|
customerDomain |
string |
|
mailVmIp |
string |
Returns: string
2.2. BuildPmgRelayConfigScript(string, string)
Renders the idempotent PMG relay-config script. Uses PMG’s OWN config DB via pmgsh (the supported mechanism that survives pmgconfig sync - editing /etc/pmg/main.cf directly does NOT, the old bug). Net effect:
-
relay domain: PMG accepts inbound mail for the customer domain.
-
transport: that domain is forwarded to \smtp:[mailVmIp]:25 (use_mx 0 = literal host, no MX lookup).
-
default smarthost (relay/relayport/relayprotocol) → mailVmIp:25, replacing the old broken :26.
Idempotent: each entry is deleted (failure swallowed if absent) before create, so a re-run overwrites to the desired state with no duplicates. The whole body is wrapped in coreutils timeout so a hung pmgconfig sync cannot stall the pct exec indefinitely (exit 124 has been observed). VERIFY on box: pmgsh paths /config/transport, /config/domains, /config/mail.
public static string BuildPmgRelayConfigScript(string customerDomain, string mailVmIp)
| Name | Type | Description |
|---|---|---|
customerDomain |
string |
|
mailVmIp |
string |
Returns: string
2.3. EnsureRelay(SystemManagement.Entities.Machine, NETSetupConfig, ISystemHost)
Every-cycle idempotent relay self-heal. Re-applies the PMG relay config (relay domain
transport → mail VM:25 + default smarthost) inside the running PMG LXC via pct exec. Best-effort: logs + swallows on failure so a flaky PMG exec never blocks the whole update. Runs regardless of whether the LXC was just created or already existed (drift correction).
public static void EnsureRelay(SystemManagement.Entities.Machine machine, NETSetupConfig config, ISystemHost sysHost)
| Name | Type | Description |
|---|---|---|
machine |
SystemManagement.Entities.Machine |
|
config |
NETSetupConfig |
|
sysHost |
ISystemHost |
2.4. EnsureRelayCore(int, string, string, Func<int, string, string>, ILogger)
Testable core for the every-cycle relay step. No-ops (warn) when the mail VM IP is not resolvable from config. Otherwise builds the idempotent relay script and runs it via the execInLxc seam, swallowing any failure (best-effort).
public static void EnsureRelayCore(int ctid, string customerDomain, string mailVmIp, Func<int, string, string> execInLxc, ILogger logger)
| Name | Type | Description |
|---|---|---|
ctid |
int |
|
customerDomain |
string |
|
mailVmIp |
string |
|
execInLxc |
Func<int, string, string> |
|
logger |
ILogger |
2.5. Run(Host, SystemManagement.Entities.Machine, NETSetupConfig, ISystemHost)
Production entry point. Wires real deps:
-
LXC existence check via
pct statuson the local Proxmox host. -
Template ensure via
pveam update + pveam download local debian-12-standard. -
LXC create via
LxcCommand+pct start. -
PMG install via
pct execrunning an apt-based install script.
public static ProvisionPmgLxcResult Run(Host proxmoxHost, SystemManagement.Entities.Machine machine, NETSetupConfig config, ISystemHost sysHost)
| Name | Type | Description |
|---|---|---|
proxmoxHost |
Host |
|
machine |
SystemManagement.Entities.Machine |
|
config |
NETSetupConfig |
|
sysHost |
ISystemHost |
Returns: ProvisionPmgLxcResult
2.6. RunCore(SystemManagement.Entities.Machine, string, string, Func<bool>, Action<int, string>, Action<int, string>, Action<int>, Func<int, string, string>, ILogger)
Testable core. Each external action is a delegate seam unit tests can stub. Sequence: extract ctid, lxcExists check (skip if true), ensureTemplate, createLxc, startLxc + status poll, execInLxc with the PMG install script.
public static ProvisionPmgLxcResult RunCore(SystemManagement.Entities.Machine machine, string customerDomain, string mailVmIp, Func<bool> lxcExists, Action<int, string> ensureTemplate, Action<int, string> createLxc, Action<int> startLxc, Func<int, string, string> execInLxc, ILogger logger)
| Name | Type | Description |
|---|---|---|
machine |
SystemManagement.Entities.Machine |
|
customerDomain |
string |
|
mailVmIp |
string |
|
lxcExists |
Func<bool> |
|
ensureTemplate |
Action<int, string> |
|
createLxc |
Action<int, string> |
|
startLxc |
Action<int> |
|
execInLxc |
Func<int, string, string> |
|
logger |
ILogger |
Returns: ProvisionPmgLxcResult