# xen-orchestra.dadl -- Xen Orchestra REST API (XO 6.4+) # DADL backend for ToolMesh # # Domain Notes for LLM consumers: # - Xen Orchestra (XO) is the web management UI for XCP-ng / XenServer hypervisors. # - The REST API is at /rest/v0/ and stable since XO 6.4 (April 2026, RBAC v2 added); current through XO 6.6 (June 2026). # - Swagger docs available at /rest/v0/docs (OpenAPI spec for runtime discovery). # - Access control: pre-6.4 was admin-only; XO 6.4+ uses fine-grained RBAC v2 (acl-privileges/acl-roles). # Endpoints with no declared privilege still require admin -- see /users/me/acl-privileges to inspect yours. # - A "pool" is a cluster of XCP-ng hosts sharing storage. Use /pools to discover them. # - A "host" is a physical XCP-ng hypervisor machine within a pool. # - A "VM" is a virtual machine identified by UUID. # - A "VM template" is a stamp from which new VMs are cloned (separate /vm-templates collection). # - A "VM controller" is the dom0 control-domain VM of a host (separate /vm-controllers collection, XO 6.5+). # - A "VM snapshot" is a point-in-time copy of a VM (separate /vm-snapshots collection). # - A "VDI snapshot" is a point-in-time copy of a VDI (separate /vdi-snapshots collection). # - A "SR" (Storage Repository) is a storage backend (local disk, NFS, iSCSI, etc.). # - A "VDI" (Virtual Disk Image) is a virtual disk stored on an SR. # - A "VBD" (Virtual Block Device) connects a VDI to a VM (disk attachment). # - A "VIF" is a Virtual Interface (network adapter attached to a VM). # - A "PIF" is a Physical Interface (physical NIC on a host). # - A "PBD" (Physical Block Device) connects an SR to a host. # - A "PCI" is a PCI device passthrough object on a host. # - A "PGPU" is a physical GPU on a host (for GPU passthrough / vGPU). # - A "SM" (Storage Manager) describes an available SR driver/plugin. # - A "proxy" is an XO Proxy instance for distributed backups and remote pool management. # - A "server" is a registered XCP-ng/XenServer connection in XO (you connect XO to your hosts via /servers). # - A "message" is a XAPI alert/notification (e.g. HA alerts, license warnings). # - An "alarm" is a XAPI performance/health alarm (CPU, memory, disk thresholds) -- distinct from messages. # - A "schedule" runs a backup-job on a cron expression; manual run via /schedules/{id}/actions/run. # - A "backup-repository" is a target where backups are written (NFS, S3, SMB, local). # - "events" provides a Server-Sent Events (SSE) stream for real-time XAPI object changes. # - RBAC v2: "groups" have users, "acl-roles" group "acl-privileges" and are attached to users/groups. # - Collections return arrays of URLs by default. Use ?fields=name_label,uuid for objects. # - Use ?filter= with XO filter syntax (e.g. "power_state:Running"). # - Use ?ndjson for newline-delimited JSON; add &watch for real-time streaming on /tasks. # - Use ?markdown=true (XO 6.4+) for markdown-formatted collection responses (LLM-friendly). # - Actions are async by default -- add ?sync to get the result directly (blocks; risk of timeout for long ops). # - VM property writes: PATCH /vms/{id} (update_vm) partially updates name, CPU, memory, boot firmware, tags, etc. # (added XO 6.5). # - VDI property writes: PATCH /vdis/{id} (update_vdi) was ADDED in XO 6.6 -- rename (name_label/name_description) # and grow the disk (size). Before 6.6 VDIs could only be renamed via tags or the JSON-RPC API. Returns 404 on XO < 6.6. # - VIF property writes: PATCH /vifs/{id} (update_vif) was ADDED in XO 6.6 -- partially updates locking mode, # allowed IPv4/IPv6 addresses, rate limit, and TX checksumming offload. Returns 404 on XO < 6.6. # - Snapshot revert: POST /vms/{id}/actions/revert_snapshot (revert_vm_to_snapshot); set snapshotBefore to make it undoable. # - SDN traffic rules: /plugins/sdn-controller/{networks|vifs}/{id}/actions/{add|delete|update}_traffic_rule -- only present # when the xo-server-sdn-controller plugin is loaded; calls fail with 404 otherwise. The update_* variants were added in XO 6.6. # - Backup repositories ("remotes") became WRITABLE in XO 6.6: POST /backup-repositories (create), # PATCH /backup-repositories/{id} (update), DELETE /backup-repositories/{id} (remove). Pre-6.6 they were read-only. # - XO 6.6 added two built-in RBAC v2 roles: "Storage Administrator" (full control of sr/vdi/vbd/pbd/backup-repository) # and "Network Administrator" (full control of networks/bonds/PIFs/VIFs). Discover via list_acl_roles. # - Auth (XO 6.6): an unauthenticated request now returns HTTP 401 with a WWW-Authenticate header so browsers # surface a native Basic Auth prompt -- ToolMesh injects the token cookie, so this only matters when debugging 401s. # - Tags: add with PUT //{id}/tags/{tag}, remove with DELETE. # - Sub-resources: alarms, messages, tasks are exposed per-object as //{id}/{alarms|messages|tasks}. # - Stats: /vms/{id}/stats and /hosts/{id}/stats accept ?granularity=seconds|minutes|hours|days. # - VM export: .xva (native), .ova. Compression: gzip (slow), zstd (fast, XCP-ng only). # - VDI export: .vhd (default), .raw. Import to existing VDI requires exact size match. # - UUID "_" means "any/default" for collection-level actions (e.g. /pools/_/actions does not exist anymore -- # action discovery is now via Swagger spec at /rest/v0/docs). # - Backup jobs: use /backup-jobs (modern). Old /backup/jobs/vm subroutes are DEPRECATED (removal 2026-09-12). # - Auth tokens: prefer POST /users/{id}/authentication_tokens (POST /users/authentication_tokens is DEPRECATED). # - User id "me" is a shortcut for the current authenticated user. spec: "https://dadl.ai/spec/dadl-spec-v0.1.md" credits: - "Dunkel Cloud GmbH -- maintainer" source_name: "Xen Orchestra REST API" source_url: "https://docs.xen-orchestra.com/restapi" date: "2026-06-30" backend: name: xen-orchestra type: rest version: "3.2" # base_url is intentionally omitted -- must be provided via backends.yaml url field, # because each deployment has its own Xen Orchestra instance address. description: "Xen Orchestra REST API (XO 6.4+, current through 6.6) -- complete coverage: VMs (incl. PATCH update + snapshot revert), VM controllers, hosts, pools, storage (SR, VDI incl. PATCH update, VBD), networks (VIF incl. PATCH update + PIF/PBD), VM/VDI snapshots, VM templates, hardware (PCI/PGPU/SM), tasks, backups (jobs/logs/writable repositories/restore), schedules, messages, alarms, events (SSE), RBAC v2 (users/groups/acl-roles/acl-privileges), proxies, servers, dashboards, auth tokens, SDN traffic rules (add/delete/update), health check" auth: type: bearer credential: xen_orchestra_token inject_into: header header_name: Cookie prefix: "authenticationToken=" defaults: headers: Content-Type: application/json Accept: application/json pagination: strategy: offset request: limit_param: limit limit_default: 100 behavior: expose max_pages: 10 errors: format: json message_path: "$.message" code_path: "$.code" retry_on: [429, 502, 503, 504] retry_strategy: max_retries: 3 backoff: exponential initial_delay: 2s terminal: [400, 401, 403, 404] response: max_items: 500 coverage: endpoints: 252 total_endpoints: 252 percentage: 100 focus: "Complete REST API coverage for XO 6.4+ (current through 6.6): all object types (VMs, VM controllers, VM templates, VM snapshots, hosts, pools, SRs, VDIs, VDI snapshots, VBDs, networks, VIFs, PIFs, PBDs, PCIs, PGPUs, SMs), all lifecycle actions (CRUD, partial update of VMs, VIFs and VDIs, power, snapshot/clone/migrate/revert, export/import, hotplug), full sub-resource access (alarms, messages, tasks, tags, stats per object), RBAC v2 (users, groups, acl-roles, acl-privileges), backups (archives, jobs, logs, restore-logs, writable repositories, schedules), real-time events via SSE, host/pool maintenance ops (rolling reboot/update, management reconfigure, audit logs, missing patches), network traffic rules (SDN controller plugin: add/delete/update), XOA appliance (dashboard, ping, gui-routes, MCP status). Some routes with format path params are exposed as multiple typed tools (e.g. /vms/{id}.{format} -> export_vm_xva + export_vm_ova, /vdis/{id}.{format} -> export_vdi_vhd + export_vdi_raw + import_vdi_to_existing + import_vdi_to_existing_raw)." missing: "Host lifecycle power actions (start/shutdown/reboot/restart_toolstack) are NOT exposed over REST -- only disable/enable/management_reconfigure are; use pool-level rolling_reboot/rolling_update or the JSON-RPC API for host power. SDN-controller traffic-rule routes (/plugins/sdn-controller/*) are included but only function when the xo-server-sdn-controller plugin is installed. Deprecated routes (POST /users/authentication_tokens, GET /backup/jobs/vm/*, GET /restore/logs/*) intentionally excluded -- removal scheduled for 2026-09-12 / 2026-10-13" last_reviewed: "2026-06-30" setup: credential_steps: - "Deploy Xen Orchestra from sources or use XOA (Xen Orchestra Appliance)" - "Log in to the XO web UI as an admin user" - "Navigate to Settings -> Users to confirm admin access" - "Generate an auth token via CLI: xo-cli --createToken xo.example.com " - "Or via API: POST /rest/v0/users/me/authentication_tokens with Basic auth" - "Copy the returned token value -- use it as the authenticationToken cookie" - "Optional (XO 6.4+): create dedicated non-admin users via POST /users and assign acl-roles via PUT /acl-roles/{id}/users/{userId}" env_var: CREDENTIAL_XEN_ORCHESTRA_TOKEN backends_yaml: | - name: xen-orchestra transport: rest dadl: xen-orchestra.dadl url: "https://xo.example.com/rest/v0" required_scopes: - "Admin user, OR an RBAC v2 user with appropriate acl-privileges (XO 6.4+)" docs_url: "https://docs.xen-orchestra.com/restapi" notes: "The url in backends.yaml must point to your XO instance including /rest/v0. Generate a token via xo-cli --createToken or POST /rest/v0/users/me/authentication_tokens with Basic auth. Live Swagger UI at /rest/v0/docs lists all endpoints with required RBAC privileges. For RBAC v2 setup see https://docs.xen-orchestra.com/xo6/acl-v2." hints: list_vms: filter_syntax: "XO filter: power_state:Running, name_label:my-vm, tags:production" fields_param: "Use fields=name_label,power_state,uuid for compact results" ndjson: "Use ndjson=true for streaming; combine with watch=true for live updates" markdown: "XO 6.4+: markdown=true returns markdown-formatted output (LLM-friendly)" create_vm: required: "name_label + template UUID are minimum required params" memory: "Memory in bytes (e.g. 2147483648 = 2 GB)" cloud_config: "Cloud-init config as string for automatic guest setup" note: "CPU count cannot be set at creation -- create the VM, then adjust cpus/cpusStaticMax via update_vm (PATCH /vms/{id})" update_vm: note: "PATCH semantics -- only the fields you send are changed. Cold-change fields (cpusStaticMax, memoryStaticMax, secureBoot, virtualizationMode) require the VM to be halted; live fields (nameLabel, cpus within static max, tags) work while running." tags: "tags replaces the entire tag list. To add/remove a single tag without rewriting, use add_vm_tag / remove_vm_tag instead." delete_vm: note: "Destroys the VM and its VDIs -- irreversible, always synchronous (no sync/async toggle). On VMs with large disks the call may exceed the HTTP client timeout ('context deadline exceeded') even though XO completes the deletion server-side -- confirm by re-reading: get_vm then returns HTTP 404 'no such VM '." revert_vm_to_snapshot: note: "Set snapshotBefore=true to auto-snapshot the current state before reverting, so the operation is undoable. Returns a task reference; use ?sync for the direct result." snapshot_vm: note: "Returns a task reference by default; use ?sync for direct result with snapshot id" clone_vm: modes: "Fast clone (same SR, fast=true), full copy (same SR, fast=false), or cross-SR/cross-pool copy (provide srId, optionally compress=gzip|zstd)" export_vm_xva: compression: "zstd is fastest (XCP-ng only), gzip is universal but slow" import_vm_xva: sr_param: "If sr is omitted, the pool's default SR is used" import_vdi_to_sr: size_match: "When importing to existing VDI (PUT), size must match exactly" formats: "VHD is default; set raw query param for raw disk images" list_tasks: watch_mode: "Use ndjson=true&watch=true for real-time task monitoring" wait: "On a single task (get_task): ?wait=true long-polls until the task completes, then returns its final state" delete_tasks: note: "Bulk delete: removes ALL tasks the current user has 'task:delete' privilege on. Irreversible." create_vbd: note: "Connects a VDI to a VM. Use connect_vbd/disconnect_vbd for hotplug." migrate_vdi: note: "Moves a VDI between SRs without VM downtime (live storage migration). Large disks can take minutes -- avoid sync=true to prevent timeouts; use async (default) and poll the returned taskId via get_task instead." migrate_vm: note: "Live-migrates a VM to another host, optionally with storage migration. For large VMs avoid sync=true to prevent timeouts; use async (default) and poll the returned taskId via get_task instead." cross_pool: "For cross-pool moves, use srIdByVdiId to place each disk on a specific target SR and networkIdByVifId to re-map each VIF to a target network; srId sets a single default SR for all disks." emergency_shutdown_pool: warning: "Shuts down ALL VMs and hosts in the pool. Use only in emergencies." rolling_reboot_pool: note: "Reboots each host sequentially, migrating VMs automatically. No downtime." rolling_update_pool: note: "Applies pending updates to each host sequentially with live migration." create_pool_network: modes: "Three flavors: create_network (basic with PIF), create_bonded_network (link aggregation across pifIds with bondMode), create_internal_network (host-internal, no physical PIF)" management_reconfigure_host: warning: "Moves the management interface to a different PIF. May briefly disconnect XO from the host." get_host_stats: granularity: "seconds (last 10min) | minutes (last 2h) | hours (last week) | days (last year)" get_vm_stats: granularity: "seconds (last 10min) | minutes (last 2h) | hours (last week) | days (last year)" data_sources: "Use add/delete data_source endpoints to enable/disable specific RRD metrics (e.g. cpu0, vif_0_rx)" open_events_stream: note: "Server-Sent Events (SSE) connection. The endpoint returns a subscription ID; then use POST /events/{id}/subscriptions to add object-type listeners (VM, host, pool, etc.). Events emitted: ping, add, update, remove." run_schedule: note: "Manually triggers a backup-job execution outside its cron schedule. Returns a task reference." get_dashboard: note: "Aggregated overview: pool/host/VM counts, storage usage, recent backup-jobs (7d), disconnected servers, disabled hosts. Pre-6.4 path was /dashboards (now removed); use /dashboard (singular)." create_user: permission_field: "Permission strings: 'admin', 'user', 'none'. Omit for default user-level access." create_authentication_token: expires_in: "Accepts either a number (milliseconds) or a duration string (e.g. '1 hour', '7 days', '30d')" note: "Use user_id='me' for self. POST /users/authentication_tokens (without user id in path) is DEPRECATED -- removal 2026-10-13." create_acl_role: flow: "1) Create role via POST /acl-roles. 2) Add privileges via POST /acl-privileges (with roleId). 3) Attach role to user/group via PUT /acl-roles/{id}/{users|groups}/{userId|groupId}." add_server: note: "Registers a new XCP-ng/XenServer pool master to XO. body.host is the IP/hostname, body.username/password are XAPI credentials. Use connect_server afterwards to establish the connection." delete_sr: warning: "Destroys the SR AND all data on the underlying storage -- irreversible. The SR must be detached/unplugged first. To keep the data and only remove it from XO, use forget_sr instead." add_network_traffic_rule: note: "Requires the xo-server-sdn-controller plugin; returns 404 if not loaded. Applies to private (tunnel) networks managed by the SDN controller. allow=false drops matching traffic." delete_network_traffic_rule: note: "Identify the rule to remove by matching its direction + ipRange + protocol + port. Requires the SDN controller plugin." add_vif_traffic_rule: note: "Per-VIF variant of add_network_traffic_rule -- scopes the rule to a single VM interface. Requires the SDN controller plugin." get_mcp_status: note: "Unauthenticated. Reports whether XO's built-in Model Context Protocol server is enabled (HTTP 200) or disabled (HTTP 503)." update_vdi: added: "XO 6.6 -- PATCH /vdis/{id} returns 404 on older builds. Before 6.6, VDIs could not be renamed via REST." note: "PATCH semantics. size can only grow (>= current size); shrinking is rejected by XAPI. Use add_vdi_tag/remove_vdi_tag for tags." update_vif: added: "XO 6.6 -- PATCH /vifs/{id} returns 404 on older builds." note: "PATCH semantics -- only the fields you send change. lockingMode='locked' enforces the ipv4Allowed/ipv6Allowed whitelists; 'disabled' blocks all traffic; 'unlocked' allows everything; 'network_default' inherits the network setting." rate_limit: "rateLimit is the egress cap in kbps (null/0 = unlimited)." tx_checksumming: "txChecksumming toggles the ethtool tx-checksumming offload (maps to other_config ethtool-tx); disable it only to work around buggy guest drivers." create_vif: tx_checksumming: "txChecksumming (added XO 6.5, PR #9793) maps to the ethtool tx-checksumming / other_config value; omit for the host default." create_backup_repository: added: "XO 6.6 -- backup repositories ('remotes') became writable over REST." note: "url encodes the backend: 'nfs://host:/path', 'smb://user:pass@host\\\\share', 's3://key:secret@endpoint/bucket', or 'file:///srv/backups' for a local mount. Returns {id}." update_backup_repository: note: "PATCH semantics. Set enabled=false to stop new backups writing here without deleting the target. options is a free-form per-backend settings map." delete_backup_repository: note: "Removes the repository registration from XO. Does NOT delete the backup data on the underlying storage. Fails if backup-jobs still reference it." update_network_traffic_rule: note: "Requires the xo-server-sdn-controller plugin (404 otherwise). Identify the existing rule by direction+ipRange+protocol+port, then send the new allow value. Added XO 6.6." update_vif_traffic_rule: note: "Per-VIF variant of update_network_traffic_rule. Requires the SDN controller plugin. Added XO 6.6." tools: # ========================================================================= # XOA -- Health Check, Dashboard, GUI Routes # ========================================================================= ping: method: GET path: /ping access: read description: "Health check. Unauthenticated. Returns {result: 'pong', timestamp}. Use this to verify the XO instance is reachable before trying authenticated calls." pagination: none get_dashboard: method: GET path: /dashboard access: read description: "Aggregated dashboard for the XOA appliance: pool/host/VM counts, storage usage, recent backup-jobs (last 7 days), disconnected servers, disabled hosts. Use ndjson=true for streaming output. Replaces the legacy plural /dashboards endpoint." params: ndjson: { type: boolean, in: query, description: "Stream as newline-delimited JSON" } pagination: none get_gui_routes: method: GET path: /gui-routes access: read description: "Returns the URLs of the legacy XO5 (xo5) and modern XO6 (xo6) web UIs. Useful for clients that need to deep-link into the GUI." pagination: none get_mcp_status: method: GET path: /mcp/status access: read description: "Report whether XO's built-in MCP server is enabled. Unauthenticated. Returns 200 when enabled, 503 when disabled. Requires a recent XO build (REST route added after 6.5.0); returns 404 on older instances." pagination: none # ========================================================================= # USERS -- CRUD + Auth Tokens + Sub-resources (RBAC v2 in XO 6.4+) # ========================================================================= list_users: method: GET path: /users access: admin description: "List all users. Returns URLs by default; use fields for objects. Requires user:read privilege." params: fields: { type: string, in: query, description: "Comma-separated fields (e.g. id,name,email,permission)" } filter: { type: string, in: query, description: "XO filter (e.g. permission:admin)" } limit: { type: integer, in: query } ndjson: { type: boolean, in: query } markdown: { type: boolean, in: query, description: "Markdown-formatted output (XO 6.4+)" } get_user: method: GET path: /users/{id} access: admin description: "Get details of a single user. Use id='me' for the current authenticated user. Self-fetch is always allowed; reading another user requires user:read privilege." params: id: { type: string, in: path, required: true, description: "User UUID or 'me'" } pagination: none create_user: method: POST path: /users access: admin description: "Create a new user. Returns {id} of the created user. Requires user:create privilege." params: name: { type: string, in: body, required: true, description: "Username/email for the user" } password: { type: string, in: body, required: true } permission: { type: string, in: body, description: "'admin', 'user', or 'none' (default: 'none')" } pagination: none update_user: method: PATCH path: /users/{id} access: admin description: "Update user properties. Cannot change own permission. Synchronized users (LDAP/SAML/OIDC) cannot have name/password changed." params: id: { type: string, in: path, required: true, description: "User UUID or 'me'" } name: { type: string, in: body } password: { type: string, in: body } permission: { type: string, in: body, description: "'admin', 'user', or 'none'" } preferences: { type: object, in: body, description: "Per-user UI/CLI preferences object" } pagination: none delete_user: method: DELETE path: /users/{id} access: dangerous description: "Delete a user. Irreversible. Requires user:delete privilege." params: id: { type: string, in: path, required: true, description: "User UUID" } pagination: none list_user_groups: method: GET path: /users/{id}/groups access: admin description: "List groups the user is a member of (filtered by group:read privilege). Use id='me' for self." params: id: { type: string, in: path, required: true, description: "User UUID or 'me'" } fields: { type: string, in: query } filter: { type: string, in: query } limit: { type: integer, in: query } ndjson: { type: boolean, in: query } markdown: { type: boolean, in: query } list_user_tasks: method: GET path: /users/{id}/tasks access: admin description: "List tasks owned by the user (filtered by task:read privilege). Use id='me' for self." params: id: { type: string, in: path, required: true, description: "User UUID or 'me'" } fields: { type: string, in: query } filter: { type: string, in: query } limit: { type: integer, in: query } ndjson: { type: boolean, in: query } markdown: { type: boolean, in: query } list_user_acl_privileges: method: GET path: /users/{id}/acl-privileges access: admin description: "List effective RBAC v2 acl-privileges for the user (XO 6.4+). Useful for inspecting what a user can actually do. Self-call (/users/me/acl-privileges) is always permitted." params: id: { type: string, in: path, required: true, description: "User UUID or 'me'" } fields: { type: string, in: query, description: "e.g. id,action,resource" } filter: { type: string, in: query } limit: { type: integer, in: query } ndjson: { type: boolean, in: query } list_auth_tokens: method: GET path: /users/{id}/authentication_tokens access: admin description: "List all authentication tokens for a user. Use id='me' for self." params: id: { type: string, in: path, required: true, description: "User UUID or 'me'" } pagination: none create_auth_token: method: POST path: /users/{id}/authentication_tokens access: admin description: "Create a new authentication token. You can only create tokens for yourself (use id='me'). Returns {token} including the secret (store it securely; cannot be retrieved later)." params: id: { type: string, in: path, required: true, description: "Must be your own user UUID or 'me'" } client: { type: object, in: body, description: "Optional {id: string} to identify the client" } description: { type: string, in: body, description: "Human-readable purpose, e.g. 'CI pipeline'" } expiresIn: { type: string, in: body, description: "Duration string ('1 hour', '7 days') or milliseconds" } pagination: none # ========================================================================= # GROUPS -- RBAC v2 (XO 6.4+) # ========================================================================= list_groups: method: GET path: /groups access: admin description: "List all groups (filtered by group:read privilege)." params: fields: { type: string, in: query } filter: { type: string, in: query } limit: { type: integer, in: query } ndjson: { type: boolean, in: query } markdown: { type: boolean, in: query } get_group: method: GET path: /groups/{id} access: admin description: "Get details of a single group." params: id: { type: string, in: path, required: true } pagination: none create_group: method: POST path: /groups access: admin description: "Create a new group. Returns {id} of the created group." params: name: { type: string, in: body, required: true } pagination: none update_group: method: PATCH path: /groups/{id} access: admin description: "Update group properties (currently only name)." params: id: { type: string, in: path, required: true } name: { type: string, in: body } pagination: none delete_group: method: DELETE path: /groups/{id} access: dangerous description: "Delete a group. Irreversible. Removes all role attachments to this group." params: id: { type: string, in: path, required: true } pagination: none add_user_to_group: method: PUT path: /groups/{id}/users/{userId} access: admin description: "Add a user to a group." params: id: { type: string, in: path, required: true, description: "Group UUID" } userId: { type: string, in: path, required: true, description: "User UUID" } pagination: none remove_user_from_group: method: DELETE path: /groups/{id}/users/{userId} access: write description: "Remove a user from a group." params: id: { type: string, in: path, required: true, description: "Group UUID" } userId: { type: string, in: path, required: true, description: "User UUID" } pagination: none list_group_users: method: GET path: /groups/{id}/users access: admin description: "List users that are members of the group." params: id: { type: string, in: path, required: true } fields: { type: string, in: query } filter: { type: string, in: query } limit: { type: integer, in: query } ndjson: { type: boolean, in: query } markdown: { type: boolean, in: query } list_group_tasks: method: GET path: /groups/{id}/tasks access: admin description: "List tasks associated with members of the group." params: id: { type: string, in: path, required: true } fields: { type: string, in: query } filter: { type: string, in: query } limit: { type: integer, in: query } ndjson: { type: boolean, in: query } markdown: { type: boolean, in: query } # ========================================================================= # ACL ROLES -- RBAC v2 (XO 6.4+) # ========================================================================= list_acl_roles: method: GET path: /acl-roles access: admin description: "List all RBAC v2 roles. A role groups privileges and can be attached to users/groups." params: fields: { type: string, in: query } filter: { type: string, in: query } limit: { type: integer, in: query } ndjson: { type: boolean, in: query } get_acl_role: method: GET path: /acl-roles/{id} access: admin description: "Get details of a single role." params: id: { type: string, in: path, required: true } pagination: none create_acl_role: method: POST path: /acl-roles access: admin description: "Create a new RBAC role. Returns {id}. Add privileges via POST /acl-privileges (with roleId), then attach to users/groups." params: name: { type: string, in: body, required: true } description: { type: string, in: body } pagination: none update_acl_role: method: PATCH path: /acl-roles/{id} access: admin description: "Update a role's name and/or description (set description to null to clear)." params: id: { type: string, in: path, required: true } name: { type: string, in: body } description: { type: string, in: body, description: "Set to null to remove description" } pagination: none delete_acl_role: method: DELETE path: /acl-roles/{id} access: dangerous description: "Delete a role. Irreversible. All attachments to users/groups are removed; privileges referencing this role are also dropped." params: id: { type: string, in: path, required: true } pagination: none copy_acl_role: method: POST path: /acl-roles/{id}/actions/copy access: admin description: "Duplicate a role (and its privileges) under a new name. Useful for deriving custom roles from built-in ones." params: id: { type: string, in: path, required: true, description: "Source role UUID" } name: { type: string, in: body, description: "Name for the new copy" } description: { type: string, in: body } sync: { type: boolean, in: query } pagination: none list_acl_role_privileges: method: GET path: /acl-roles/{id}/privileges access: admin description: "List all acl-privileges attached to this role." params: id: { type: string, in: path, required: true } fields: { type: string, in: query } filter: { type: string, in: query } limit: { type: integer, in: query } ndjson: { type: boolean, in: query } attach_acl_role_to_group: method: PUT path: /acl-roles/{id}/groups/{groupId} access: admin description: "Attach an RBAC role to a group. All members of the group inherit the role's privileges." params: id: { type: string, in: path, required: true, description: "Role UUID" } groupId: { type: string, in: path, required: true, description: "Group UUID" } pagination: none detach_acl_role_from_group: method: DELETE path: /acl-roles/{id}/groups/{groupId} access: write description: "Detach an RBAC role from a group." params: id: { type: string, in: path, required: true, description: "Role UUID" } groupId: { type: string, in: path, required: true, description: "Group UUID" } pagination: none attach_acl_role_to_user: method: PUT path: /acl-roles/{id}/users/{userId} access: admin description: "Attach an RBAC role directly to a user (in addition to roles inherited via groups)." params: id: { type: string, in: path, required: true, description: "Role UUID" } userId: { type: string, in: path, required: true, description: "User UUID" } pagination: none detach_acl_role_from_user: method: DELETE path: /acl-roles/{id}/users/{userId} access: write description: "Detach an RBAC role from a user." params: id: { type: string, in: path, required: true, description: "Role UUID" } userId: { type: string, in: path, required: true, description: "User UUID" } pagination: none # ========================================================================= # ACL PRIVILEGES -- RBAC v2 (XO 6.4+) # ========================================================================= list_acl_privileges: method: GET path: /acl-privileges access: admin description: "List all RBAC v2 privileges (each privilege = role + resource + action, optionally scoped by object)." params: fields: { type: string, in: query, description: "e.g. id,action,resource,roleId" } filter: { type: string, in: query, description: "e.g. action:create" } limit: { type: integer, in: query } ndjson: { type: boolean, in: query } get_acl_privilege: method: GET path: /acl-privileges/{id} access: admin description: "Get details of a single privilege." params: id: { type: string, in: path, required: true } pagination: none create_acl_privilege: method: POST path: /acl-privileges access: admin description: "Grant a new privilege (action on a resource type, optionally scoped to a specific object) to a role. Returns {id}. Body fields depend on the resource type -- see Swagger /rest/v0/docs for the privilege schema." params: roleId: { type: string, in: body, required: true, description: "Target role UUID" } resource: { type: string, in: body, required: true, description: "e.g. 'vm', 'host', 'pool', 'user', 'backup-job'" } action: { type: string, in: body, required: true, description: "e.g. 'read', 'create', 'delete', 'update', 'snapshot'" } objectId: { type: string, in: body, description: "Optional: scope to a specific object UUID" } pagination: none update_acl_privilege: method: PATCH path: /acl-privileges/{id} access: admin description: "Update a privilege's resource/action/objectId (roleId is immutable)." params: id: { type: string, in: path, required: true } resource: { type: string, in: body } action: { type: string, in: body } objectId: { type: string, in: body } pagination: none delete_acl_privilege: method: DELETE path: /acl-privileges/{id} access: dangerous description: "Revoke a privilege. Irreversible." params: id: { type: string, in: path, required: true } pagination: none # ========================================================================= # VMs -- Core CRUD # ========================================================================= list_vms: method: GET path: /vms access: read description: "List all VMs. Returns URLs by default; use fields for objects." params: fields: { type: string, in: query, description: "Comma-separated fields" } filter: { type: string, in: query, description: "XO filter (e.g. power_state:Running)" } limit: { type: integer, in: query } ndjson: { type: boolean, in: query } markdown: { type: boolean, in: query } get_vm: method: GET path: /vms/{uuid} access: read description: "Get full details of a VM" params: uuid: { type: string, in: path, required: true } pagination: none update_vm: method: PATCH path: /vms/{uuid} access: write description: "Partially update a VM's properties (rename, resize CPU/RAM, boot firmware, tags, etc.). Only the fields you pass are changed. Some fields require the VM to be halted. Requires XO 6.5+ (PATCH /vms route returns 404 on older builds)." params: uuid: { type: string, in: path, required: true } nameLabel: { type: string, in: body, description: "New display name" } nameDescription: { type: string, in: body, description: "New description" } notes: { type: string, in: body, description: "Free-form notes (null to clear)" } tags: { type: array, in: body, description: "Replace the full tag list (array of strings)" } cpus: { type: integer, in: body, description: "Number of vCPUs currently assigned" } cpusStaticMax: { type: integer, in: body, description: "Max vCPUs (cold change; requires halted VM)" } coresPerSocket: { type: integer, in: body, description: "vCPU cores per socket (null for auto)" } cpuCap: { type: integer, in: body, description: "CPU cap (null for none)" } cpuWeight: { type: integer, in: body, description: "CPU scheduling weight (null for default)" } cpuMask: { type: array, in: body, description: "Pin vCPUs to host physical cores (array of integers)" } memory: { type: integer, in: body, description: "Dynamic memory target in bytes" } memoryMin: { type: integer, in: body, description: "Dynamic memory minimum in bytes" } memoryMax: { type: integer, in: body, description: "Dynamic memory maximum in bytes" } memoryStaticMax: { type: integer, in: body, description: "Static memory maximum in bytes (cold change)" } affinityHost: { type: string, in: body, description: "Preferred host UUID (null to clear)" } suspendSr: { type: string, in: body, description: "SR UUID used to store suspend memory image (null for default)" } startDelay: { type: integer, in: body, description: "Delay in seconds before auto-start" } autoPoweron: { type: boolean, in: body, description: "Start this VM automatically when the pool boots" } highAvailability: { type: string, in: body, description: "HA restart priority: 'best-effort', 'restart', or '' (disabled)" } blockedOperations: { type: object, in: body, description: "Map of VM operations to block (e.g. {\"destroy\": true})" } hvmBootFirmware: { type: string, in: body, description: "HVM boot firmware: 'bios' or 'uefi'" } secureBoot: { type: boolean, in: body, description: "Enable UEFI Secure Boot" } uefiMode: { type: string, in: body, description: "UEFI mode: 'setup' or 'user'" } virtualizationMode: { type: string, in: body, description: "'pv' or 'hvm'" } nestedVirt: { type: boolean, in: body, description: "Expose virtualization extensions to the guest" } expNestedHvm: { type: boolean, in: body, description: "Experimental nested HVM support" } viridian: { type: boolean, in: body, description: "Enable Viridian (Hyper-V) enlightenments for Windows guests" } hasVendorDevice: { type: boolean, in: body, description: "Expose the Xen PV vendor device (Windows Update drivers)" } nicType: { type: string, in: body, description: "Emulated NIC type (null for default)" } vga: { type: string, in: body, description: "Emulated graphics: 'std' or 'cirrus'" } videoram: { type: integer, in: body, description: "Video RAM in MiB: 1, 2, 4, 8 or 16" } PV_args: { type: string, in: body, description: "PV kernel command-line arguments" } xenStoreData: { type: object, in: body, description: "Key/value map written to the guest's xenstore" } creation: { type: object, in: body, description: "Creation metadata, e.g. {\"user\": \"\"}" } resourceSet: { type: string, in: body, description: "Assign the VM to a resource set (null to remove)" } share: { type: boolean, in: body, description: "Share the VM with all members of its resource set" } pagination: none delete_vm: method: DELETE path: /vms/{uuid} access: dangerous description: "Destroy a VM and its VDIs. Irreversible." params: uuid: { type: string, in: path, required: true } pagination: none # ========================================================================= # VM ACTIONS -- Power Management # ========================================================================= start_vm: method: POST path: /vms/{uuid}/actions/start access: write description: "Start a halted VM. Optionally pin to a specific host via body.hostId." params: uuid: { type: string, in: path, required: true } hostId: { type: string, in: body, description: "Optional: target host UUID" } sync: { type: boolean, in: query } pagination: none clean_shutdown_vm: method: POST path: /vms/{uuid}/actions/clean_shutdown access: write description: "Gracefully shut down a VM (ACPI shutdown)" params: uuid: { type: string, in: path, required: true } sync: { type: boolean, in: query } pagination: none hard_shutdown_vm: method: POST path: /vms/{uuid}/actions/hard_shutdown access: write description: "Force shutdown a VM immediately (equivalent to pulling the plug)" params: uuid: { type: string, in: path, required: true } sync: { type: boolean, in: query } pagination: none clean_reboot_vm: method: POST path: /vms/{uuid}/actions/clean_reboot access: write description: "Gracefully reboot a VM (ACPI reboot)" params: uuid: { type: string, in: path, required: true } sync: { type: boolean, in: query } pagination: none hard_reboot_vm: method: POST path: /vms/{uuid}/actions/hard_reboot access: write description: "Force reboot a VM immediately" params: uuid: { type: string, in: path, required: true } sync: { type: boolean, in: query } pagination: none pause_vm: method: POST path: /vms/{uuid}/actions/pause access: write description: "Pause a running VM (freezes CPU, keeps memory)" params: uuid: { type: string, in: path, required: true } sync: { type: boolean, in: query } pagination: none unpause_vm: method: POST path: /vms/{uuid}/actions/unpause access: write description: "Unpause a paused VM" params: uuid: { type: string, in: path, required: true } sync: { type: boolean, in: query } pagination: none suspend_vm: method: POST path: /vms/{uuid}/actions/suspend access: write description: "Suspend a VM to disk (saves memory state, frees RAM)" params: uuid: { type: string, in: path, required: true } sync: { type: boolean, in: query } pagination: none resume_vm: method: POST path: /vms/{uuid}/actions/resume access: write description: "Resume a suspended VM from disk" params: uuid: { type: string, in: path, required: true } sync: { type: boolean, in: query } pagination: none # ========================================================================= # VM ACTIONS -- Snapshot, Clone, Migration # ========================================================================= snapshot_vm: method: POST path: /vms/{uuid}/actions/snapshot access: write description: "Create a snapshot of a VM. Returns task reference; use ?sync to get the snapshot id directly." params: uuid: { type: string, in: path, required: true } name_label: { type: string, in: body, description: "Optional name for the snapshot (auto-generated if omitted)" } sync: { type: boolean, in: query } pagination: none revert_vm_to_snapshot: method: POST path: /vms/{uuid}/actions/revert_snapshot access: write description: "Revert a VM to one of its snapshots. Optionally take a fresh snapshot of the current state first (snapshotBefore) so the revert is undoable. Requires XO 6.5+ (route returns 404 on older builds)." params: uuid: { type: string, in: path, required: true } snapshotId: { type: string, in: body, required: true, description: "UUID of the snapshot to revert to" } snapshotBefore: { type: boolean, in: body, description: "Snapshot the current state before reverting" } sync: { type: boolean, in: query } pagination: none clone_vm: method: POST path: /vms/{uuid}/actions/clone access: write description: "Clone a VM. Three modes: fast clone on same SR (fast=true, omit srId), full copy on same SR (fast=false), or copy to another SR/pool (provide srId, optional compress=gzip|zstd for cross-pool)." params: uuid: { type: string, in: path, required: true } name_label: { type: string, in: body, description: "Name for the clone" } fast: { type: boolean, in: body, description: "True for fast clone (CoW); ignored if srId is set" } srId: { type: string, in: body, description: "Target SR UUID (full copy; can be on another pool)" } compress: { type: string, in: body, description: "'gzip' or 'zstd' (only for cross-pool copy)" } sync: { type: boolean, in: query } pagination: none migrate_vm: method: POST path: /vms/{uuid}/actions/migrate access: write description: "Live-migrate a VM to another host. Supports cross-SR storage migration and per-disk / per-VIF placement for cross-pool moves." params: uuid: { type: string, in: path, required: true } hostId: { type: string, in: body, required: true, description: "Target host UUID" } migrationNetworkId: { type: string, in: body, description: "Network UUID to use for the migration traffic" } srId: { type: string, in: body, description: "Default target SR for storage migration (all VDIs)" } srIdByVdiId: { type: object, in: body, description: "Per-VDI target SR map: {\"\": \"\"} (overrides srId per disk)" } networkIdByVifId: { type: object, in: body, description: "Per-VIF target network map: {\"\": \"\"} (cross-pool)" } sync: { type: boolean, in: query } pagination: none # ========================================================================= # VM EXPORT & IMPORT # ========================================================================= export_vm_xva: method: GET path: /vms/{uuid}.xva access: read description: "Export a VM as XVA (XCP-ng native format). Streamed binary download." params: uuid: { type: string, in: path, required: true } compress: { type: string, in: query, description: "Compression method: 'gzip' (universal, slow), 'zstd' (XCP-ng only, fast), or omit/'undefined' for no compression (XO 6.6 made these explicit)" } response: binary: true content_type: application/octet-stream pagination: none export_vm_ova: method: GET path: /vms/{uuid}.ova access: read description: "Export a VM as OVA (Open Virtualization Format, portable across hypervisors)." params: uuid: { type: string, in: path, required: true } response: binary: true content_type: application/octet-stream pagination: none import_vm_xva: method: POST path: /pools/{pool_id}/vms access: write description: "Import a VM from XVA upload into a pool. Body is the raw XVA stream." params: pool_id: { type: string, in: path, required: true } sr: { type: string, in: query, description: "Target SR (pool default if omitted)" } content_type: application/octet-stream pagination: none # ========================================================================= # VM SUB-RESOURCES -- Stats, Tags, VDIs, Dashboard, Sub-collections # ========================================================================= get_vm_stats: method: GET path: /vms/{uuid}/stats access: read description: "Get RRD statistics for a VM (CPU, memory, network, disk). VM must be running." params: uuid: { type: string, in: path, required: true } granularity: { type: string, in: query, description: "'seconds' (10min) | 'minutes' (2h) | 'hours' (1w) | 'days' (1y)" } pagination: none add_vm_data_source: method: PUT path: /vms/{uuid}/stats/data_source/{data_source} access: write description: "Enable a specific RRD data source on a VM (e.g. cpu0, vif_0_rx). VM must be running." params: uuid: { type: string, in: path, required: true } data_source: { type: string, in: path, required: true, description: "Data source name" } pagination: none delete_vm_data_source: method: DELETE path: /vms/{uuid}/stats/data_source/{data_source} access: write description: "Disable / forget a specific RRD data source on a VM." params: uuid: { type: string, in: path, required: true } data_source: { type: string, in: path, required: true } pagination: none add_vm_tag: method: PUT path: /vms/{uuid}/tags/{tag} access: write description: "Add a tag to a VM" params: uuid: { type: string, in: path, required: true } tag: { type: string, in: path, required: true } pagination: none remove_vm_tag: method: DELETE path: /vms/{uuid}/tags/{tag} access: write description: "Remove a tag from a VM" params: uuid: { type: string, in: path, required: true } tag: { type: string, in: path, required: true } pagination: none list_vm_vdis: method: GET path: /vms/{uuid}/vdis access: read description: "List all VDIs attached to a VM" params: uuid: { type: string, in: path, required: true } fields: { type: string, in: query } pagination: none list_vm_alarms: method: GET path: /vms/{uuid}/alarms access: read description: "List XAPI alarms (performance/health thresholds) for the VM." params: uuid: { type: string, in: path, required: true } fields: { type: string, in: query } filter: { type: string, in: query } limit: { type: integer, in: query } ndjson: { type: boolean, in: query } markdown: { type: boolean, in: query } list_vm_messages: method: GET path: /vms/{uuid}/messages access: read description: "List XAPI messages/alerts emitted for the VM." params: uuid: { type: string, in: path, required: true } fields: { type: string, in: query } filter: { type: string, in: query } limit: { type: integer, in: query } ndjson: { type: boolean, in: query } markdown: { type: boolean, in: query } list_vm_tasks: method: GET path: /vms/{uuid}/tasks access: read description: "List tasks scoped to this VM (running/completed/failed)." params: uuid: { type: string, in: path, required: true } fields: { type: string, in: query } filter: { type: string, in: query } limit: { type: integer, in: query } ndjson: { type: boolean, in: query } markdown: { type: boolean, in: query } list_vm_backup_jobs: method: GET path: /vms/{uuid}/backup-jobs access: read description: "List backup-jobs that include this VM." params: uuid: { type: string, in: path, required: true } fields: { type: string, in: query } filter: { type: string, in: query } limit: { type: integer, in: query } ndjson: { type: boolean, in: query } markdown: { type: boolean, in: query } get_vm_dashboard: method: GET path: /vms/{uuid}/dashboard access: read description: "Get dashboard/statistics for a VM (aggregate metrics)." params: uuid: { type: string, in: path, required: true } pagination: none # ========================================================================= # VM SNAPSHOTS # ========================================================================= list_vm_snapshots: method: GET path: /vm-snapshots access: read description: "List all VM snapshots" params: fields: { type: string, in: query } filter: { type: string, in: query } limit: { type: integer, in: query } ndjson: { type: boolean, in: query } markdown: { type: boolean, in: query } get_vm_snapshot: method: GET path: /vm-snapshots/{uuid} access: read description: "Get details of a VM snapshot" params: uuid: { type: string, in: path, required: true } pagination: none delete_vm_snapshot: method: DELETE path: /vm-snapshots/{uuid} access: dangerous description: "Delete a VM snapshot. Irreversible." params: uuid: { type: string, in: path, required: true } pagination: none export_vm_snapshot: method: GET path: /vm-snapshots/{uuid}.{format} access: read description: "Export a VM snapshot as XVA or OVA. Format is the file extension ('xva' or 'ova')." params: uuid: { type: string, in: path, required: true } format: { type: string, in: path, required: true, description: "'xva' or 'ova'" } response: binary: true content_type: application/octet-stream pagination: none list_vm_snapshot_vdis: method: GET path: /vm-snapshots/{uuid}/vdis access: read description: "List VDIs belonging to a VM snapshot." params: uuid: { type: string, in: path, required: true } fields: { type: string, in: query } pagination: none list_vm_snapshot_alarms: method: GET path: /vm-snapshots/{uuid}/alarms access: read description: "List XAPI alarms scoped to a VM snapshot." params: uuid: { type: string, in: path, required: true } fields: { type: string, in: query } filter: { type: string, in: query } limit: { type: integer, in: query } ndjson: { type: boolean, in: query } markdown: { type: boolean, in: query } list_vm_snapshot_messages: method: GET path: /vm-snapshots/{uuid}/messages access: read description: "List XAPI messages scoped to a VM snapshot." params: uuid: { type: string, in: path, required: true } fields: { type: string, in: query } filter: { type: string, in: query } limit: { type: integer, in: query } ndjson: { type: boolean, in: query } markdown: { type: boolean, in: query } list_vm_snapshot_tasks: method: GET path: /vm-snapshots/{uuid}/tasks access: read description: "List tasks scoped to a VM snapshot." params: uuid: { type: string, in: path, required: true } fields: { type: string, in: query } filter: { type: string, in: query } limit: { type: integer, in: query } ndjson: { type: boolean, in: query } markdown: { type: boolean, in: query } add_vm_snapshot_tag: method: PUT path: /vm-snapshots/{uuid}/tags/{tag} access: write description: "Add a tag to a VM snapshot." params: uuid: { type: string, in: path, required: true } tag: { type: string, in: path, required: true } pagination: none remove_vm_snapshot_tag: method: DELETE path: /vm-snapshots/{uuid}/tags/{tag} access: write description: "Remove a tag from a VM snapshot." params: uuid: { type: string, in: path, required: true } tag: { type: string, in: path, required: true } pagination: none # ========================================================================= # VM TEMPLATES -- New collection in XO 6.4+ # ========================================================================= list_vm_templates: method: GET path: /vm-templates access: read description: "List all VM templates. Templates are stamps from which create_vm produces new VMs." params: fields: { type: string, in: query } filter: { type: string, in: query } limit: { type: integer, in: query } ndjson: { type: boolean, in: query } markdown: { type: boolean, in: query } get_vm_template: method: GET path: /vm-templates/{uuid} access: read description: "Get details of a VM template." params: uuid: { type: string, in: path, required: true } pagination: none delete_vm_template: method: DELETE path: /vm-templates/{uuid} access: dangerous description: "Delete a VM template. Irreversible. Built-in templates may be protected." params: uuid: { type: string, in: path, required: true } pagination: none export_vm_template: method: GET path: /vm-templates/{uuid}.{format} access: read description: "Export a VM template as XVA or OVA." params: uuid: { type: string, in: path, required: true } format: { type: string, in: path, required: true, description: "'xva' or 'ova'" } response: binary: true content_type: application/octet-stream pagination: none list_vm_template_vdis: method: GET path: /vm-templates/{uuid}/vdis access: read description: "List VDIs belonging to a VM template." params: uuid: { type: string, in: path, required: true } fields: { type: string, in: query } pagination: none list_vm_template_alarms: method: GET path: /vm-templates/{uuid}/alarms access: read description: "List XAPI alarms scoped to a VM template." params: uuid: { type: string, in: path, required: true } fields: { type: string, in: query } filter: { type: string, in: query } limit: { type: integer, in: query } ndjson: { type: boolean, in: query } markdown: { type: boolean, in: query } list_vm_template_messages: method: GET path: /vm-templates/{uuid}/messages access: read description: "List XAPI messages scoped to a VM template." params: uuid: { type: string, in: path, required: true } fields: { type: string, in: query } filter: { type: string, in: query } limit: { type: integer, in: query } ndjson: { type: boolean, in: query } markdown: { type: boolean, in: query } list_vm_template_tasks: method: GET path: /vm-templates/{uuid}/tasks access: read description: "List tasks scoped to a VM template." params: uuid: { type: string, in: path, required: true } fields: { type: string, in: query } filter: { type: string, in: query } limit: { type: integer, in: query } ndjson: { type: boolean, in: query } markdown: { type: boolean, in: query } add_vm_template_tag: method: PUT path: /vm-templates/{uuid}/tags/{tag} access: write description: "Add a tag to a VM template." params: uuid: { type: string, in: path, required: true } tag: { type: string, in: path, required: true } pagination: none remove_vm_template_tag: method: DELETE path: /vm-templates/{uuid}/tags/{tag} access: write description: "Remove a tag from a VM template." params: uuid: { type: string, in: path, required: true } tag: { type: string, in: path, required: true } pagination: none # ========================================================================= # VM CONTROLLERS -- dom0 control domains (new collection in XO 6.5+) # ========================================================================= list_vm_controllers: method: GET path: /vm-controllers access: read description: "List VM controllers (the dom0 control-domain VM of each host). Returns URLs by default; use fields for objects." params: fields: { type: string, in: query, description: "Comma-separated fields" } filter: { type: string, in: query, description: "XO filter expression" } limit: { type: integer, in: query } ndjson: { type: boolean, in: query } markdown: { type: boolean, in: query } get_vm_controller: method: GET path: /vm-controllers/{uuid} access: read description: "Get full details of a VM controller (dom0 control domain)." params: uuid: { type: string, in: path, required: true } pagination: none list_vm_controller_alarms: method: GET path: /vm-controllers/{uuid}/alarms access: read description: "List XAPI alarms for a VM controller." params: uuid: { type: string, in: path, required: true } fields: { type: string, in: query } filter: { type: string, in: query } limit: { type: integer, in: query } ndjson: { type: boolean, in: query } markdown: { type: boolean, in: query } list_vm_controller_vdis: method: GET path: /vm-controllers/{uuid}/vdis access: read description: "List VDIs attached to a VM controller." params: uuid: { type: string, in: path, required: true } fields: { type: string, in: query } filter: { type: string, in: query } limit: { type: integer, in: query } ndjson: { type: boolean, in: query } markdown: { type: boolean, in: query } list_vm_controller_messages: method: GET path: /vm-controllers/{uuid}/messages access: read description: "List XAPI messages for a VM controller." params: uuid: { type: string, in: path, required: true } fields: { type: string, in: query } filter: { type: string, in: query } limit: { type: integer, in: query } ndjson: { type: boolean, in: query } markdown: { type: boolean, in: query } list_vm_controller_tasks: method: GET path: /vm-controllers/{uuid}/tasks access: read description: "List tasks scoped to a VM controller." params: uuid: { type: string, in: path, required: true } fields: { type: string, in: query } filter: { type: string, in: query } limit: { type: integer, in: query } ndjson: { type: boolean, in: query } markdown: { type: boolean, in: query } add_vm_controller_tag: method: PUT path: /vm-controllers/{uuid}/tags/{tag} access: write description: "Add a tag to a VM controller." params: uuid: { type: string, in: path, required: true } tag: { type: string, in: path, required: true } pagination: none remove_vm_controller_tag: method: DELETE path: /vm-controllers/{uuid}/tags/{tag} access: write description: "Remove a tag from a VM controller." params: uuid: { type: string, in: path, required: true } tag: { type: string, in: path, required: true } pagination: none # ========================================================================= # HOSTS # ========================================================================= list_hosts: method: GET path: /hosts access: read description: "List all XCP-ng/XenServer hosts" params: fields: { type: string, in: query } filter: { type: string, in: query } limit: { type: integer, in: query } ndjson: { type: boolean, in: query } markdown: { type: boolean, in: query } get_host: method: GET path: /hosts/{uuid} access: read description: "Get details of a host" params: uuid: { type: string, in: path, required: true } pagination: none get_host_stats: method: GET path: /hosts/{uuid}/stats access: read description: "Get RRD statistics for a host (CPU, memory, NIC, disk). Host must be enabled." params: uuid: { type: string, in: path, required: true } granularity: { type: string, in: query, description: "'seconds' | 'minutes' | 'hours' | 'days'" } pagination: none get_host_audit_log: method: GET path: /hosts/{uuid}/audit.txt access: admin description: "Download the host's audit log as plain text. Useful for compliance and forensics." params: uuid: { type: string, in: path, required: true } response: binary: true content_type: text/plain pagination: none get_host_logs: method: GET path: /hosts/{uuid}/logs.tgz access: admin description: "Download the host's full logs as a gzipped tarball. Useful for support tickets and debugging." params: uuid: { type: string, in: path, required: true } response: binary: true content_type: application/gzip pagination: none list_host_alarms: method: GET path: /hosts/{uuid}/alarms access: read description: "List XAPI alarms (performance/health thresholds) for the host." params: uuid: { type: string, in: path, required: true } fields: { type: string, in: query } filter: { type: string, in: query } limit: { type: integer, in: query } ndjson: { type: boolean, in: query } markdown: { type: boolean, in: query } get_host_smt: method: GET path: /hosts/{uuid}/smt access: read description: "Get the SMT (Simultaneous Multi-Threading / Hyper-Threading) status of the host." params: uuid: { type: string, in: path, required: true } pagination: none list_host_missing_patches: method: GET path: /hosts/{uuid}/missing_patches access: read description: "List all patches/updates that are available but not yet installed on the host." params: uuid: { type: string, in: path, required: true } fields: { type: string, in: query } filter: { type: string, in: query } limit: { type: integer, in: query } ndjson: { type: boolean, in: query } markdown: { type: boolean, in: query } list_host_messages: method: GET path: /hosts/{uuid}/messages access: read description: "List XAPI messages/alerts emitted for the host." params: uuid: { type: string, in: path, required: true } fields: { type: string, in: query } filter: { type: string, in: query } limit: { type: integer, in: query } ndjson: { type: boolean, in: query } markdown: { type: boolean, in: query } list_host_tasks: method: GET path: /hosts/{uuid}/tasks access: read description: "List tasks scoped to this host (running/completed/failed)." params: uuid: { type: string, in: path, required: true } fields: { type: string, in: query } filter: { type: string, in: query } limit: { type: integer, in: query } ndjson: { type: boolean, in: query } markdown: { type: boolean, in: query } add_host_tag: method: PUT path: /hosts/{uuid}/tags/{tag} access: write description: "Add a tag to a host." params: uuid: { type: string, in: path, required: true } tag: { type: string, in: path, required: true } pagination: none remove_host_tag: method: DELETE path: /hosts/{uuid}/tags/{tag} access: write description: "Remove a tag from a host." params: uuid: { type: string, in: path, required: true } tag: { type: string, in: path, required: true } pagination: none management_reconfigure_host: method: POST path: /hosts/{uuid}/actions/management_reconfigure access: admin description: "Move the management interface (control plane) to a different PIF. May briefly disconnect XO from the host." params: uuid: { type: string, in: path, required: true } pif: { type: string, in: body, required: true, description: "Target PIF UUID" } sync: { type: boolean, in: query } pagination: none disable_host: method: POST path: /hosts/{uuid}/actions/disable access: admin description: "Disable a host (prepare for maintenance). Optionally evacuate VMs in the same call (body.evacuate=true, optional force/vmIdsToForceMigrate)." params: uuid: { type: string, in: path, required: true } evacuate: { type: boolean, in: body, description: "True = evacuate VMs to other hosts" } force: { type: boolean, in: body, description: "Force migration even if rules disagree (with evacuate=true)" } vmIdsToForceMigrate: { type: array, in: body, description: "Specific VM UUIDs to force-migrate" } sync: { type: boolean, in: query } pagination: none enable_host: method: POST path: /hosts/{uuid}/actions/enable access: admin description: "Re-enable a disabled host" params: uuid: { type: string, in: path, required: true } sync: { type: boolean, in: query } pagination: none # ========================================================================= # POOLS # ========================================================================= list_pools: method: GET path: /pools access: read description: "List all pools" params: fields: { type: string, in: query } filter: { type: string, in: query } limit: { type: integer, in: query } ndjson: { type: boolean, in: query } markdown: { type: boolean, in: query } get_pool: method: GET path: /pools/{uuid} access: read description: "Get details of a pool" params: uuid: { type: string, in: path, required: true } pagination: none get_pool_stats: method: GET path: /pools/{uuid}/stats access: read description: "Get aggregated RRD statistics for the entire pool." params: uuid: { type: string, in: path, required: true } granularity: { type: string, in: query, description: "'seconds' | 'minutes' | 'hours' | 'days'" } pagination: none get_pool_dashboard: method: GET path: /pools/{uuid}/dashboard access: read description: "Get dashboard for a single pool (counts of VMs/hosts/SRs, usage)." params: uuid: { type: string, in: path, required: true } pagination: none list_pool_alarms: method: GET path: /pools/{uuid}/alarms access: read description: "List XAPI alarms for the pool and its objects." params: uuid: { type: string, in: path, required: true } fields: { type: string, in: query } filter: { type: string, in: query } limit: { type: integer, in: query } ndjson: { type: boolean, in: query } markdown: { type: boolean, in: query } list_pool_missing_patches: method: GET path: /pools/{uuid}/missing_patches access: read description: "Aggregate of missing patches across all hosts in the pool." params: uuid: { type: string, in: path, required: true } fields: { type: string, in: query } filter: { type: string, in: query } limit: { type: integer, in: query } ndjson: { type: boolean, in: query } markdown: { type: boolean, in: query } list_pool_messages: method: GET path: /pools/{uuid}/messages access: read description: "List XAPI messages for the pool." params: uuid: { type: string, in: path, required: true } fields: { type: string, in: query } filter: { type: string, in: query } limit: { type: integer, in: query } ndjson: { type: boolean, in: query } markdown: { type: boolean, in: query } list_pool_tasks: method: GET path: /pools/{uuid}/tasks access: read description: "List tasks scoped to the pool." params: uuid: { type: string, in: path, required: true } fields: { type: string, in: query } filter: { type: string, in: query } limit: { type: integer, in: query } ndjson: { type: boolean, in: query } markdown: { type: boolean, in: query } add_pool_tag: method: PUT path: /pools/{uuid}/tags/{tag} access: write description: "Add a tag to a pool." params: uuid: { type: string, in: path, required: true } tag: { type: string, in: path, required: true } pagination: none remove_pool_tag: method: DELETE path: /pools/{uuid}/tags/{tag} access: write description: "Remove a tag from a pool." params: uuid: { type: string, in: path, required: true } tag: { type: string, in: path, required: true } pagination: none create_vm: method: POST path: /pools/{pool_id}/actions/create_vm access: write description: "Create a new VM from a template" params: pool_id: { type: string, in: path, required: true, description: "Pool UUID" } name_label: { type: string, in: body, required: true } template: { type: string, in: body, required: true, description: "Template UUID" } name_description: { type: string, in: body } memory: { type: integer, in: body, description: "Memory in bytes" } boot: { type: boolean, in: body, description: "Start after creation" } auto_poweron: { type: boolean, in: body } clone: { type: boolean, in: body, description: "Full clone (default true)" } cloud_config: { type: string, in: body } network_config: { type: string, in: body } vdis: { type: array, in: body } sync: { type: boolean, in: query } pagination: none emergency_shutdown_pool: method: POST path: /pools/{pool_id}/actions/emergency_shutdown access: dangerous description: "Emergency shutdown of all VMs and hosts in a pool" params: pool_id: { type: string, in: path, required: true } sync: { type: boolean, in: query } pagination: none rolling_reboot_pool: method: POST path: /pools/{pool_id}/actions/rolling_reboot access: admin description: "Sequentially reboot hosts with automatic VM migration. Zero downtime." params: pool_id: { type: string, in: path, required: true } sync: { type: boolean, in: query } pagination: none rolling_update_pool: method: POST path: /pools/{pool_id}/actions/rolling_update access: admin description: "Apply updates to hosts sequentially with live migration" params: pool_id: { type: string, in: path, required: true } sync: { type: boolean, in: query } pagination: none create_pool_network: method: POST path: /pools/{pool_id}/actions/create_network access: write description: "Create a basic network on a pool (attached to a single PIF). For aggregation use create_bonded_network; for isolated use create_internal_network." params: pool_id: { type: string, in: path, required: true } name: { type: string, in: body, required: true } description: { type: string, in: body } pif: { type: string, in: body, required: true, description: "PIF UUID to bind the network to" } mtu: { type: integer, in: body, description: "MTU (optional, default from PIF)" } vlan: { type: integer, in: body, description: "VLAN ID (optional)" } sync: { type: boolean, in: query } pagination: none create_bonded_network: method: POST path: /pools/{pool_id}/actions/create_bonded_network access: write description: "Create a bonded (link-aggregated) network across multiple PIFs in the pool." params: pool_id: { type: string, in: path, required: true } name: { type: string, in: body, required: true } description: { type: string, in: body } pifIds: { type: array, in: body, required: true, description: "Array of PIF UUIDs to bond" } bondMode: { type: string, in: body, description: "'balance-slb' | 'active-backup' | 'lacp'" } sync: { type: boolean, in: query } pagination: none create_internal_network: method: POST path: /pools/{pool_id}/actions/create_internal_network access: write description: "Create an internal-only network (host-local, no physical PIF, useful for VM-to-VM private traffic)" params: pool_id: { type: string, in: path, required: true } name_label: { type: string, in: body, required: true } name_description: { type: string, in: body } sync: { type: boolean, in: query } pagination: none management_reconfigure_pool: method: POST path: /pools/{pool_id}/actions/management_reconfigure access: admin description: "Reconfigure the management interface across all hosts in the pool. Coordinated equivalent of management_reconfigure_host." params: pool_id: { type: string, in: path, required: true } pif: { type: string, in: body, required: true, description: "Target PIF UUID (must exist on the pool master)" } sync: { type: boolean, in: query } pagination: none # ========================================================================= # STORAGE REPOSITORIES (SRs) # ========================================================================= list_srs: method: GET path: /srs access: read description: "List all storage repositories" params: fields: { type: string, in: query } filter: { type: string, in: query } limit: { type: integer, in: query } ndjson: { type: boolean, in: query } markdown: { type: boolean, in: query } get_sr: method: GET path: /srs/{uuid} access: read description: "Get details of an SR" params: uuid: { type: string, in: path, required: true } pagination: none delete_sr: method: DELETE path: /srs/{uuid} access: dangerous description: "Destroy an SR and all data on its underlying storage. Irreversible -- the SR must be detached/unplugged first. To keep the data, use forget_sr instead. Requires XO 6.5+ (DELETE /srs route returns 404 on older builds)." params: uuid: { type: string, in: path, required: true } pagination: none list_sr_alarms: method: GET path: /srs/{uuid}/alarms access: read description: "List XAPI alarms for the SR (e.g. low free space)." params: uuid: { type: string, in: path, required: true } fields: { type: string, in: query } filter: { type: string, in: query } limit: { type: integer, in: query } ndjson: { type: boolean, in: query } markdown: { type: boolean, in: query } list_sr_messages: method: GET path: /srs/{uuid}/messages access: read description: "List XAPI messages for the SR." params: uuid: { type: string, in: path, required: true } fields: { type: string, in: query } filter: { type: string, in: query } limit: { type: integer, in: query } ndjson: { type: boolean, in: query } markdown: { type: boolean, in: query } list_sr_tasks: method: GET path: /srs/{uuid}/tasks access: read description: "List tasks scoped to the SR." params: uuid: { type: string, in: path, required: true } fields: { type: string, in: query } filter: { type: string, in: query } limit: { type: integer, in: query } ndjson: { type: boolean, in: query } markdown: { type: boolean, in: query } add_sr_tag: method: PUT path: /srs/{uuid}/tags/{tag} access: write description: "Add a tag to an SR." params: uuid: { type: string, in: path, required: true } tag: { type: string, in: path, required: true } pagination: none remove_sr_tag: method: DELETE path: /srs/{uuid}/tags/{tag} access: write description: "Remove a tag from an SR." params: uuid: { type: string, in: path, required: true } tag: { type: string, in: path, required: true } pagination: none scan_sr: method: POST path: /srs/{uuid}/actions/scan access: write description: "Scan an SR to refresh its VDI list (detects manual changes on the underlying storage)." params: uuid: { type: string, in: path, required: true } sync: { type: boolean, in: query } pagination: none forget_sr: method: POST path: /srs/{uuid}/actions/forget access: dangerous description: "Forget an SR (detach without destroying data on the underlying storage)." params: uuid: { type: string, in: path, required: true } sync: { type: boolean, in: query } pagination: none reclaim_space_sr: method: POST path: /srs/{uuid}/actions/reclaim_space access: write description: "Reclaim unused space on a thin-provisioned SR." params: uuid: { type: string, in: path, required: true } sync: { type: boolean, in: query } pagination: none # ========================================================================= # VDIs -- Virtual Disk Images # ========================================================================= list_vdis: method: GET path: /vdis access: read description: "List all virtual disk images" params: fields: { type: string, in: query } filter: { type: string, in: query } limit: { type: integer, in: query } ndjson: { type: boolean, in: query } markdown: { type: boolean, in: query } get_vdi: method: GET path: /vdis/{uuid} access: read description: "Get details of a VDI" params: uuid: { type: string, in: path, required: true } pagination: none create_vdi: method: POST path: /vdis access: write description: "Create a new VDI" params: sr: { type: string, in: body, required: true, description: "SR UUID" } name_label: { type: string, in: body, required: true } name_description: { type: string, in: body } size: { type: integer, in: body, required: true, description: "Size in bytes" } other_config: { type: object, in: body, description: "Optional XAPI other_config key/value map (no longer required as of XO 6.5)" } pagination: none update_vdi: method: PATCH path: /vdis/{uuid} access: write description: "Partially update a VDI: rename (name_label / name_description) or grow the disk (size). Only the fields you pass are changed. Added in XO 6.6 -- returns 404 on older builds. Size can only be increased, never shrunk." params: uuid: { type: string, in: path, required: true } name_label: { type: string, in: body, description: "New display name" } name_description: { type: string, in: body, description: "New description" } size: { type: integer, in: body, description: "New size in bytes (grow only; must be >= current size)" } pagination: none delete_vdi: method: DELETE path: /vdis/{uuid} access: dangerous description: "Delete a VDI. Irreversible." params: uuid: { type: string, in: path, required: true } pagination: none list_vdi_alarms: method: GET path: /vdis/{uuid}/alarms access: read description: "List XAPI alarms for the VDI." params: uuid: { type: string, in: path, required: true } fields: { type: string, in: query } filter: { type: string, in: query } limit: { type: integer, in: query } ndjson: { type: boolean, in: query } markdown: { type: boolean, in: query } list_vdi_messages: method: GET path: /vdis/{uuid}/messages access: read description: "List XAPI messages for the VDI." params: uuid: { type: string, in: path, required: true } fields: { type: string, in: query } filter: { type: string, in: query } limit: { type: integer, in: query } ndjson: { type: boolean, in: query } markdown: { type: boolean, in: query } list_vdi_tasks: method: GET path: /vdis/{uuid}/tasks access: read description: "List tasks scoped to the VDI." params: uuid: { type: string, in: path, required: true } fields: { type: string, in: query } filter: { type: string, in: query } limit: { type: integer, in: query } ndjson: { type: boolean, in: query } markdown: { type: boolean, in: query } add_vdi_tag: method: PUT path: /vdis/{uuid}/tags/{tag} access: write description: "Add a tag to a VDI." params: uuid: { type: string, in: path, required: true } tag: { type: string, in: path, required: true } pagination: none remove_vdi_tag: method: DELETE path: /vdis/{uuid}/tags/{tag} access: write description: "Remove a tag from a VDI." params: uuid: { type: string, in: path, required: true } tag: { type: string, in: path, required: true } pagination: none migrate_vdi: method: POST path: /vdis/{uuid}/actions/migrate access: write description: "Migrate a VDI to a different SR (live storage migration)" params: uuid: { type: string, in: path, required: true } srId: { type: string, in: body, required: true, description: "Target SR UUID" } sync: { type: boolean, in: query } pagination: none export_vdi_vhd: method: GET path: /vdis/{uuid}.vhd access: read description: "Export a VDI as VHD" params: uuid: { type: string, in: path, required: true } preferNbd: { type: boolean, in: query } nbdConcurrency: { type: integer, in: query } response: binary: true content_type: application/octet-stream pagination: none export_vdi_raw: method: GET path: /vdis/{uuid}.raw access: read description: "Export a VDI as raw disk image" params: uuid: { type: string, in: path, required: true } preferNbd: { type: boolean, in: query } nbdConcurrency: { type: integer, in: query } response: binary: true content_type: application/octet-stream pagination: none import_vdi_to_sr: method: POST path: /srs/{sr_uuid}/vdis access: write description: "Import a VDI from VHD/raw upload into an SR" params: sr_uuid: { type: string, in: path, required: true } name_label: { type: string, in: query } name_description: { type: string, in: query } raw: { type: boolean, in: query, description: "True for raw images" } content_type: application/octet-stream pagination: none import_vdi_to_existing: method: PUT path: /vdis/{uuid}.vhd access: write description: "Replace existing VDI contents (VHD format). Size must match exactly." params: uuid: { type: string, in: path, required: true } content_type: application/octet-stream pagination: none import_vdi_to_existing_raw: method: PUT path: /vdis/{uuid}.raw access: write description: "Replace existing VDI contents (raw format). Size must match exactly." params: uuid: { type: string, in: path, required: true } content_type: application/octet-stream pagination: none # ========================================================================= # VDI SNAPSHOTS # ========================================================================= list_vdi_snapshots: method: GET path: /vdi-snapshots access: read description: "List all VDI snapshots." params: fields: { type: string, in: query } filter: { type: string, in: query } limit: { type: integer, in: query } ndjson: { type: boolean, in: query } markdown: { type: boolean, in: query } get_vdi_snapshot: method: GET path: /vdi-snapshots/{uuid} access: read description: "Get details of a VDI snapshot." params: uuid: { type: string, in: path, required: true } pagination: none delete_vdi_snapshot: method: DELETE path: /vdi-snapshots/{uuid} access: dangerous description: "Delete a VDI snapshot. Irreversible." params: uuid: { type: string, in: path, required: true } pagination: none export_vdi_snapshot: method: GET path: /vdi-snapshots/{uuid}.{format} access: read description: "Export a VDI snapshot as VHD or raw. Format is the file extension ('vhd' or 'raw')." params: uuid: { type: string, in: path, required: true } format: { type: string, in: path, required: true, description: "'vhd' or 'raw'" } response: binary: true content_type: application/octet-stream pagination: none list_vdi_snapshot_alarms: method: GET path: /vdi-snapshots/{uuid}/alarms access: read description: "List XAPI alarms scoped to a VDI snapshot." params: uuid: { type: string, in: path, required: true } fields: { type: string, in: query } filter: { type: string, in: query } limit: { type: integer, in: query } ndjson: { type: boolean, in: query } markdown: { type: boolean, in: query } list_vdi_snapshot_messages: method: GET path: /vdi-snapshots/{uuid}/messages access: read description: "List XAPI messages scoped to a VDI snapshot." params: uuid: { type: string, in: path, required: true } fields: { type: string, in: query } filter: { type: string, in: query } limit: { type: integer, in: query } ndjson: { type: boolean, in: query } markdown: { type: boolean, in: query } list_vdi_snapshot_tasks: method: GET path: /vdi-snapshots/{uuid}/tasks access: read description: "List tasks scoped to a VDI snapshot." params: uuid: { type: string, in: path, required: true } fields: { type: string, in: query } filter: { type: string, in: query } limit: { type: integer, in: query } ndjson: { type: boolean, in: query } markdown: { type: boolean, in: query } add_vdi_snapshot_tag: method: PUT path: /vdi-snapshots/{uuid}/tags/{tag} access: write description: "Add a tag to a VDI snapshot." params: uuid: { type: string, in: path, required: true } tag: { type: string, in: path, required: true } pagination: none remove_vdi_snapshot_tag: method: DELETE path: /vdi-snapshots/{uuid}/tags/{tag} access: write description: "Remove a tag from a VDI snapshot." params: uuid: { type: string, in: path, required: true } tag: { type: string, in: path, required: true } pagination: none # ========================================================================= # VBDs -- Virtual Block Devices # ========================================================================= list_vbds: method: GET path: /vbds access: read description: "List all VBDs (VDI-to-VM attachments)." params: fields: { type: string, in: query } filter: { type: string, in: query } limit: { type: integer, in: query } ndjson: { type: boolean, in: query } markdown: { type: boolean, in: query } get_vbd: method: GET path: /vbds/{uuid} access: read description: "Get details of a VBD." params: uuid: { type: string, in: path, required: true } pagination: none create_vbd: method: POST path: /vbds access: write description: "Create a VBD to attach a VDI to a VM" params: vm: { type: string, in: body, required: true, description: "VM UUID" } vdi: { type: string, in: body, required: true, description: "VDI UUID" } pagination: none delete_vbd: method: DELETE path: /vbds/{uuid} access: write description: "Delete a VBD (detach disk)" params: uuid: { type: string, in: path, required: true } pagination: none list_vbd_alarms: method: GET path: /vbds/{uuid}/alarms access: read description: "List XAPI alarms for the VBD." params: uuid: { type: string, in: path, required: true } fields: { type: string, in: query } filter: { type: string, in: query } limit: { type: integer, in: query } ndjson: { type: boolean, in: query } markdown: { type: boolean, in: query } list_vbd_messages: method: GET path: /vbds/{uuid}/messages access: read description: "List XAPI messages for the VBD." params: uuid: { type: string, in: path, required: true } fields: { type: string, in: query } filter: { type: string, in: query } limit: { type: integer, in: query } ndjson: { type: boolean, in: query } markdown: { type: boolean, in: query } list_vbd_tasks: method: GET path: /vbds/{uuid}/tasks access: read description: "List tasks scoped to the VBD." params: uuid: { type: string, in: path, required: true } fields: { type: string, in: query } filter: { type: string, in: query } limit: { type: integer, in: query } ndjson: { type: boolean, in: query } markdown: { type: boolean, in: query } connect_vbd: method: POST path: /vbds/{uuid}/actions/connect access: write description: "Hotplug a VBD to a running VM" params: uuid: { type: string, in: path, required: true } sync: { type: boolean, in: query } pagination: none disconnect_vbd: method: POST path: /vbds/{uuid}/actions/disconnect access: write description: "Hot-unplug a VBD from a running VM" params: uuid: { type: string, in: path, required: true } sync: { type: boolean, in: query } pagination: none # ========================================================================= # NETWORKS # ========================================================================= list_networks: method: GET path: /networks access: read description: "List all networks" params: fields: { type: string, in: query } filter: { type: string, in: query } limit: { type: integer, in: query } ndjson: { type: boolean, in: query } markdown: { type: boolean, in: query } get_network: method: GET path: /networks/{uuid} access: read description: "Get details of a network" params: uuid: { type: string, in: path, required: true } pagination: none delete_network: method: DELETE path: /networks/{uuid} access: dangerous description: "Delete a network" params: uuid: { type: string, in: path, required: true } pagination: none list_network_alarms: method: GET path: /networks/{uuid}/alarms access: read description: "List XAPI alarms for the network." params: uuid: { type: string, in: path, required: true } fields: { type: string, in: query } filter: { type: string, in: query } limit: { type: integer, in: query } ndjson: { type: boolean, in: query } markdown: { type: boolean, in: query } list_network_messages: method: GET path: /networks/{uuid}/messages access: read description: "List XAPI messages for the network." params: uuid: { type: string, in: path, required: true } fields: { type: string, in: query } filter: { type: string, in: query } limit: { type: integer, in: query } ndjson: { type: boolean, in: query } markdown: { type: boolean, in: query } list_network_tasks: method: GET path: /networks/{uuid}/tasks access: read description: "List tasks scoped to the network." params: uuid: { type: string, in: path, required: true } fields: { type: string, in: query } filter: { type: string, in: query } limit: { type: integer, in: query } ndjson: { type: boolean, in: query } markdown: { type: boolean, in: query } add_network_tag: method: PUT path: /networks/{uuid}/tags/{tag} access: write description: "Add a tag to a network." params: uuid: { type: string, in: path, required: true } tag: { type: string, in: path, required: true } pagination: none remove_network_tag: method: DELETE path: /networks/{uuid}/tags/{tag} access: write description: "Remove a tag from a network." params: uuid: { type: string, in: path, required: true } tag: { type: string, in: path, required: true } pagination: none # ========================================================================= # VIFs -- Virtual Network Interfaces # ========================================================================= list_vifs: method: GET path: /vifs access: read description: "List all virtual network interfaces." params: fields: { type: string, in: query } filter: { type: string, in: query } limit: { type: integer, in: query } ndjson: { type: boolean, in: query } markdown: { type: boolean, in: query } get_vif: method: GET path: /vifs/{uuid} access: read description: "Get details of a VIF." params: uuid: { type: string, in: path, required: true } pagination: none create_vif: method: POST path: /vifs access: write description: "Create a virtual network interface on a VM. The VM and network must belong to the same pool." params: vmId: { type: string, in: body, required: true, description: "VM UUID" } networkId: { type: string, in: body, required: true, description: "Network UUID" } MAC: { type: string, in: body, description: "Optional MAC address (auto-generated if omitted)" } device: { type: string, in: body, description: "Optional device index (e.g. '0', '1')" } MTU: { type: integer, in: body, description: "Optional MTU override" } txChecksumming: { type: boolean, in: body, description: "Toggle TX checksumming offload (added XO 6.5; maps to ethtool tx-checksumming / other_config). Omit for host default." } pagination: none update_vif: method: PATCH path: /vifs/{uuid} access: write description: "Partially update a VIF's properties (locking mode, allowed IPs, rate limit, TX checksumming). Only the fields you pass are changed. Added in XO 6.6 -- returns 404 on older builds. Does not move the VIF to another network; recreate it for that." params: uuid: { type: string, in: path, required: true } lockingMode: { type: string, in: body, description: "'network_default' | 'locked' | 'unlocked' | 'disabled'. 'locked' enforces the ipv4Allowed/ipv6Allowed whitelists." } ipv4Allowed: { type: array, in: body, description: "Allowed IPv4 addresses/CIDRs (array of strings) when lockingMode='locked'" } ipv6Allowed: { type: array, in: body, description: "Allowed IPv6 addresses/CIDRs (array of strings) when lockingMode='locked'" } rateLimit: { type: integer, in: body, description: "Egress rate limit in kbps (null or 0 for unlimited)" } txChecksumming: { type: boolean, in: body, description: "Toggle the ethtool TX checksumming offload" } pagination: none delete_vif: method: DELETE path: /vifs/{uuid} access: write description: "Delete a virtual network interface" params: uuid: { type: string, in: path, required: true } pagination: none connect_vif: method: POST path: /vifs/{uuid}/actions/connect access: write description: "Hotplug a VIF to a running VM (XO 6.4+)." params: uuid: { type: string, in: path, required: true } sync: { type: boolean, in: query } pagination: none disconnect_vif: method: POST path: /vifs/{uuid}/actions/disconnect access: write description: "Hot-unplug a VIF from a running VM (XO 6.4+)." params: uuid: { type: string, in: path, required: true } sync: { type: boolean, in: query } pagination: none list_vif_alarms: method: GET path: /vifs/{uuid}/alarms access: read description: "List XAPI alarms for the VIF." params: uuid: { type: string, in: path, required: true } fields: { type: string, in: query } filter: { type: string, in: query } limit: { type: integer, in: query } ndjson: { type: boolean, in: query } markdown: { type: boolean, in: query } list_vif_messages: method: GET path: /vifs/{uuid}/messages access: read description: "List XAPI messages for the VIF." params: uuid: { type: string, in: path, required: true } fields: { type: string, in: query } filter: { type: string, in: query } limit: { type: integer, in: query } ndjson: { type: boolean, in: query } markdown: { type: boolean, in: query } list_vif_tasks: method: GET path: /vifs/{uuid}/tasks access: read description: "List tasks scoped to the VIF." params: uuid: { type: string, in: path, required: true } fields: { type: string, in: query } filter: { type: string, in: query } limit: { type: integer, in: query } ndjson: { type: boolean, in: query } markdown: { type: boolean, in: query } # ========================================================================= # SDN CONTROLLER -- Network traffic rules (xo-server-sdn-controller plugin) # These routes only exist when the SDN Controller plugin is loaded. # ========================================================================= add_network_traffic_rule: method: POST path: /plugins/sdn-controller/networks/{uuid}/actions/add_traffic_rule access: write description: "Add a traffic (OpenFlow) rule to a private SDN network. Requires the SDN Controller plugin." params: uuid: { type: string, in: path, required: true, description: "Network UUID" } allow: { type: boolean, in: body, required: true, description: "true to allow matching traffic, false to drop it" } direction: { type: string, in: body, required: true, description: "Traffic direction: 'from' or 'to'" } ipRange: { type: string, in: body, required: true, description: "IP range in CIDR notation (e.g. 10.0.0.0/24)" } protocol: { type: string, in: body, required: true, description: "Protocol, e.g. 'TCP', 'UDP', 'ICMP'" } port: { type: integer, in: body, description: "Port number (for TCP/UDP)" } sync: { type: boolean, in: query } pagination: none delete_network_traffic_rule: method: POST path: /plugins/sdn-controller/networks/{uuid}/actions/delete_traffic_rule access: write description: "Remove a traffic rule from a private SDN network. Match the existing rule by direction/ipRange/protocol/port. Requires the SDN Controller plugin." params: uuid: { type: string, in: path, required: true, description: "Network UUID" } direction: { type: string, in: body, required: true, description: "Traffic direction of the rule to remove: 'from' or 'to'" } ipRange: { type: string, in: body, required: true, description: "IP range of the rule to remove" } protocol: { type: string, in: body, required: true, description: "Protocol of the rule to remove" } port: { type: integer, in: body, description: "Port of the rule to remove (for TCP/UDP)" } sync: { type: boolean, in: query } pagination: none update_network_traffic_rule: method: POST path: /plugins/sdn-controller/networks/{uuid}/actions/update_traffic_rule access: write description: "Update an existing traffic rule on a private SDN network -- match the rule by direction/ipRange/protocol/port, then set the new allow value. Added in XO 6.6. Requires the SDN Controller plugin (404 otherwise)." params: uuid: { type: string, in: path, required: true, description: "Network UUID" } direction: { type: string, in: body, required: true, description: "Traffic direction of the rule to update: 'from' or 'to'" } ipRange: { type: string, in: body, required: true, description: "IP range of the rule to update" } protocol: { type: string, in: body, required: true, description: "Protocol of the rule to update" } port: { type: integer, in: body, description: "Port of the rule to update (for TCP/UDP)" } allow: { type: boolean, in: body, required: true, description: "New value: true to allow matching traffic, false to drop it" } sync: { type: boolean, in: query } pagination: none add_vif_traffic_rule: method: POST path: /plugins/sdn-controller/vifs/{uuid}/actions/add_traffic_rule access: write description: "Add a traffic (OpenFlow) rule to a VIF connected to a private SDN network. Requires the SDN Controller plugin." params: uuid: { type: string, in: path, required: true, description: "VIF UUID" } allow: { type: boolean, in: body, required: true, description: "true to allow matching traffic, false to drop it" } direction: { type: string, in: body, required: true, description: "Traffic direction: 'from' or 'to'" } ipRange: { type: string, in: body, required: true, description: "IP range in CIDR notation (e.g. 10.0.0.0/24)" } protocol: { type: string, in: body, required: true, description: "Protocol, e.g. 'TCP', 'UDP', 'ICMP'" } port: { type: integer, in: body, description: "Port number (for TCP/UDP)" } sync: { type: boolean, in: query } pagination: none delete_vif_traffic_rule: method: POST path: /plugins/sdn-controller/vifs/{uuid}/actions/delete_traffic_rule access: write description: "Remove a traffic rule from a VIF connected to a private SDN network. Match the existing rule by direction/ipRange/protocol/port. Requires the SDN Controller plugin." params: uuid: { type: string, in: path, required: true, description: "VIF UUID" } direction: { type: string, in: body, required: true, description: "Traffic direction of the rule to remove: 'from' or 'to'" } ipRange: { type: string, in: body, required: true, description: "IP range of the rule to remove" } protocol: { type: string, in: body, required: true, description: "Protocol of the rule to remove" } port: { type: integer, in: body, description: "Port of the rule to remove (for TCP/UDP)" } sync: { type: boolean, in: query } pagination: none update_vif_traffic_rule: method: POST path: /plugins/sdn-controller/vifs/{uuid}/actions/update_traffic_rule access: write description: "Update an existing traffic rule on a VIF connected to a private SDN network -- match the rule by direction/ipRange/protocol/port, then set the new allow value. Added in XO 6.6. Requires the SDN Controller plugin (404 otherwise)." params: uuid: { type: string, in: path, required: true, description: "VIF UUID" } direction: { type: string, in: body, required: true, description: "Traffic direction of the rule to update: 'from' or 'to'" } ipRange: { type: string, in: body, required: true, description: "IP range of the rule to update" } protocol: { type: string, in: body, required: true, description: "Protocol of the rule to update" } port: { type: integer, in: body, description: "Port of the rule to update (for TCP/UDP)" } allow: { type: boolean, in: body, required: true, description: "New value: true to allow matching traffic, false to drop it" } sync: { type: boolean, in: query } pagination: none # ========================================================================= # PIFs -- Physical Network Interfaces # ========================================================================= list_pifs: method: GET path: /pifs access: read description: "List all physical network interfaces" params: fields: { type: string, in: query } filter: { type: string, in: query } limit: { type: integer, in: query } ndjson: { type: boolean, in: query } markdown: { type: boolean, in: query } get_pif: method: GET path: /pifs/{uuid} access: read description: "Get details of a PIF" params: uuid: { type: string, in: path, required: true } pagination: none list_pif_alarms: method: GET path: /pifs/{uuid}/alarms access: read description: "List XAPI alarms for the PIF (e.g. link down)." params: uuid: { type: string, in: path, required: true } fields: { type: string, in: query } filter: { type: string, in: query } limit: { type: integer, in: query } ndjson: { type: boolean, in: query } markdown: { type: boolean, in: query } list_pif_messages: method: GET path: /pifs/{uuid}/messages access: read description: "List XAPI messages for the PIF." params: uuid: { type: string, in: path, required: true } fields: { type: string, in: query } filter: { type: string, in: query } limit: { type: integer, in: query } ndjson: { type: boolean, in: query } markdown: { type: boolean, in: query } list_pif_tasks: method: GET path: /pifs/{uuid}/tasks access: read description: "List tasks scoped to the PIF." params: uuid: { type: string, in: path, required: true } fields: { type: string, in: query } filter: { type: string, in: query } limit: { type: integer, in: query } ndjson: { type: boolean, in: query } markdown: { type: boolean, in: query } # ========================================================================= # PBDs -- Physical Block Devices (SR-to-host attachments) # ========================================================================= list_pbds: method: GET path: /pbds access: read description: "List all PBDs (SR-to-host connections)" params: fields: { type: string, in: query } filter: { type: string, in: query } limit: { type: integer, in: query } ndjson: { type: boolean, in: query } markdown: { type: boolean, in: query } get_pbd: method: GET path: /pbds/{uuid} access: read description: "Get details of a PBD" params: uuid: { type: string, in: path, required: true } pagination: none plug_pbd: method: POST path: /pbds/{uuid}/actions/plug access: write description: "Plug a PBD to connect an SR to a host" params: uuid: { type: string, in: path, required: true } sync: { type: boolean, in: query } pagination: none unplug_pbd: method: POST path: /pbds/{uuid}/actions/unplug access: write description: "Unplug a PBD to disconnect an SR from a host" params: uuid: { type: string, in: path, required: true } sync: { type: boolean, in: query } pagination: none # ========================================================================= # PCIs, PGPUs, SMs -- Hardware & Drivers # ========================================================================= list_pcis: method: GET path: /pcis access: read description: "List all PCI devices (for passthrough)" params: fields: { type: string, in: query } filter: { type: string, in: query } limit: { type: integer, in: query } ndjson: { type: boolean, in: query } markdown: { type: boolean, in: query } get_pci: method: GET path: /pcis/{uuid} access: read description: "Get details of a PCI device" params: uuid: { type: string, in: path, required: true } pagination: none list_pgpus: method: GET path: /pgpus access: read description: "List all physical GPUs" params: fields: { type: string, in: query } filter: { type: string, in: query } limit: { type: integer, in: query } ndjson: { type: boolean, in: query } markdown: { type: boolean, in: query } get_pgpu: method: GET path: /pgpus/{uuid} access: read description: "Get details of a physical GPU" params: uuid: { type: string, in: path, required: true } pagination: none list_sms: method: GET path: /sms access: read description: "List all storage manager plugins/drivers" params: fields: { type: string, in: query } filter: { type: string, in: query } limit: { type: integer, in: query } ndjson: { type: boolean, in: query } markdown: { type: boolean, in: query } get_sm: method: GET path: /sms/{uuid} access: read description: "Get details of a storage manager plugin" params: uuid: { type: string, in: path, required: true } pagination: none # ========================================================================= # TASKS # ========================================================================= list_tasks: method: GET path: /tasks access: read description: "List all tasks. Use ndjson+watch for real-time streaming." params: fields: { type: string, in: query } filter: { type: string, in: query } ndjson: { type: boolean, in: query } watch: { type: boolean, in: query, description: "Watch mode (requires ndjson)" } markdown: { type: boolean, in: query } get_task: method: GET path: /tasks/{id} access: read description: "Get a task. Set wait=true to block until the task finishes (long-poll), then return its final state." params: id: { type: string, in: path, required: true } wait: { type: boolean, in: query, description: "Block until the task completes before responding" } pagination: none delete_task: method: DELETE path: /tasks/{id} access: write description: "Delete a completed or failed task" params: id: { type: string, in: path, required: true } pagination: none delete_tasks: method: DELETE path: /tasks access: dangerous description: "Bulk delete: removes ALL tasks the current user has 'task:delete' privilege on. Irreversible." pagination: none abort_task: method: POST path: /tasks/{id}/actions/abort access: write description: "Abort a running task" params: id: { type: string, in: path, required: true } sync: { type: boolean, in: query } pagination: none # ========================================================================= # MESSAGES (XAPI alerts/notifications) # ========================================================================= list_messages: method: GET path: /messages access: read description: "List XAPI messages/alerts (e.g. HA alerts, license warnings)" params: fields: { type: string, in: query } filter: { type: string, in: query } limit: { type: integer, in: query } ndjson: { type: boolean, in: query } markdown: { type: boolean, in: query } get_message: method: GET path: /messages/{uuid} access: read description: "Get details of a XAPI message" params: uuid: { type: string, in: path, required: true } pagination: none # ========================================================================= # ALARMS (XAPI performance/health alarms; distinct from messages) # ========================================================================= list_alarms: method: GET path: /alarms access: read description: "List all XAPI alarms (CPU, memory, disk, latency thresholds)." params: fields: { type: string, in: query } filter: { type: string, in: query } limit: { type: integer, in: query } ndjson: { type: boolean, in: query } markdown: { type: boolean, in: query } get_alarm: method: GET path: /alarms/{uuid} access: read description: "Get details of a single XAPI alarm." params: uuid: { type: string, in: path, required: true } pagination: none # ========================================================================= # EVENTS (Server-Sent Events for real-time XAPI changes) # ========================================================================= open_events_stream: method: GET path: /events access: read description: "Open an SSE (Server-Sent Events) connection. Returns a subscriber id which is used to add/remove subscriptions. Events emitted: ping (keepalive), add (object created), update (object changed), remove (object deleted)." pagination: none add_event_subscription: method: POST path: /events/{id}/subscriptions access: read description: "Add an object-type subscription to an open SSE connection. Returns the subscription id (= collection name)." params: id: { type: string, in: path, required: true, description: "Subscriber UUID returned by open_events_stream" } collection: { type: string, in: body, required: true, description: "XO collection: 'VM', 'host', 'pool', 'SR', 'VDI', 'task', etc." } fields: { type: string, in: body, description: "Either '*' (all fields) or array of field names" } pagination: none remove_event_subscription: method: DELETE path: /events/{id}/subscriptions/{subscriptionId} access: read description: "Remove a subscription from an open SSE connection." params: id: { type: string, in: path, required: true, description: "Subscriber UUID" } subscriptionId: { type: string, in: path, required: true, description: "Collection name (e.g. 'VM')" } pagination: none # ========================================================================= # BACKUPS -- Archives, Jobs, Logs, Restore Logs, Repositories, Schedules # ========================================================================= list_backups: method: GET path: /backup-archives access: read description: "List all backup archives" params: fields: { type: string, in: query } filter: { type: string, in: query } limit: { type: integer, in: query } ndjson: { type: boolean, in: query } markdown: { type: boolean, in: query } get_backup: method: GET path: /backup-archives/{id} access: read description: "Get details of a backup archive." params: id: { type: string, in: path, required: true } pagination: none list_backup_jobs: method: GET path: /backup-jobs access: read description: "List all configured backup jobs (VM, metadata, mirror)" params: fields: { type: string, in: query } filter: { type: string, in: query } limit: { type: integer, in: query } ndjson: { type: boolean, in: query } markdown: { type: boolean, in: query } get_backup_job: method: GET path: /backup-jobs/{id} access: read description: "Get details of a backup job." params: id: { type: string, in: path, required: true } pagination: none list_backup_logs: method: GET path: /backup-logs access: read description: "List backup execution logs" params: fields: { type: string, in: query } filter: { type: string, in: query } limit: { type: integer, in: query } ndjson: { type: boolean, in: query } markdown: { type: boolean, in: query } get_backup_log: method: GET path: /backup-logs/{id} access: read description: "Get details of a single backup log entry." params: id: { type: string, in: path, required: true } pagination: none list_restore_logs: method: GET path: /restore-logs access: read description: "List restore execution logs" params: fields: { type: string, in: query } filter: { type: string, in: query } limit: { type: integer, in: query } ndjson: { type: boolean, in: query } markdown: { type: boolean, in: query } get_restore_log: method: GET path: /restore-logs/{id} access: read description: "Get details of a single restore log entry." params: id: { type: string, in: path, required: true } pagination: none list_backup_repositories: method: GET path: /backup-repositories access: read description: "List all backup repositories (NFS, S3, SMB, local targets where backups are stored)." params: fields: { type: string, in: query } filter: { type: string, in: query } limit: { type: integer, in: query } ndjson: { type: boolean, in: query } markdown: { type: boolean, in: query } get_backup_repository: method: GET path: /backup-repositories/{id} access: read description: "Get details of a backup repository (capacity, type, status)." params: id: { type: string, in: path, required: true } pagination: none create_backup_repository: method: POST path: /backup-repositories access: write description: "Register a new backup repository ('remote') where backups are written. Added in XO 6.6 (was read-only before). Returns {id}. The url encodes the backend type (nfs://, smb://, s3://, file://)." params: name: { type: string, in: body, required: true, description: "Friendly display name" } url: { type: string, in: body, required: true, description: "Backend URL: 'nfs://host:/path', 'smb://user:pass@host\\\\share', 's3://key:secret@endpoint/bucket', or 'file:///srv/backups'" } options: { type: object, in: body, description: "Optional per-backend settings (e.g. mount options, S3 region/forcePathStyle)" } proxy: { type: string, in: body, description: "Optional XO Proxy UUID to route traffic through" } enabled: { type: boolean, in: body, description: "Whether the repository is active (default true)" } pagination: none update_backup_repository: method: PATCH path: /backup-repositories/{id} access: write description: "Update a backup repository's properties (name, url, options, enabled). PATCH semantics -- only the fields you send change. Added in XO 6.6. Set enabled=false to stop new backups writing here without removing it." params: id: { type: string, in: path, required: true } name: { type: string, in: body, description: "New display name" } url: { type: string, in: body, description: "New backend URL" } options: { type: object, in: body, description: "Per-backend settings map" } proxy: { type: string, in: body, description: "XO Proxy UUID (null to clear)" } enabled: { type: boolean, in: body, description: "Enable/disable the repository" } pagination: none delete_backup_repository: method: DELETE path: /backup-repositories/{id} access: dangerous description: "Remove a backup repository registration from XO. Added in XO 6.6. Does NOT delete the backup data on the underlying storage. Fails if backup-jobs still reference it." params: id: { type: string, in: path, required: true } pagination: none # ========================================================================= # SCHEDULES (Backup-job schedules / cron triggers) # ========================================================================= list_schedules: method: GET path: /schedules access: read description: "List all schedules (cron triggers attached to backup-jobs)." params: fields: { type: string, in: query } filter: { type: string, in: query } limit: { type: integer, in: query } ndjson: { type: boolean, in: query } markdown: { type: boolean, in: query } get_schedule: method: GET path: /schedules/{id} access: read description: "Get details of a schedule (cron expression, jobId, enabled)." params: id: { type: string, in: path, required: true } pagination: none run_schedule: method: POST path: /schedules/{id}/actions/run access: write description: "Manually trigger the backup-job behind this schedule (outside its cron). Returns task reference." params: id: { type: string, in: path, required: true } sync: { type: boolean, in: query } pagination: none # ========================================================================= # PROXIES (XO Proxy instances for distributed backups) # ========================================================================= list_proxies: method: GET path: /proxies access: read description: "List all XO Proxy instances" params: fields: { type: string, in: query } filter: { type: string, in: query } limit: { type: integer, in: query } ndjson: { type: boolean, in: query } markdown: { type: boolean, in: query } get_proxy: method: GET path: /proxies/{id} access: read description: "Get details of an XO Proxy" params: id: { type: string, in: path, required: true } pagination: none # ========================================================================= # SERVERS (XCP-ng/XenServer pool master connections) # ========================================================================= list_servers: method: GET path: /servers access: admin description: "List all XCP-ng/XenServer connections registered in XO." params: fields: { type: string, in: query } filter: { type: string, in: query } limit: { type: integer, in: query } ndjson: { type: boolean, in: query } markdown: { type: boolean, in: query } get_server: method: GET path: /servers/{id} access: admin description: "Get details of a registered server (status, label, host, username)." params: id: { type: string, in: path, required: true } pagination: none add_server: method: POST path: /servers access: admin description: "Register a new XCP-ng/XenServer pool master in XO. Returns {id}. Use connect_server afterwards to establish the XAPI connection." params: host: { type: string, in: body, required: true, description: "IP or hostname of the pool master" } username: { type: string, in: body, required: true, description: "XAPI username (typically 'root')" } password: { type: string, in: body, required: true, description: "XAPI password" } label: { type: string, in: body, description: "Friendly display name" } httpProxy: { type: string, in: body, description: "Optional HTTP proxy URL" } allowUnauthorized: { type: boolean, in: body, description: "Skip SSL cert verification (insecure)" } readOnly: { type: boolean, in: body, description: "Connect in read-only mode" } pagination: none delete_server: method: DELETE path: /servers/{id} access: admin description: "Remove a XCP-ng/XenServer connection from XO" params: id: { type: string, in: path, required: true } pagination: none connect_server: method: POST path: /servers/{id}/actions/connect access: admin description: "Establish the XAPI connection to a registered server." params: id: { type: string, in: path, required: true } sync: { type: boolean, in: query } pagination: none disconnect_server: method: POST path: /servers/{id}/actions/disconnect access: admin description: "Drop the XAPI connection to a registered server (without removing it from XO)." params: id: { type: string, in: path, required: true } sync: { type: boolean, in: query } pagination: none list_server_tasks: method: GET path: /servers/{id}/tasks access: admin description: "List tasks scoped to this server connection." params: id: { type: string, in: path, required: true } fields: { type: string, in: query } filter: { type: string, in: query } limit: { type: integer, in: query } ndjson: { type: boolean, in: query } markdown: { type: boolean, in: query } examples: - name: "List running VMs" description: "Get all running VMs with names" code: | const vms = await api.list_vms({ fields: "name_label,power_state,uuid", filter: "power_state:Running" }); return vms; - name: "Create and start a VM" description: "Create a VM from template and boot it" code: | const pools = await api.list_pools({ fields: "uuid" }); const task = await api.create_vm({ pool_id: pools[0].uuid, name_label: "my-new-vm", template: "template-uuid-here", memory: 2147483648, boot: true }); return await api.get_task({ id: task.id, wait: "result" }); - name: "Attach disk to running VM" description: "Create VDI, create VBD, hotplug" code: | const vdi = await api.create_vdi({ sr: "sr-uuid", name_label: "data-disk", size: 53687091200 }); const vbd = await api.create_vbd({ vm: "vm-uuid", vdi: vdi.uuid }); await api.connect_vbd({ uuid: vbd.uuid, sync: true }); return vbd; - name: "Host maintenance" description: "Disable host with auto-evacuation, then re-enable" code: | await api.disable_host({ uuid: "host-uuid", evacuate: true, sync: true }); // ... do maintenance work ... await api.enable_host({ uuid: "host-uuid", sync: true }); - name: "Rolling pool update" description: "Zero-downtime update of all hosts" code: | const pools = await api.list_pools({ fields: "uuid" }); const task = await api.rolling_update_pool({ pool_id: pools[0].uuid }); return await api.get_task({ id: task.id, wait: "result" }); - name: "Provision RBAC v2 read-only operator role (XO 6.4+)" description: "Create a role, grant VM/host read privileges, and attach to a user" code: | const role = await api.create_acl_role({ name: "operator-readonly", description: "Read-only access to all VMs and hosts" }); await api.create_acl_privilege({ roleId: role.id, resource: "vm", action: "read" }); await api.create_acl_privilege({ roleId: role.id, resource: "host", action: "read" }); await api.create_acl_privilege({ roleId: role.id, resource: "pool", action: "read" }); await api.attach_acl_role_to_user({ id: role.id, userId: "operator-user-uuid" }); return role; - name: "Group-based access control" description: "Create a team group, add users, attach a role" code: | const group = await api.create_group({ name: "devops-team" }); await api.add_user_to_group({ id: group.id, userId: "alice-uuid" }); await api.add_user_to_group({ id: group.id, userId: "bob-uuid" }); await api.attach_acl_role_to_group({ id: "operator-role-uuid", groupId: group.id }); return group; - name: "Trigger a scheduled backup manually" description: "Run a backup-job outside its cron schedule" code: | const schedules = await api.list_schedules({ fields: "id,name,jobId", filter: "name:nightly-backup" }); const task = await api.run_schedule({ id: schedules[0].id }); return await api.get_task({ id: task.id, wait: "result" }); - name: "Add a new XCP-ng pool to XO" description: "Register a pool master and connect" code: | const server = await api.add_server({ host: "xcp.example.com", username: "root", password: "supersecret", label: "production-pool" }); await api.connect_server({ id: server.id, sync: true }); return server; - name: "Provision VM from template list" description: "Pick the first Debian template and clone-boot a new VM" code: | const templates = await api.list_vm_templates({ fields: "uuid,name_label", filter: "name_label:Debian" }); const pools = await api.list_pools({ fields: "uuid" }); return await api.create_vm({ pool_id: pools[0].uuid, name_label: "fresh-debian", template: templates[0].uuid, memory: 2147483648, boot: true, sync: true }); - name: "Live host stats over the last hour" description: "Pull minute-granularity CPU/memory/IO for a host" code: | return await api.get_host_stats({ uuid: "host-uuid", granularity: "minutes" }); - name: "Health snapshot — alarms, missing patches, disconnected servers" description: "Quick infrastructure health overview" code: | const [alarms, dashboard] = await Promise.all([ api.list_alarms({ fields: "id,object,time,body" }), api.get_dashboard() ]); return { alarms, dashboard }; - name: "Register an NFS backup repository (XO 6.6+)" description: "Add a writable backup remote and verify it" code: | const repo = await api.create_backup_repository({ name: "nightly-nfs", url: "nfs://192.168.100.20:/srv/backups", enabled: true }); return await api.get_backup_repository({ id: repo.id }); - name: "Lock down a VIF to a single IP (XO 6.6+)" description: "Switch a VIF to locked mode and whitelist one address" code: | return await api.update_vif({ uuid: "vif-uuid-here", lockingMode: "locked", ipv4Allowed: ["10.0.0.42/32"], rateLimit: 100000 });