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
22 changes: 9 additions & 13 deletions ADMIN_API_ENDPOINTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -1769,11 +1769,10 @@ Body:
// optional - if not provided, cost plan will be auto-created
"region_id": number,
"ip4_count": number,
// optional - IPv4 addresses included in this offer, default 1. Capped at 1
// until the guest network config can carry more than one address per family
// optional - IPv4 addresses included in this offer, default 1
"ip6_count": number,
// optional - IPv6 addresses included in this offer, default 1, capped at 1.
// Assignment is best-effort: a region with no IPv6 range still provisions
// optional - IPv6 addresses included in this offer, default 1. Assignment is
// best-effort: a region with no IPv6 range still provisions

// Cost plan auto-creation fields (used when cost_plan_id not provided)
"cost_plan_name": "string",
Expand Down Expand Up @@ -2042,12 +2041,11 @@ Body:
"min_ip4": number,
// optional - Minimum IPv4 addresses selectable, default 1
"max_ip4": number,
// optional - Maximum IPv4 addresses selectable, default 1. Capped at 1 until
// the guest network config can carry more than one address per family
// optional - Maximum IPv4 addresses selectable, default 1
"min_ip6": number,
// optional - Minimum IPv6 addresses selectable, default 1
"max_ip6": number,
// optional - Maximum IPv6 addresses selectable, default 1, capped at 1
// optional - Maximum IPv6 addresses selectable, default 1
"disk_iops_read": number,
// optional - Maximum disk read IOPS (omit or null = uncapped)
"disk_iops_write": number,
Expand Down Expand Up @@ -2120,12 +2118,11 @@ Body (all optional):
"min_ip4": number,
// optional - Minimum IPv4 addresses selectable, default 1
"max_ip4": number,
// optional - Maximum IPv4 addresses selectable, default 1. Capped at 1 until
// the guest network config can carry more than one address per family
// optional - Maximum IPv4 addresses selectable, default 1
"min_ip6": number,
// optional - Minimum IPv6 addresses selectable, default 1
"max_ip6": number,
// optional - Maximum IPv6 addresses selectable, default 1, capped at 1
// optional - Maximum IPv6 addresses selectable, default 1
"disk_iops_read": "number | null",
// Maximum disk read IOPS - send null to clear
"disk_iops_write": "number | null",
Expand Down Expand Up @@ -4852,12 +4849,11 @@ The RBAC system uses the following permission format: `resource::action`
"min_ip4": number,
// optional - Minimum IPv4 addresses selectable, default 1
"max_ip4": number,
// optional - Maximum IPv4 addresses selectable, default 1. Capped at 1 until
// the guest network config can carry more than one address per family
// optional - Maximum IPv4 addresses selectable, default 1
"min_ip6": number,
// optional - Minimum IPv6 addresses selectable, default 1
"max_ip6": number,
// optional - Maximum IPv6 addresses selectable, default 1, capped at 1
// optional - Maximum IPv6 addresses selectable, default 1
"disk_iops_read": number,
// Maximum disk read IOPS - omitted when uncapped
"disk_iops_write": number,
Expand Down
3 changes: 2 additions & 1 deletion API_CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,15 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

- **Admin can create a custom-spec VM** (issue #335) — `POST /api/admin/v1/vms/custom` creates a VM from an arbitrary spec for any user, billed against a custom pricing plan, instead of only from the fixed template catalog. Body is the customer custom-order shape plus `user_id`/`reason`: `pricing_id` (also decides the region), `cpu`, `memory` and `disk` in bytes, `disk_type`, `disk_interface`, optional `cpu_mfg`/`cpu_arch`/`cpu_feature`, `image_id`, `ssh_key_id` (must belong to the target user), optional `ref_code`. Returns `{ "job_id": string }`; the VM is created unpaid like a customer order. Unknown enum spellings are `400` and are never defaulted. Additive: `POST /api/admin/v1/vms` (template path) is unchanged.

- **A VM can hold more than one address per family** (issue #343) — every assignment is now placed on the VM's single NIC through a per-VM cloud-init network config (`cicustom network=`), instead of the built-in `ipconfig`, which carries only one IPv4 and one IPv6 per interface. A VM with one address per family is unaffected and keeps its existing `ipconfig`, so nothing already deployed is reconfigured. The interface is matched by MAC so the config does not depend on guest device naming; one default route per family is emitted (the first assignment's gateway wins, as before); a SLAAC range contributes router advertisement rather than a pinned address. No response shape changed — the offered maximum is now simply whatever an operator sets on the plan or template.

- **IP counts are a sellable dimension of an offer** (issue #105) — a VM offer now states how many addresses it includes instead of implying exactly one IPv4 plus an optional IPv6.
- `ApiVmTemplate` (in `GET /api/v1/vm/templates`, `GET /api/v1/vm/{id}` and every VM listing) gains `ip4_count` and `ip6_count`. Additive; nothing existing changed shape.
- `GET /api/v1/vm/custom-template` params gain `min_ip4`/`max_ip4`/`min_ip6`/`max_ip6`. `max_ip4` is already capped by what the region can actually supply, so a client can use it directly as a selector bound, and a plan whose `min_ip4` cannot be met is not listed at all.
- `POST /api/v1/vm/custom-template` (and the price quote) accept `ip4_count`/`ip6_count`. Both default to `1` when omitted, so existing clients order exactly what they did before. Counts outside the plan's range are rejected. Cost is `count × ip4_cost` / `count × ip6_cost`, so the quote for a multi-address order is higher than before by exactly the extra addresses.
- Custom VM billing now takes the counts from the ordered spec rather than counting the VM's current assignments. Existing VMs are migrated to the counts they currently hold, so no live renewal amount moves.
- Provisioning allocates the stated number of addresses across the region's ranges. IPv4 is all-or-nothing: an order asking for addresses the region cannot supply fails instead of quietly getting fewer. IPv6 remains best-effort, unchanged — a region without an IPv6 range still provisions, and a SLAAC range yields one address per VM.
- Admin: `ip4_count`/`ip6_count` on VM template create/update/read, and `min_ip4`/`max_ip4`/`min_ip6`/`max_ip6` on custom pricing create/update/read/copy. All default to 1, so no plan starts offering a choice until an operator widens it.
- **The offered maximum is capped at 1 address per family for now** — a guest gets one cloud-init `ipconfig` entry, which carries a single IPv4 and a single IPv6, so a wider offer would allocate and bill for addresses that never reach the VM. `ip4_count`/`ip6_count` above 1 on a template, and `max_ip4`/`max_ip6` above 1 on a custom pricing plan, are refused with a `400`, and an order for more than 1 is refused even if a stored plan's range would allow it. The cap lifts once the guest-side network config can carry more than one address per family (issue #343).

- **`host_ssh_keys` on VM status** (issue #154) — `GET /api/v1/vm/{id}` and `GET /api/v1/vm` now return the VM's own SSH host keys as `[{ key_type, public_key, fingerprint_sha256 }]`, so a client can verify the host on first connect instead of accepting whatever key answers. The list is empty until the keys are captured (scanned from the host once the VM is running, public keys only) and is re-captured after a reinstall, which regenerates them. Additive: no existing field changed. Not to be confused with `ssh_key`, which is the customer's authorized key.

Expand Down
Loading
Loading