Preview — pending review
This reference is generated from the platform API contract and is still under review. Operations, fields, and descriptions can change before publication.
Attach a volume to an instance
Attaches the block volume volumeId to a compute instance in the same scope and
availability zone, exposing it under a device path (e.g. /dev/sdf). Triggered from the
volume detail header and the per-row "Attach" action. On success the volume's attachedTo
is populated with {instanceId, instanceName, device} and its status moves to IN_USE; the
resulting state is visible to subsequent reads of the volume and the attached instance.
Validation: volumeId must reference an existing volume in the scope and instanceId
a reachable instance (unknown → 404 NOT_FOUND); the current state must permit attach — the
volume must be AVAILABLE, or already-attached only when multiAttach is enabled
(violation → 409 CONFLICT); the instance and volume must share an availability zone and the
requested device path, when supplied, must be free on the instance (→ 422
VALIDATION_FAILED or 409 CONFLICT); caller needs owner|admin|contributor (→ 403).
Async & state: Conditional — a fast attach completes synchronously with 200 returning
the updated Volume (AVAILABLE → IN_USE, attachedTo set); a slow attach returns 202 with
an Operation and the same terminal transition. Progress is emitted as resource.updated SSE
frames (kind storage.volume, patch {status, attachedTo, updatedAt}).
Errors: 401 unauthenticated; 403 caller lacks permission on the scope; 404 unknown
scopeId, volumeId or instanceId; 409 CONFLICT volume already attached without
multi-attach, device path in use, or state does not permit attach; 412 If-Match mismatch;
422 zone mismatch or invalid device path; 500 unexpected backend failure.
Idempotency/Concurrency: Idempotency-Key is optional (recommended); replaying the same
key returns
the original outcome instead of double-attaching. If-Match with the volume ETag guards
against attaching a concurrently modified volume.
bearerTokenAuthorizationBearer <token>An API token (pc_live_…) or a service-account key (sak_live_…), created in Identity & Access Management and sent as Authorization: Bearer <token>. Requests are rate limited per credential and per tenant: every response carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset, and a refused request gets 429 RATE_LIMITED with Retry-After.
scopeId*stringOpaque ownership+authorization scope resolving to tenant + subscription + project.
volumeId*stringIdentifier of the block volume.
Idempotency-Key?stringOptional; recommended for safe retries. When sent, the same key+body within 24h replays the original result and the same key with a different body is 409 CONFLICT; keys are unique per tenant. When an API token or service-account (bearer) caller omits it, the request is applied once with no replay protection (a retry may apply it again). A console session gets one from the edge.
uuidIf-Match?stringETag for optimistic concurrency on mutations/deletes; mismatch results in 412.
application/json- body
Payload for attaching a volume to a compute instance.
instanceId*stringIdentifier of the target compute instance in the same scope and availability zone.
device?|Requested device path (e.g. /dev/sdf); the platform assigns the next free path when omitted.
Attach completed synchronously; returns the updated volume (status IN_USE, attachedTo set).
application/json- response
Block volume read model as rendered by the Volumes page and the volume detail page.
origin?stringWhere the resource was made -- portal for a create on the console, api for an object made straight against the OpenStack API (CLI, Terraform, the Kubernetes cloud provider) that discovery adopted. Informational; nothing behaves differently. Absent from a backend that predates it, which means portal.
"portal""api""system""imported"id*stringVolume identifier (e.g. vol-0a1b2c3d).
name*stringVolume display name.
status*stringLifecycle status of the volume.
"CREATING""AVAILABLE""IN_USE""AWAITING_TRANSFER""ERROR"statusReason*|Human-readable, customer-facing reason for the current status when it is a failure (e.g. "This request exceeds the quota available to this project. Free up existing resources or request a quota increase." on an ERROR volume); never raw provider text. Null while the volume is healthy or the failure carried no detail.
statusReasonCode*|Machine-readable companion to statusReason: a small, OPEN vocabulary of codes the console can branch on without parsing the human text, deliberately a plain string rather than a closed enum so a code the client does not recognize yet degrades to the generic failure message. Today the only value is cancelled: the resource is in its failure state because its create operation was CANCELLED; a provider object the create had already built may be left in place, and the resource's DELETE removes it. Reported only while the resource is in that failure state; null otherwise.
sizeGb*integerProvisioned size in GB (1–16,384).
type*|Volume performance tier. Null when the volume sits on a provider volume type no platform tier maps to (e.g. a volume adopted from an imported project): the tier is then UNKNOWN and unpriced, never reported as a tier it is not. providerVolumeType carries the provider's own type name in that case.
providerVolumeType*|The provider's (Cinder's) own volume type name, present only when type is null because no platform tier maps to it (an adopted volume on a legacy type). Null for a volume on a platform tier. Display-only: it has no price and cannot be requested.
attachedTo*|nullInstance attachment (instance id, name, device), or null when detached.
bootable*booleanWhether the volume carries a bootable OS image.
encrypted*booleanWhether the volume is encrypted at rest, DERIVED from the provider's own view of the volume (the volume type's encryption spec) — never from the create request. False until the provider confirms encryption, so this field never asserts a security property the platform did not actually deliver.
multiAttach*booleanAllow attaching to more than one instance (RWX).
availabilityZone*stringAvailability zone the volume lives in (e.g. ist-1a).
description*stringOperator-supplied description.
source*Provenance of a block volume — empty, created from an image, restored from a snapshot, or cloned from another volume.
kmsKeyId*|Customer-managed KMS key for envelope encryption; null when platform-managed or encryption is off.
keyRotationDays*integerAutomatic key-rotation period in days (0 = manual).
pricePerGbMonthlyCents*|Price per GB per month in hundredths of the currency unit (an integer, not a Money object): the rate billing meters this volume's tier at for the volume's subscription, rounded down to the hundredth. Null when no price applies to the tier: there is no per-type default figure.
deletionProtection*booleanPlatform-owned deletion guard. While true, storage.volume.delete is refused with 409 DELETION_PROTECTED before any work starts; turn it off with the metadata PATCH, then delete. It guards the console and this API ONLY: Cinder has no lock, so an OpenStack API credential with Full access can still delete the volume directly. False for every volume unless a caller turned it on; the collector never writes it.
tags*array<>Key-value pairs shown on the Tags tab; double as Cost Explorer dimensions.
createdAt*stringCreation timestamp (ISO 8601).
date-timeupdatedAt*stringLast modification timestamp (ISO 8601).
date-timecurl -X POST "https://example.com/scopes/string/storage/volumes/string/actions/attach" \ -H "Content-Type: application/json" \ -d '{ "instanceId": "string" }'{ "origin": "portal", "id": "string", "name": "string", "status": "CREATING", "statusReason": "string", "statusReasonCode": "string", "sizeGb": 0, "type": "Standard SSD", "providerVolumeType": "string", "attachedTo": { "instanceId": "string", "instanceName": "string", "device": "string" }, "bootable": true, "encrypted": true, "multiAttach": true, "availabilityZone": "string", "description": "string", "source": { "kind": "empty", "ref": "string" }, "kmsKeyId": "string", "keyRotationDays": 0, "pricePerGbMonthlyCents": 0, "deletionProtection": true, "tags": [ { "key": "string", "value": "string" } ], "createdAt": "2019-08-24T14:15:22Z", "updatedAt": "2019-08-24T14:15:22Z"}