two/api/agent.yaml
GnomeZworc 0237acba81
f-33: net: add multi net in dispatch et vms #33
Signed-off-by: GnomeZworc <nicolas.boufidjeline@g3e.fr>
2026-08-25 22:48:13 +02:00

579 lines
17 KiB
YAML

openapi: "3.1.0"
info:
title: Two API
version: "0.1.0"
description: REST API for managing VPCs and Subnets in the Two orchestrator.
servers:
- url: http://localhost:8080
description: Local development server
paths:
# ── VPC ────────────────────────────────────────────────────────────────────
/vpcs:
get:
summary: List all VPCs
operationId: listVPCs
responses:
"200":
description: List of VPCs
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/VPC"
"500":
$ref: "#/components/responses/InternalError"
post:
summary: Create a VPC
operationId: createVPC
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/VPCCreateRequest"
responses:
"202":
description: VPC creation accepted
content:
application/json:
schema:
$ref: "#/components/schemas/VPC"
"400":
description: Missing required field or invalid request body
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
"409":
description: VPC already exists
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
"500":
$ref: "#/components/responses/InternalError"
/vpcs/{name}:
parameters:
- $ref: "#/components/parameters/ResourceName"
get:
summary: Get VPC status and info
operationId: getVPC
responses:
"200":
description: VPC found
content:
application/json:
schema:
$ref: "#/components/schemas/VPC"
"404":
$ref: "#/components/responses/NotFound"
"500":
$ref: "#/components/responses/InternalError"
delete:
summary: Delete a VPC
operationId: deleteVPC
responses:
"202":
description: VPC deletion accepted
content:
application/json:
schema:
$ref: "#/components/schemas/VPC"
"404":
$ref: "#/components/responses/NotFound"
"409":
description: VPC not deletable — only running or error states can be deleted, and all its subnets must be deleted first
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
"500":
$ref: "#/components/responses/InternalError"
# ── VM ─────────────────────────────────────────────────────────────────────
/vms:
get:
summary: List all VMs
operationId: listVMs
responses:
"200":
description: List of VMs
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/VM"
"500":
$ref: "#/components/responses/InternalError"
post:
summary: Start a VM
operationId: startVM
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/VMCreateRequest"
responses:
"202":
description: VM start accepted
content:
application/json:
schema:
$ref: "#/components/schemas/VM"
"400":
description: Missing required field or invalid request body
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
"409":
description: VM already exists
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
"422":
description: Subnet not found, or not in creating/running state
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
"500":
$ref: "#/components/responses/InternalError"
/vms/{name}:
parameters:
- $ref: "#/components/parameters/ResourceName"
get:
summary: Get VM status and info
operationId: getVM
responses:
"200":
description: VM found
content:
application/json:
schema:
$ref: "#/components/schemas/VM"
"404":
$ref: "#/components/responses/NotFound"
"500":
$ref: "#/components/responses/InternalError"
delete:
summary: Stop a VM
operationId: stopVM
responses:
"202":
description: VM stop accepted
content:
application/json:
schema:
$ref: "#/components/schemas/VM"
"404":
$ref: "#/components/responses/NotFound"
"409":
description: VM not stoppable — only running or error states can be stopped
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
"500":
$ref: "#/components/responses/InternalError"
# ── Subnet ─────────────────────────────────────────────────────────────────
/subnets:
get:
summary: List all subnets
operationId: listSubnets
responses:
"200":
description: List of subnets
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/Subnet"
"500":
$ref: "#/components/responses/InternalError"
post:
summary: Create a subnet
operationId: createSubnet
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/SubnetCreateRequest"
responses:
"202":
description: Subnet creation accepted
content:
application/json:
schema:
$ref: "#/components/schemas/Subnet"
"400":
description: Missing required field or unknown iface_type
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
"409":
description: Subnet already exists
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
"422":
description: Parent VPC does not exist or is not ready
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
"500":
$ref: "#/components/responses/InternalError"
/subnets/{name}:
parameters:
- $ref: "#/components/parameters/ResourceName"
get:
summary: Get subnet status and info
operationId: getSubnet
responses:
"200":
description: Subnet found
content:
application/json:
schema:
$ref: "#/components/schemas/Subnet"
"404":
$ref: "#/components/responses/NotFound"
"500":
$ref: "#/components/responses/InternalError"
delete:
summary: Delete a subnet
operationId: deleteSubnet
responses:
"202":
description: Subnet deletion accepted
content:
application/json:
schema:
$ref: "#/components/schemas/Subnet"
"404":
$ref: "#/components/responses/NotFound"
"409":
description: Subnet not deletable — only running or error states can be deleted
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
"500":
$ref: "#/components/responses/InternalError"
# ── Components ──────────────────────────────────────────────────────────────
components:
parameters:
ResourceName:
name: name
in: path
required: true
schema:
type: string
description: Resource name
schemas:
VPCCreateRequest:
type: object
required: [name, cidr]
properties:
name:
type: string
description: Unique name for the VPC, must follow the format vp-[id]
pattern: '^vp-.+'
example: vp-00001
cidr:
type: string
description: CIDR block for the entire VPC address space
example: "10.0.0.0/16"
VPC:
type: object
properties:
name:
type: string
example: vp-00001
state:
type: string
enum: [creating, running, error, deleting, deleted]
example: running
cidr:
type: string
example: "10.0.0.0/16"
SubnetCreateRequest:
type: object
required: [name, vpc, interface_ip, cidr]
properties:
name:
type: string
description: Unique name for the subnet
example: sn-00001
vpc:
type: string
description: Parent VPC name
example: vpc1
mode:
type: string
description: >
Subnet mode. "vxlan" (default): creates a VXLAN tunnel and a host bridge.
"bridge": attaches directly to an existing bridge resolved from iface_type in the agent config.
"public_ip": accepted and routed like vxlan for DHCP purposes, but its host network
setup is not implemented yet — creating such a subnet currently fails in Execute.
"vlan" is reserved for future use.
enum: [vxlan, bridge, public_ip]
default: vxlan
example: vxlan
vxlan_id:
type: integer
description: VXLAN VNI identifier. Required when mode is "vxlan", ignored otherwise.
example: 100
iface_type:
type: string
description: Interface type key defined in the agent config (e.g. vms, internet, admin). Falls back to default_interface if omitted or unknown.
example: vms
interface_ip:
type: string
format: ipv4
description: Gateway IP for the subnet
example: "10.10.10.1"
cidr:
type: string
description: Subnet CIDR block
example: "10.10.10.0/24"
default_route:
type: boolean
description: >
A default route is always advertised via DHCP; this flag only chooses its next-hop.
When false, the next-hop is the subnet interface_ip. When true, it is the `gateway`
field if supplied, otherwise the gateway read from the host routing table.
The route to the VPC CIDR always keeps interface_ip as its next-hop (except in bridge
mode, which has no VPC route), so VPC traffic never leaves through a public gateway.
default: false
gateway:
type: string
format: ipv4
description: >
Optional next-hop for the default route. Only used when default_route is true;
supplied with default_route false, it is ignored. Not validated by the agent:
reachability and coherence with the subnet CIDR are the caller's responsibility.
example: "10.10.10.254"
Subnet:
type: object
properties:
name:
type: string
example: sn-00001
state:
type: string
enum: [creating, running, error, deleting, deleted]
example: running
vpc:
type: string
example: vpc1
mode:
type: string
enum: [vxlan, bridge, public_ip]
example: vxlan
vxlan_id:
type: integer
description: VXLAN VNI. Present only when mode is "vxlan".
example: 100
local_iface:
type: string
description: Resolved interface name from agent config
example: br-000000
interface_ip:
type: string
example: "10.10.10.1"
cidr:
type: string
example: "10.10.10.0/24"
default_route:
type: boolean
example: false
gateway:
type: string
example: "10.10.10.254"
VMCreateRequest:
type: object
required: [name, interfaces, storage]
properties:
name:
type: string
example: vm-00001
memory:
type: integer
description: Memory in MB (default 512)
example: 1024
cpus:
type: integer
description: Number of vCPUs (default 1)
example: 2
metadata:
$ref: "#/components/schemas/VMMetadata"
interfaces:
type: array
minItems: 1
description: >
Network interfaces, in order. The position determines the PCI slot
(0x03 + index) and therefore the interface name inside the guest.
**Exactly one** interface must be marked primary: it carries the
default route and the metadata server. All subnets must belong to
the same VPC.
items:
$ref: "#/components/schemas/VMInterface"
storage:
type: array
minItems: 1
items:
$ref: "#/components/schemas/VMStorage"
uefi:
type: boolean
description: Boot with UEFI firmware (OVMF). Defaults to false (SeaBIOS).
example: false
VMMetadata:
type: object
description: >
cloud-init inputs for this VM. Every field is optional. Documents not
supplied here fall back to the agent's built-in templates; a document
supplied as an empty string is served empty, which is not the same
thing.
properties:
password:
type: string
description: >
Password *hash* for the syonad account, as expected by the
cloud-config `passwd` key (e.g. `$6$...`) — not a plaintext
password. When omitted, the account is created locked
(`lock_passwd: true`); when neither password nor sshkey is given,
no account is created at all.
example: "$6$rounds=4096$saltsalt$hash..."
sshkey:
type: string
description: Public SSH key added to the syonad account. Sent as-is, not encoded.
example: "ssh-ed25519 AAAA..."
user_data:
type: string
format: byte
description: >
cloud-init user-data, **base64-encoded**. Encoding keeps multi-line
documents out of JSON escaping and allows gzip+base64 payloads.
Invalid base64 is rejected with 400 rather than silently serving an
empty document. The agent never interprets this content.
example: "I2Nsb3VkLWNvbmZpZwpwYWNrYWdlczoKICAtIG5naW54Cg=="
VMInterface:
type: object
required: [subnet, ip, primary]
properties:
subnet:
type: string
example: sn-00001
ip:
type: string
format: ipv4
example: "10.0.0.5"
primary:
type: boolean
example: true
VMStorage:
type: object
required: [path, dev]
properties:
path:
type: string
description: Path to the disk image on the host
example: /var/lib/two/volumes/abc.qcow2
dev:
type: string
description: Device name inside the VM
pattern: '^[sv]d[a-z]$'
example: vda
VM:
type: object
properties:
name:
type: string
example: vm-00001
state:
type: string
enum: [creating, running, error, deleting, deleted]
example: running
metadata_port:
type: string
example: "80"
memory:
type: integer
example: 1024
cpus:
type: integer
example: 2
interfaces:
type: array
items:
$ref: "#/components/schemas/VMInterface"
storage:
type: array
items:
$ref: "#/components/schemas/VMStorage"
uefi:
type: boolean
example: false
Error:
type: object
properties:
error:
type: string
example: "resource not found"
responses:
NotFound:
description: Resource not found
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
InternalError:
description: Internal server error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"