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)
Table 1. Parameters
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)
Table 2. Parameters
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)
Table 3. Parameters
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)
Table 4. Parameters
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 status on 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 exec running an apt-based install script.

public static ProvisionPmgLxcResult Run(Host proxmoxHost, SystemManagement.Entities.Machine machine, NETSetupConfig config, ISystemHost sysHost)
Table 5. Parameters
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)
Table 6. Parameters
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