Learn OpenStack · core/storage/block

Cinder Internals

Driver Interface

Every Cinder volume driver inherits from cinder.volume.driver.BaseVD (and optionally from mixin classes for snapshots, cloning, backup, etc.). The driver is loaded by the cinder-volume service at startup based on the volume_driver config option in the backend stanza.

Required Driver Methods

Method Signature Description
create_volume (volume) Allocate storage for a new volume. Sets provider_location if needed.
delete_volume (volume) Release storage and clean up. Must be idempotent.
create_snapshot (snapshot) Create a point-in-time snapshot of a volume.
delete_snapshot (snapshot) Remove a snapshot.
initialize_connection (volume, connector) Export volume to a host. Returns connection_info dict consumed by os-brick on the compute node.
terminate_connection (volume, connector, **kwargs) Unexport volume from host.
get_volume_stats (refresh=False) Return dict of backend stats (free_capacity_gb, total_capacity_gb, driver_version, capabilities). Called periodically by the volume manager to update the scheduler cache.
do_setup (context) One-time initialization on service start. Connect to storage backend, validate credentials.
check_for_setup_error () Validate configuration and connectivity after do_setup. Raises exception if backend is not usable.

Optional but Common Driver Methods

Method Description
create_volume_from_snapshot Create a new volume from an existing snapshot (faster than full copy)
create_cloned_volume Clone an existing volume to a new volume
extend_volume Grow a volume to a larger size
copy_image_to_volume Write a Glance image to a volume (used during volume create --image)
copy_volume_to_image Read a volume and upload to Glance
migrate_volume Driver-assisted migration to another backend
retype Change volume type in-place without data movement (if driver supports it)
update_migrated_volume Finalize migration — update provider_id, rename backend objects
manage_existing Adopt a pre-existing backend volume
manage_existing_get_size Return size of a pre-existing volume
unmanage Release Cinder management without deleting data
failover_host Switch to replication target (replication v2.1)
enable_replication Enable replication for a volume
disable_replication Disable replication for a volume
create_consistencygroup Create a consistency group (deprecated; use generic groups)
create_group Create a generic group
create_group_snapshot Create a group snapshot

connection_info Structure

The dict returned by initialize_connection is passed to os-brick on the compute node. Structure varies by protocol:

iSCSI:

{
    "driver_volume_type": "iscsi",
    "data": {
        "target_iqn": "iqn.2010-10.org.openstack:volume-abc123",
        "target_portal": "10.0.0.30:3260",
        "target_lun": 1,
        "volume_id": "abc123...",
        "auth_method": "CHAP",
        "auth_username": "cinder-volume-abc123",
        "auth_password": "chappassword",
        "encrypted": False,
        "qos_specs": None,
        "access_mode": "rw"
    }
}

Ceph RBD:

{
    "driver_volume_type": "rbd",
    "data": {
        "name": "volumes/volume-abc123",
        "hosts": ["ceph-mon01", "ceph-mon02", "ceph-mon03"],
        "ports": ["6789", "6789", "6789"],
        "cluster_name": "ceph",
        "auth_enabled": True,
        "auth_username": "cinder",
        "secret_type": "ceph",
        "secret_uuid": "457eb676-33da-42ec-9a8c-9293d545c337",
        "volume_id": "abc123...",
        "access_mode": "rw",
        "encrypted": False
    }
}

NFS:

{
    "driver_volume_type": "nfs",
    "data": {
        "export": "10.0.0.40:/exports/cinder",
        "name": "volume-abc123",
        "options": "vers=4,sec=sys",
        "access_mode": "rw"
    }
}

Multi-Backend Configuration

Multi-backend allows a single cinder-volume process to manage multiple storage backends simultaneously.

Enable Multi-Backend

[DEFAULT]
enabled_backends = lvm-ssd,ceph-hdd,pure-nvme

[lvm-ssd]
volume_driver = cinder.volume.drivers.lvm.LVMVolumeDriver
volume_backend_name = lvm-ssd
volume_group = cinder-ssd
target_protocol = iscsi
target_helper = lioadm

[ceph-hdd]
volume_driver = cinder.volume.drivers.rbd.RBDDriver
volume_backend_name = ceph-hdd
rbd_pool = volumes-hdd
rbd_ceph_conf = /etc/ceph/ceph.conf
rbd_user = cinder
rbd_secret_uuid = 457eb676-33da-42ec-9a8c-9293d545c337

[pure-nvme]
volume_driver = cinder.volume.drivers.pure.PureISCSIDriver
volume_backend_name = pure-nvme
san_ip = 192.168.10.20
pure_api_token = T-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
use_chap_auth = true

When enabled_backends is set, the volume_driver in [DEFAULT] is ignored. Each stanza becomes an independent backend. The cinder-volume process registers one service entry per backend, e.g.: - storage01@lvm-ssd - storage01@ceph-hdd - storage01@pure-nvme

The scheduler's volume_backend_name filter matches the type's extra spec volume_backend_name against the value in each backend stanza.

Pool-Aware Scheduling

Many drivers report multiple pools within a single backend (e.g., Ceph reports pools, NetApp reports aggregates). Pool information is included in get_volume_stats:

{
    "volume_backend_name": "ceph",
    "driver_version": "2.0.0",
    "storage_protocol": "ceph",
    "pools": [
        {
            "pool_name": "volumes-ssd",
            "total_capacity_gb": 10240,
            "free_capacity_gb": 7168,
            "reserved_percentage": 0,
            "thin_provisioning_support": True,
            "thick_provisioning_support": False,
        },
        {
            "pool_name": "volumes-hdd",
            "total_capacity_gb": 51200,
            "free_capacity_gb": 40960,
            "reserved_percentage": 5,
        }
    ]
}

The full host string including pool: storage01@ceph#volumes-ssd


Scheduler Internals

The scheduler uses a two-phase pipeline: filters (eliminate ineligible backends) then weighers (rank remaining backends).

Built-In Scheduler Filters

Filter Description
AvailabilityZoneFilter Backend must be in the requested AZ (or AZ must be nova which matches any)
CapacityFilter Backend must have enough free capacity. Respects reserved_percentage and max_over_subscription_ratio
CapabilitiesFilter Backend capabilities must match volume type extra specs. Parses <is>, <in>, <all-in>, <or>, ==, !=, >=, <= operators
DriverFilter Evaluates a Python expression from the backend's filter_function capability
JsonFilter Parses complex JSON-encoded filter expressions
RetryFilter Excludes backends that were already tried and failed for this request
InstanceLocalityFilter Prefer backends local to the requesting Nova compute host (for performance)
DifferentBackendFilter Places volumes of a consistency group on different backends
SameBackendFilter Places volumes of a consistency group on the same backend
AffinityFilter Soft affinity: schedule near another volume

Default filter list in cinder.conf:

[DEFAULT]
scheduler_default_filters = AvailabilityZoneFilter,CapacityFilter,CapabilitiesFilter,DriverFilter

Built-In Scheduler Weighers

Weigher Description
CapacityWeigher Prefer backends with more free capacity (default weight multiplier: 1.0)
AllocatedCapacityWeigher Prefer backends with less allocated capacity (inverse of CapacityWeigher)
ChanceWeigher Random weight — distributes load randomly
GoodnessWeigher Uses the backend's goodness_function expression (allows custom weighting logic)
VolumeNumberWeigher Prefer backends with fewer volumes

Configure weighers:

[DEFAULT]
scheduler_default_weighers = CapacityWeigher,GoodnessWeigher
capacity_weight_multiplier = 1.0    # positive = prefer more free space
goodness_weight_multiplier = 1.0

DriverFilter and GoodnessWeigher Expressions

These allow backends to provide custom scheduling logic using Python expressions evaluated against backend stats:

[lvm-ssd]
filter_function = "stats.total_capacity_gb < 500"
goodness_function = "100 - stats.allocated_capacity_gb / stats.total_capacity_gb * 100"

Available variables: stats.* (any key from get_volume_stats), volume.* (properties of the volume being scheduled), context.*.

Scheduler Capabilities Cache

The scheduler receives update_service_capabilities RPC messages from each cinder-volume service at scheduler_driver_init_wait_time intervals (default: 60 seconds). These are stored in HostManager._pool_state_cache. This cache is in-memory and is lost on scheduler restart — the scheduler waits for at least one capability update from each backend before processing requests.


Volume Replication v2.1

Cinder replication v2.1 (introduced in Mitaka, current in 2025.x) provides disaster-recovery failover between a primary and one or more replica backends. It is a per-backend operation, not per-volume (all volumes on the backend are replicated).

Concepts

Term Description
Primary backend Normal active backend serving I/O
Secondary/replica backend Backup backend; data is replicated here asynchronously or synchronously
Replication status Per-volume: enabled, disabled, not-capable, error, failing-over, failed-over
failover_host Admin RPC command that promotes the replica to primary
freeze_host Marks a backend as not accepting new operations (pre-failover step)
thaw_host Re-enables a frozen backend

Driver Requirements for Replication v2.1

Drivers must implement: - enable_replication(context, group, volumes) — enable replication on a group of volumes - disable_replication(context, group, volumes) — disable replication - failover_replication(context, group, volumes, secondary_backend_id) — perform failover - list_replication_targets(context, group) — return available replication targets - Report replication_enabled: True and replication_targets in get_volume_stats

Configuring Replication

Example for a driver that supports replication:

[ceph-primary]
volume_driver = cinder.volume.drivers.rbd.RBDDriver
volume_backend_name = ceph-primary
rbd_pool = volumes
rbd_ceph_conf = /etc/ceph/ceph.conf
rbd_user = cinder
rbd_secret_uuid = 457eb676-33da-42ec-9a8c-9293d545c337
replication_device = backend_id:ceph-dr,conf:/etc/ceph/ceph-dr.conf,user:cinder,pool:volumes-dr,secret_uuid:88b4c97c-aaaa-bbbb-cccc-1234567890ab

Failover Procedure

# 1. Freeze the primary backend (stops new operations)
cinder --os-volume-api-version 3.0 failover-host storage01@ceph-primary --backend-id ceph-dr

# Alternatively using the admin API:
openstack volume backend failover storage01@ceph-primary --backend-id ceph-dr

# 2. After failover, cinder-volume reports host as 'storage01@ceph-primary' but
#    operating on ceph-dr. Update Nova to use new attachment paths.

# 3. To fail back (when primary is restored):
openstack volume backend failover storage01@ceph-primary --backend-id default

Database Models

Cinder uses SQLAlchemy ORM with Alembic-managed migrations. Key tables:

volumes

Column Type Description
id UUID Primary key
display_name VARCHAR(255) User-visible name
display_description VARCHAR(255) User-visible description
user_id VARCHAR(255) Owner user UUID
project_id VARCHAR(255) Owner project UUID
status VARCHAR(255) creating, available, in-use, deleting, error, migrating, maintenance, etc.
size INTEGER Size in GiB
volume_type_id UUID FK → volume_types.id
availability_zone VARCHAR(255) AZ name
host VARCHAR(255) Backend host: hostname@backend#pool
provider_location VARCHAR(255) Driver-specific location (e.g., LV path, RBD image name)
provider_id VARCHAR(255) Driver-specific identifier
encryption_key_id UUID Barbican secret UUID for encrypted volumes
replication_status VARCHAR(255) Replication state
migration_status VARCHAR(255) Migration state
multiattach BOOLEAN Multi-attach enabled
bootable BOOLEAN Marked as bootable
deleted BOOLEAN Soft-delete flag
deleted_at DATETIME Soft-delete timestamp
created_at DATETIME Creation timestamp
updated_at DATETIME Last update timestamp

snapshots

Column Type Description
id UUID Primary key
volume_id UUID FK → volumes.id
status VARCHAR(255) creating, available, deleting, error, etc.
volume_size INTEGER Size of parent volume at snapshot time
provider_location VARCHAR(255) Driver-specific snapshot location
encryption_key_id UUID Inherited from parent volume
cgsnapshot_id UUID FK → cgsnapshots.id if part of a CG snapshot
group_snapshot_id UUID FK → group_snapshots.id

backups

Column Type Description
id UUID Primary key
volume_id UUID UUID of the backed-up volume
status VARCHAR(255) creating, available, restoring, deleting, error
size INTEGER Volume size at backup time (GiB)
object_count INTEGER Number of backup objects written
container VARCHAR(255) Swift container or S3 bucket name
parent_id UUID Parent backup UUID for incremental backups
is_incremental BOOLEAN True if this is an incremental backup
has_dependent_backups BOOLEAN True if there are incrementals dependent on this backup
driver_info TEXT JSON blob of driver-specific backup metadata
encryption_key_id UUID Key UUID for encrypted volume backups

volume_types

Column Type Description
id UUID Primary key
name VARCHAR(255) User-visible type name
description VARCHAR(255) Optional description
qos_specs_id UUID FK → quality_of_service_specs.id
is_public BOOLEAN Visible to all projects
deleted BOOLEAN Soft-delete flag

volume_type_extra_specs

Column Type Description
id INTEGER Primary key
volume_type_id UUID FK → volume_types.id
key VARCHAR(255) Extra spec key
value VARCHAR(255) Extra spec value

quality_of_service_specs

Column Type Description
id UUID Primary key
name VARCHAR(255) QoS spec name
consumer VARCHAR(255) front-end, back-end, or both
specs JSON Key-value pairs of QoS parameters

volume_attachment

Column Type Description
id UUID Primary key
volume_id UUID FK → volumes.id
instance_uuid UUID Nova instance UUID
attached_host VARCHAR(255) Compute host where attached
mountpoint VARCHAR(255) Device path on compute node
attach_mode VARCHAR(36) rw or ro
attach_status VARCHAR(255) attached, detached
connection_info TEXT JSON blob from initialize_connection

RPC Flow

All inter-service communication uses oslo.messaging. Cinder uses both call (synchronous, waits for reply) and cast (fire-and-forget).

Create Volume RPC Flow

cinder-api (HTTP POST /volumes)
  │
  ├─ DB: create volumes row (status=creating)
  ├─ DB: reserve quota
  │
  ├─ scheduler_rpcapi.create_volume()   [cast to scheduler queue]
  │
cinder-scheduler (consumes from cinder-scheduler queue)
  │
  ├─ run filters → eliminate backends
  ├─ run weighers → rank backends
  ├─ select winner: storage01@ceph#volumes-ssd
  │
  ├─ volume_rpcapi.create_volume()   [cast to storage01@ceph queue]
  │
cinder-volume (storage01@ceph)
  │
  ├─ driver.create_volume(volume)
  ├─ DB: update volumes row (status=available, host=storage01@ceph#volumes-ssd)
  ├─ update quota usage

Attach Volume RPC Flow (Nova-initiated)

nova-compute
  │
  ├─ cinder_client.volumes.reserve(volume_id)          [HTTP → cinder-api]
  │   └─ DB: volume status → attaching
  │
  ├─ cinder_client.volumes.initialize_connection(       [HTTP → cinder-api]
  │       volume_id, connector)
  │   └─ volume_rpcapi.initialize_connection() [call → cinder-volume]
  │       └─ driver.initialize_connection() → returns connection_info
  │
  ├─ os-brick.connect_volume(connection_info)          [local on compute node]
  │   └─ iscsiadm / rbd map / NFS mount
  │
  ├─ libvirt: hot-attach device to instance
  │
  ├─ cinder_client.volumes.attach(volume_id,            [HTTP → cinder-api]
  │       instance_uuid, mountpoint)
  │   └─ DB: volume status → in-use, create volume_attachment row

Backup Create RPC Flow

cinder-api (POST /backups)
  │
  ├─ DB: create backups row (status=creating)
  │
  ├─ backup_rpcapi.create_backup()   [cast to cinder-backup queue]
  │
cinder-backup
  │
  ├─ Create a temporary snapshot of the volume
  ├─ Read snapshot via volume driver (attach locally or use driver export)
  ├─ Write chunks to backup store (Swift, Ceph, etc.)
  ├─ Delete temporary snapshot
  ├─ DB: update backups row (status=available, object_count, size)

Volume Manager Periodic Tasks

The cinder-volume manager runs several periodic tasks:

Task Default Interval Description
_report_driver_status 60 seconds Call get_volume_stats, publish capabilities to scheduler
_clean_temporary_snapshots 300 seconds Remove leaked temporary snapshots from failed backups
_update_volume_pg_fields 60 seconds Sync volume group membership metadata
_notify_about_service_capabilities 60 seconds oslo.notifications: publish backend stats to Ceilometer/Gnocchi
_do_cleanup At startup Recover volumes stuck in transitional states after crash

The startup cleanup (_do_cleanup) handles volumes left in creating, deleting, or attaching states after a cinder-volume crash. It resets them to error so the operator can investigate.


Thin Provisioning

Thin provisioning allows the sum of allocated volume sizes to exceed actual backend capacity. The scheduler enforces limits via:

[DEFAULT]
max_over_subscription_ratio = 20.0   # Allow 20x overcommit
reserved_percentage = 10             # Reserve 10% of capacity for internal use

A backend reports thin_provisioning_support = True and provisioned_capacity_gb in its stats. The CapacityFilter computes:

virtual_free = total_capacity * max_over_subscription_ratio
             - provisioned_capacity
             - total_capacity * reserved_percentage / 100

If virtual_free < requested_size, the backend is filtered out.

Per-backend override:

[ceph-ssd]
max_over_subscription_ratio = 10.0
reserved_percentage = 5