Sell IP counts as a template/custom-pricing dimension - #339
Conversation
|
|
||
| // The router-generated vMAC is per-VM, not per-address, so it is only | ||
| // generated for the first assignment; later ones reuse the VM's MAC. | ||
| self.assign_mac(&mut assignment).await?; |
There was a problem hiding this comment.
This is an assumption, in some cases each IP needs its own vMAC, meaning we would have to attach multiple nics to be able to use additional ips, please dig into this more before making assumptions
There was a problem hiding this comment.
You are right, and it is worse than the vMAC question — the guest side cannot carry a second address at all today, so I am not shipping the provisioning half as it stands.
What I found:
lnvps_api/src/host/proxmox.rs:979-1049builds exactly one NIC (net0) and joins every assignment into oneipconfig0. Withip4_count = 2make_configemitsipconfig0=ip=185.18.221.65/24,gw=…,ip6=auto,ip=185.18.221.66/24,gw=…— threeip=keys in one property string. Proxmoxipconfig[n]takes at most oneip=and oneip6=, one index per net device, so the extra address is dropped (and the reconcile compare at:1768would then churn against it forever). Verified by runningmake_configwith two v4 assignments, not by reading alone.lnvps_api/src/provisioner/vm.rs:1026-1047:assign_macgenerates the vMAC for the first v4 only and every later address is added to that same vMAC viaadd_arp_entry(router/ovh.rs:113→POST virtualMac/{mac}/virtualAddress). That is the assumption you are pointing at.
I cannot confirm OVH's own rule from here — whether one vMAC may legitimately hold N addresses or whether each address needs its own vMAC — I have no OVH credentials and the docs/API console are not reachable from this machine. But it does not change the verdict: even if OVH accepted it, the guest never gets address 2.
Prod evidence that this path has never run: of 412 live IP assignments there is not one VM with more than one live IPv4, and not one vMAC holding more than one address (GET /api/admin/v1/vm_ip_assignments, all pages, grouped by vm_id and by the mac=ip arp_ref prefix).
Multi-address provisioning therefore needs a NIC per address — net1/ipconfig1…, one vMAC per NIC, per-NIC ARP entries and the matching capacity/reconcile handling — which is a separate piece of work from selling the dimension.
Proposal, happy to be overruled: keep this PR to schema, pricing, capacity accounting and the API/admin surface, and refuse an order above 1 address until the multi-NIC work lands, so nothing can be sold that would not be configured. The multi-NIC provisioning becomes its own issue where the OVH vMAC rule gets settled first with your account access. Say the word if you would rather I grow this branch to do the multi-NIC work instead.
6d69637 to
f77ab1a
Compare
Closes #105.
Offers now state how many addresses they include, instead of every offer implying exactly one IPv4 plus a best-effort IPv6.
Schema (
lnvps_db/migrations/20260730130000_template_ip_counts.sql) —vm_template.ip4_count/ip6_count,vm_custom_template.ip4_count/ip6_count,vm_custom_pricing.min_ip4/max_ip4/min_ip6/max_ip6, all defaulting to 1 so nothing starts offering a choice until an operator widens a plan. Existing custom VMs are backfilled from the addresses they actually hold, and each plan's range is widened to cover its own existing VMs, so no live renewal amount moves and no grandfathered VM fails spec validation on upgrade.Billing — custom VM cost now multiplies the ordered counts by
ip4_cost/ip6_cost(lnvps_api_common/src/pricing.rs:757-806) instead of counting current assignments with a.max(1)floor. That was unquotable before a VM existed and would have moved the price whenever provisioning held fewer addresses than were sold. Counts are range-checked at order/upgrade time (:855-872), and an upgrade carries the sold counts over untouched (:1330-1345,lnvps_api/src/provisioner/vm.rs:938-952— the second builder, which is what the upgrade actually persists).Capacity —
can_accommodaterequiresip4_countfree addresses region-wide rather than>= 1(lnvps_api_common/src/capacity.rs:438-465), and custom plan params getmax_ip4clamped to what the region can supply, with plans whosemin_ip4cannot be met dropped from the listing (:200-240).Provisioning —
pick_ips_for_regionallocates N addresses across the region's ranges, excluding what it has already picked in the same pass (nothing is persisted until the whole assignment succeeds), andassign_ipsloops instead of taking one of each (lnvps_api_common/src/network.rs:69-140,lnvps_api/src/provisioner/vm.rs:1056-1105).Two deliberate calls, both worth arguing with:
ip6_countis billed but not guaranteed — same as the old.max(1)behaviour, now explicit in the docs and the API comment.ip4_countonVmTemplatedrives capacity and provisioning; a standard plan's price is still its flat cost plan, so an operator selling extra addresses prices them into the plan.Tests: unit for the picker (distinct addresses, refusal when short, IPv6-absent region), capacity gating, price scaling and range validation, request defaults;
assign_ipscovered directly against the mock host (lnvps_api/src/provisioner/vm.rs:2405-2530). e2e orders a 2x IPv4 custom VM, asserts the counts land on the VM and the billed amount includes both addresses, and that an out-of-range count is refused (lnvps_e2e/src/lifecycle.rs:1648-1705).cargo test --workspacegreen at ceae93e (excludinglnvps_e2e, which needs the live stack); full e2e 165 passed in 15.9s; clippy clean on every line touched.Docs:
API_CHANGELOG.mdandADMIN_API_ENDPOINTS.md. Additive — no existing field changed shape.Follow-ups, not in scope here:
CustomVmSpec(the admin custom-create spec added in #337) needs the same two fields once that lands, which folds into #338; per-order/per-account caps beyond region capacity are still unsettled in the issue; web#92 and admin#14 can now build againstmin_ip4/max_ip4andip4_count.Originating channel: general (
721a8a6c-c36c-5fa1-ae07-822ddc8567c5).