Learn OpenStack · core/storage/shared-filesystem
Manila Internals
Driver Interface
All Manila storage drivers inherit from manila.share.driver.ShareDriver. The driver interface defines the contract between manila-share and the storage backend. Each method maps to a specific share lifecycle operation.
Core Share Methods
class ShareDriver:
def create_share(self, context, share, share_server=None):
"""
Provision a new share on the backend.
Args:
context: RequestContext with auth info
share: Share object (dict-like) with keys:
id, size, share_proto, share_network_id, etc.
share_server: ShareServer object (DHSS=true only); contains
the backend_details dict with IPs and credentials
populated during share server setup.
Returns:
list of export location dicts, each containing:
{'path': 'host:/export/path',
'is_admin_only': False,
'metadata': {...}}
"""
def delete_share(self, context, share, share_server=None):
"""
Delete the share from the backend and release storage.
Must be idempotent — safe to call on an already-deleted share.
"""
def allow_access(self, context, share, access, share_server=None):
"""
Grant a client access to the share.
access dict keys:
access_type: 'ip', 'user', 'cert', 'cephx'
access_to: CIDR, username, cert CN, or CephX client name
access_level: 'rw' or 'ro'
"""
def deny_access(self, context, share, access, share_server=None):
"""
Revoke a previously granted access rule.
Must be idempotent — safe if the rule no longer exists.
"""
Snapshot Methods
def create_snapshot(self, context, snapshot, share_server=None):
"""
Create a point-in-time snapshot of a share.
snapshot dict contains:
id, share_id, share_size, share_proto
Returns: dict of provider_location or None
"""
def delete_snapshot(self, context, snapshot, share_server=None):
"""
Delete a snapshot. Must be idempotent.
"""
def create_share_from_snapshot(self, context, share, snapshot,
share_server=None, parent_share=None):
"""
Create a new share cloned/hydrated from a snapshot.
Returns: list of export location dicts (same as create_share).
"""
def revert_to_snapshot(self, context, snapshot, share_access_rules,
snapshot_access_rules, share_server=None):
"""
Revert the share in-place to the given snapshot state.
All data written after the snapshot is destroyed.
"""
Manage/Unmanage
def manage_existing(self, share, driver_options):
"""
Take control of an existing export that was created outside Manila.
driver_options: dict of backend-specific hints from the operator
Returns: dict with 'size' (GiB) and 'export_locations'
"""
def unmanage(self, share):
"""
Release Manila's management of a share without deleting it.
"""
Capacity and Capability Reporting
def _update_share_stats(self):
"""
Called periodically (default every 60 seconds) by the share manager.
Must populate self._stats dict with:
{
'share_backend_name': 'CEPHFS',
'vendor_name': 'Red Hat',
'driver_version': '1.0',
'storage_protocol': 'NFS',
'total_capacity_gb': 10000.0,
'free_capacity_gb': 7500.0,
'reserved_percentage': 0,
'snapshot_support': True,
'create_share_from_snapshot_support': True,
'revert_to_snapshot_support': False,
'replication_type': None,
'driver_handles_share_servers': False,
'pools': [
{
'pool_name': 'ceph-pool-0',
'total_capacity_gb': 10000.0,
'free_capacity_gb': 7500.0,
'allocated_capacity_gb': 2500.0,
'reserved_percentage': 0,
'snapshot_support': True,
}
]
}
"""
Capability stats flow:
manila-share (driver._update_share_stats)
│ every 60s via share_manager.py periodic task
▼
DB: share_stats table (or in-memory cache)
│
▼
manila-scheduler reads stats when evaluating filters
Extend and Shrink
def extend_share(self, share, new_size, share_server=None):
"""
Increase share size to new_size GiB.
Backend must resize the underlying filesystem/quota.
"""
def shrink_share(self, share, new_size, share_server=None):
"""
Decrease share size to new_size GiB.
Backend must verify current usage <= new_size before shrinking.
Raise ShareShrinkingPossibleDataLoss if current usage > new_size.
"""
Share Server Lifecycle (DHSS=true)
In DHSS=true mode, manila-share manages the full lifecycle of share server infrastructure. The generic driver is the reference implementation; hardware drivers implement analogous steps.
Share Server Creation
Triggered when a share is first created on a share network that has no active share server for that backend.
User: openstack share create --share-network share-net NFS 10
│
manila-api: write share record (status=creating) → RPC to scheduler
│
manila-scheduler: select manila-share host → RPC to manila-share
│
manila-share.share_manager.create_share():
│
├─ 1. Check for existing share server on this (share_network, host) pair
│ └─ If none: call _setup_server()
│
├─ _setup_server():
│ │
│ ├─ 2. Allocate Neutron ports
│ │ manila calls Neutron API:
│ │ POST /v2.0/ports (on tenant's neutron_net_id)
│ │ → gets port IP, MAC address
│ │
│ ├─ 3. Spawn share server VM (generic driver)
│ │ manila calls Nova API:
│ │ POST /v2.1/servers
│ │ image: manila-service-image
│ │ flavor: service_instance_flavor_id
│ │ networks: [service_net, tenant_net_port]
│ │ → polls until VM is ACTIVE
│ │
│ ├─ 4. Wait for SSH availability
│ │ manila SSHes into the new VM to verify it is ready
│ │
│ ├─ 5. driver.setup_server()
│ │ installs NFS-Ganesha or Samba on the VM via SSH
│ │ configures exports directory
│ │
│ └─ 6. Write share_server record to DB
│ status=active
│ backend_details: {
│ 'instance_id': '<nova-vm-uuid>',
│ 'service_ip': '10.0.0.100',
│ 'tenant_ip': '10.0.1.10',
│ 'username': 'manila',
│ 'pk_path': '/tmp/...'
│ }
│
└─ driver.create_share(share, share_server)
uses backend_details to SSH/call backend API
creates the actual filesystem/export
returns export_locations
Share Server Reuse
By default, Manila reuses an existing share server for subsequent shares on the same (share_network, backend) pair:
share_network "share-net" + backend "generic@host1"
└── share_server SS-001 (active, tenant_ip=10.0.1.10)
├── share-A (export: 10.0.1.10:/shares/share-A)
├── share-B (export: 10.0.1.10:/shares/share-B)
└── share-C (export: 10.0.1.10:/shares/share-C)
Configure share server sharing:
[DEFAULT]
# Reuse share servers across shares on the same network (default: true)
# Set to false to create one share server per share
use_scheduler_creating_share_from_snapshot = true
Share Server Deletion
All shares on share server are deleted
│
manila-share: periodic cleanup task
│
├─ Check: any shares still using this server? No →
│
├─ driver.teardown_server()
│ SSH to VM: stop Ganesha/Samba, unmount volumes
│
├─ Delete Nova VM (Nova API DELETE /v2.1/servers/<id>)
│
├─ Delete Neutron ports (Neutron API DELETE /v2.0/ports/<id>)
│
└─ Delete Cinder volumes if any
(Cinder API DELETE /v3/<proj>/volumes/<id>)
Cleanup interval: unused_share_server_cleanup_interval (default: 10 minutes).
manila-data Service
The manila-data service performs the data-plane work for host-assisted share migration. It is a standalone process with its own oslo.messaging consumer.
Migration Data Copy Flow
manila-api: migration_start RPC → manila-data
│
manila-data.data_manager.migration_start():
│
├─ 1. Create destination share (via manila-share RPC, status=migrating_to)
│
├─ 2. Add temporary access rules to source (rw for data service IP)
│ Add temporary access rules to destination (rw for data service IP)
│
├─ 3. Mount source share locally
│ /tmp/manila-migration/<src-share-id>/
│
├─ 4. Mount destination share locally
│ /tmp/manila-migration/<dst-share-id>/
│
├─ 5. Iterative copy loop (using shutil or rsync-style logic)
│ task_state = data_copying_in_progress
│ progress percentage updated in DB
│
├─ 6. On migration_complete RPC from API:
│ Final rsync pass (catch changes during copy)
│ Unmount both shares
│ Remove temporary access rules
│
├─ 7. Driver swap (source export → destination export)
│ Update share.export_locations in DB
│ Update share.host, share.share_type, share.share_network
│ Delete source share from old backend
│
└─ 8. task_state = None, status = available
Share record now points to destination backend
The manila-data service requires:
- Network access to both source and destination share export paths
- Sufficient local disk for temporary mount points (not data — it streams)
- The NFS/CIFS client utilities installed on the data service host
Scheduler Filters
The scheduler runs a pipeline of filter classes to eliminate incompatible share backends, then weighers to rank them. The winning backend receives the share creation RPC.
Filter Pipeline
share_create request
│
▼
[all active manila-share nodes with their reported capabilities]
│
├─ AvailabilityZoneFilter
│ Keep only backends in the requested AZ (or all AZs if not specified)
│
├─ CapacityFilter
│ Keep backends where:
│ free_capacity_gb >= requested_size
│ free_capacity_gb > total_capacity_gb * reserved_percentage / 100
│
├─ CapabilitiesFilter
│ Match share type extra_specs against backend capability dict
│ Example: snapshot_support=True must match backend's snapshot_support=True
│ Supports operators: =, <, >, <=, >=, <is>, <in>, <or>
│
├─ ShareReplicationFilter
│ When creating a replica: require that backend has same replication_type
│ as the existing active replica's backend
│
└─ [custom filters via scheduler_default_filters config]
CapabilitiesFilter Matching Rules
The filter compares share type extra_specs to backend capability keys:
| Extra Spec | Backend Value | Match? |
|---|---|---|
snapshot_support=True |
snapshot_support: True |
Yes |
snapshot_support=True |
snapshot_support: False |
No |
replication_type=readable |
replication_type: readable |
Yes |
replication_type=readable |
replication_type: dr |
No |
provisioning:max_share_size=<= 500 |
provisioning:max_share_size: 1000 |
Yes (500 <= 1000) |
Weighers
After filtering, weighers score remaining backends. The default weigher is CapacityWeigher, which preferentially places shares on the backend with the most free capacity (or least, depending on capacity_weight_multiplier):
[DEFAULT]
# Positive = place on most free backend (default)
# Negative = place on least free backend (spread usage evenly)
capacity_weight_multiplier = 1.0
Scheduler Configuration
[DEFAULT]
# Filter classes to use (comma-separated)
scheduler_default_filters = AvailabilityZoneFilter,CapacityFilter,CapabilitiesFilter,ShareReplicationFilter
# Weigher classes to use
scheduler_default_weighers = CapacityWeigher
# How long (seconds) before a backend's stats are considered stale
scheduler_driver = manila.scheduler.drivers.filter.FilterScheduler
Database Models
Manila uses SQLAlchemy with Alembic migrations. The schema lives in manila/db/migrations/alembic/versions/.
Core Tables
shares
├── id UUID primary key
├── deleted soft-delete flag
├── user_id owner user
├── project_id owner project
├── host manila-share host (e.g., "manila@cephfs#pool")
├── size GiB
├── status creating | available | error | deleting | migrating | ...
├── task_state migration/replication in-progress state
├── share_proto NFS | CIFS | CephFS | ...
├── share_type_id FK → share_types
├── share_network_id FK → share_networks (nullable, DHSS=true only)
├── share_server_id FK → share_servers (nullable, DHSS=true only)
└── is_public boolean
share_instances
├── id UUID; a share can have multiple instances (replicas)
├── share_id FK → shares
├── host
├── status
├── replica_state active | in_sync | out_of_sync (replication only)
└── availability_zone
export_locations
├── id
├── share_instance_id FK → share_instances
├── path mount path string
└── is_admin_only admin-visible paths (e.g., management IPs)
share_access_map
├── id
├── share_id FK → shares
├── access_type ip | user | cert | cephx
├── access_to CIDR, username, cert CN, CephX id
├── access_level rw | ro
└── state active | error | queued_to_apply | queued_to_deny
share_types
├── id
├── name
├── is_public
└── extra_specs[] FK → share_type_extra_specs
share_type_extra_specs
├── share_type_id FK → share_types
├── key
└── value
share_networks
├── id
├── project_id
└── name
share_network_subnets
├── id
├── share_network_id FK → share_networks
├── neutron_net_id
├── neutron_subnet_id
└── availability_zone_id
share_servers
├── id
├── share_network_subnet_id FK → share_network_subnets
├── host
├── status creating | active | deleting | error
└── backend_details JSON blob (driver-specific: IPs, credentials, VM IDs)
share_snapshots
├── id
├── share_id
├── size
├── status
└── provider_location (driver-specific opaque reference)
share_groups
├── id
├── project_id
├── share_type_ids[]
└── status
share_group_snapshots
├── id
├── share_group_id
└── status
State Machine for Shares
┌─────────┐
─────▶ │ creating│
└────┬────┘
│ create_share() success
▼
┌─────────┐
┌──────────│available│◀──────────────────┐
│ └────┬────┘ │
│ │ │
extending deleting extending/
shrinking │ shrinking done
│ ┌────▼────┐
└────▶ │ deleting│
└────┬────┘
│
┌────▼────┐
│ deleted │
└─────────┘
Additional states:
error — driver raised an exception
migrating — migration_start called on source
migrating_to — this instance is the migration destination
manage_starting — manage_existing() in progress
unmanage_starting — unmanage() in progress
replication_change — replica promotion in progress
Access Rule State Machine
queued_to_apply → applying → active
→ error
queued_to_deny → denying → deleted
→ error
Access rules are applied asynchronously. After allow_access is called, the rule enters queued_to_apply. The manila-share periodic task picks up queued rules and calls the driver's allow_access() method. On success the state transitions to active.
Periodic Tasks
manila-share runs several periodic tasks via the oslo.service periodic task framework:
| Task | Default Interval | Purpose |
|---|---|---|
_update_share_stats |
60 seconds | Report capacity/capabilities to scheduler |
_update_access_rules_if_needed |
60 seconds | Apply/deny queued access rules |
delete_free_share_servers |
600 seconds | GC unused share servers (DHSS=true) |
_do_share_replication_update |
300 seconds | Trigger backend replica sync check |
update_share_instances_access |
300 seconds | Reconcile access on recovered instances |
RPC Versioning
Manila uses oslo.messaging versioned RPC with oslo.versionedobjects. API service calls follow:
manila-api
│ cast/call to manila.scheduler.rpcapi
│ topic: manila-scheduler
▼
manila-scheduler
│ cast to manila.share.rpcapi
│ topic: manila-share.<host>
▼
manila-share
│ cast/call to manila.data.rpcapi (migration only)
│ topic: manila-data
▼
manila-data
Each rpcapi module specifies RPC_API_VERSION and uses can_send_version() for rolling upgrade compatibility, allowing mixed-version deployments during upgrades.