FABRIC Ceph Manager Service
A lightweight REST service that orchestrates Ceph Dashboard APIs across one or more Ceph clusters. It standardizes common workflows:
-
CephX users: list, create/update (upsert), delete, export keyrings, and synchronize the same keyring across clusters.
-
CephFS subvolumes: create subvolume groups, create/resize subvolumes, get paths, existence checks, and delete — applied consistently across clusters.
-
Multi-cluster logic:
- “First success” discovery APIs (e.g., list users, export keyrings, get subvolume info).
- “Ensure/sync” APIs (create/update on a source cluster, then fan-out imports/operations to other clusters).
Built with Flask/Connexion (OpenAPI-first), requests for Dashboard calls, and paramiko to run ceph auth import over SSH when replicating CephX secrets.
Table of Contents
Features
-
🔐 CephX user lifecycle via Dashboard REST:
GET /cluster/user— listPOST /cluster/user— createPUT /cluster/user— update (with upsert in helper)DELETE /cluster/user/{entity}— deletePOST /cluster/user/export— export keyrings (JSON or plaintext)
-
📦 Keyring fan-out: export the keyring from a “source” cluster and SSH import it to all other clusters (
ceph auth import) so the same secret is valid everywhere. -
🗂 CephFS subvolumes:
PUT /cephfs/subvolume/{vol_name}— create or resize subvolumeGET /cephfs/subvolume/{vol_name}/info— info/getpathGET /cephfs/subvolume/{vol_name}/exists— existence probeDELETE /cephfs/subvolume/{vol_name}— delete
-
🧠 Cross-cluster helpers (in
cluster_helper.py):ensure_user_across_clusters,update_user_across_clusters,delete_user_across_clustersensure_subvolume_across_clusters,delete_subvolume_across_clusterslist_users_first_success,export_users_first_success
-
🧾 Clear OpenAPI 3.0 contract (
openapi.yml) and Connexion handlers.
Architecture
+-------------------+ +---------------------+
Client ---> | Flask + Connexion| REST ---> | DashClient (REST) |
| (OpenAPI-first) | | /api/auth + calls |
+-------------------+ +----------+----------+
| |
| | Keyring text
| export_keyring()
v |
+-------------------+ |
| cluster_helper | <-- SSH (paramiko) --+
| (multi-cluster | ceph auth import
| orchestration) |
+-------------------+
- DashClient logs into each cluster’s Dashboard (
/api/auth) to get a JWT then calls the documented endpoints. - For keyring replication, the service uses SSH to run
ceph auth importon the target clusters so the secret stays identical. - Config-driven multi-cluster: names, endpoints, creds, and SSH parameters per cluster.
OpenAPI
-
Spec:
fabric_ceph/openapi.yml -
Served with Connexion; controller modules:
fabric_ceph.openapi_server.controllers.cluster_user_controllerfabric_ceph.openapi_server.controllers.ceph_fs_controllerfabric_ceph.openapi_server.controllers.version_controller
Notable behaviors
X-Clusterheader (optional): influence which clusters are tried and in what order, e.g.X-Cluster: europe,us-westapplication/jsoneverywhere.- Errors are normalized using
status_4xx/status_5xxschemas.
Code generation
The OpenAPI spec for this service is maintained on SwaggerHub:
https://app.swaggerhub.com/apis/RENCI3/ceph/1.0.0
Use the commands below to validate the spec and generate fresh Flask server stubs.
The helper script will archive the previous scaffold to openapi_server_archive/ and place the newly generated code in openapi_server/ — you can then review and merge changes as needed.
Prereqs:
openapi-generatorinstalled (e.g.,brew install openapi-generator) and Java available on your PATH.
cd fabric_ceph
# Validate the spec
openapi-generator validate -i openapi.yml
# Generate a Python Flask server scaffold (into a temp folder)
openapi-generator generate -i openapi.yml -g python-flask -o python-flask-server-generated
# Archive the old server code and install the new one
./swagger_code_gen.sh
Configuration
Example config.yml:
cluster:
europe:
ceph_cli: ceph
default_fs: CEPH-FS-01
dashboard:
endpoints: [ "https://10.145.126.2:8443" ]
user: admin
password: abcd1234
ssh_user: rocky # used for importing keyrings
ssh_key: ~/.ssh/id_rsa_ceph
rgw_admin:
endpoints: [ "http://10.145.124.2:8080" ]
admin_access_key: admin_ak
admin_secret_key: admin_sk
ssh_user: rocky
ssh_key: ~/.ssh/id_rsa_ceph
runtime:
service_project:
logging:
log-directory: /var/log/actor
log-file: actor.log
metrics-log-file: metrics.log
log-level: INFO
log-retain: 5
log-size: 5000000
logger: ceph-mgr
oauth:
jwks-url: https://cm.fabric-testbed.net/credmgr/certs
key-refresh: 00:10:00
verify-exp: true
core_api:
enable: true
host: https://uis.fabric-testbed.net
token:
Environment overrides (per cluster, optional):
EUROPE_SSH_HOST, EUROPE_SSH_PORT, EUROPE_SSH_USER, EUROPE_SSH_KEY, EUROPE_SSH_PASSWORD
Set
APP_CONFIG_PATHto point the service at your YAML.
Running locally
python -m venv .venv
. .venv/bin/activate
pip install --upgrade pip
# Install service
pip install -r requirements.txt
pip install -e .
# Run
export APP_CONFIG_PATH=/path/to/config.yml
python -m fabric_ceph
# or just: fabric_ceph
By default, the server binds to the host/port your app config or entrypoint sets (commonly 3500 are exposed in Docker; adjust to taste).
Docker
Dockerfile is included. Make sure your build context includes README.md, pyproject.toml, and fabric_ceph/.
Build & run:
# From repo root
docker build -t fabric-ceph .
docker run --rm -p 3500:3500 \
-e APP_CONFIG_PATH=/etc/fabric/ceph/config/config.yml \
-v /local/config.yml:/etc/fabric/ceph/config/config.yml:ro \
fabric-ceph
The container starts
cron(optional) and runs the module with the system Python (3.13). Ensure your requirements includeconnexion,requests, andparamiko.
API usage (curl)
Headers
# (If using bearer auth via Dashboard login)
-H "Authorization: Bearer <jwt>"
# Optional: influence cluster order
-H "X-Cluster: europe,lab"
CephX users
List users:
curl -s http://localhost:3500/cluster/user
Create or update (upsert handled server-side):
curl -s -X POST http://localhost:3500/cluster/user \
-H "Content-Type: application/json" \
-d '{
"user_entity": "client.demo",
"capabilities": [
{"entity": "mon", "cap": "allow r"},
{"entity": "mds", "cap": "allow rw fsname=CEPH-FS-01 path=/volumes/_nogroup/demo"},
{"entity": "osd", "cap": "allow rw tag cephfs data=CEPH-FS-01"},
{"entity": "osd", "cap": "allow rw tag cephfs metadata=CEPH-FS-01"}
]
}'
Update caps only:
curl -s -X PUT http://localhost:3500/cluster/user \
-H "Content-Type: application/json" \
-d '{"user_entity":"client.demo","capabilities":[{"entity":"mon","cap":"allow r"}]}'
Delete:
curl -s -X DELETE http://localhost:3500/cluster/user/client.demo
Export keyring(s):
curl -s -X POST http://localhost:3500/cluster/user/export \
-H "Content-Type: application/json" \
-d '{"entities":["client.demo","client.alice"]}'
Internally, cross-cluster sync helpers can export the keyring from a source and SSH-import it to others, ensuring identical secrets everywhere.
CephFS subvolumes
Create or resize subvolume:
# create with mode and quota
curl -s -X PUT http://localhost:3500/cephfs/subvolume/CEPH-FS-01 \
-H "Content-Type: application/json" \
-d '{"subvol_name":"alice","group_name":"fabric_staff","size":10737418240,"mode":"0777"}'
# resize quota
curl -s -X PUT http://localhost:3500/cephfs/subvolume/CEPH-FS-01 \
-H "Content-Type: application/json" \
-d '{"subvol_name":"alice","group_name":"fabric_staff","size":536870912}'
Get path/info:
curl -s "http://localhost:3500/cephfs/subvolume/CEPH-FS-01/info?subvol_name=alice&group_name=fabric_staff"
Exists:
curl -s "http://localhost:3500/cephfs/subvolume/CEPH-FS-01/exists?subvol_name=alice&group_name=fabric_staff"
Delete:
curl -s -X DELETE "http://localhost:3500/cephfs/subvolume/CEPH-FS-01?subvol_name=alice&group_name=fabric_staff"
Multi-cluster behavior
-
First-success queries:
list_users_first_successreturns the first cluster that responds successfully.export_users_first_successexports keyrings from the first cluster that can.get_subvolume_info/subvolume_existstry clusters inX-Clusterorder (or config order) and return on first success.
-
Ensure/sync mutations:
ensure_user_across_clusters/update_user_across_clusters: pick a source (existing user or preferred cluster), apply caps (create if missing), export keyring, SSH import everywhere else.ensure_subvolume_across_clusters: ensure group, create/resize subvolume on a source, then apply the same to all clusters; returns per-cluster paths.delete_*_across_clusters: best-effort deletes with per-cluster results.
Security notes
- Dashboard credentials grant cluster-admin control; protect
config.yml. - SSH key used for
ceph auth importshould be restricted and rotated. - Keyring fan-out intentionally makes the same secret valid across clusters — treat the exported keyring as sensitive data.
- TLS verification: default behavior depends on your
DashClientsettings. Prefer HTTPS with CA validation in production.
License
MIT © 2025 FABRIC Testbed
Acknowledgements
- Ceph Dashboard REST API (for authentication and cluster/user/cephfs management).
- Thanks to contributors for testing multi-cluster synchronization and API shape validation.
Code gen
Swagger API is maintained here: https://app.swaggerhub.com/apis/RENCI3/ceph/1.0.0
Generate the code using the following commands
this should place latest code in openapi_server and old code in openapi_server_archive
Merge as needed
cd fabric_ceph
openapi-generator validate -i openapi.yml
openapi-generator generate -i openapi.yml -g python-flask -o python-flask-server-generated
./swagger_code_gen.sh
Metadata
Release files for fabric_ceph 1.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| fabric_ceph-1.0.0.tar.gz | 69.5 kB | Without provenance | Details for fabric_ceph-1.0.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| fabric_ceph-1.0.0-py3-none-any.whl | Python 3 | none | any | Details for fabric_ceph-1.0.0-py3-none-any.whl |
Total release size: 165.6 kB
Release files / fabric_ceph-1.0.0.tar.gz
| Download URL | fabric_ceph-1.0.0.tar.gz |
|---|---|
| Size | 69.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
5a16e7dfe6e83eabf5db2d7caa3763e18c28e199bd1fcb24c08c9bcea179dbe0
|
|
BLAKE2b-256 checksum How to use checksums |
83ae6055c5fa82aaf0d1c9241c38d61f2781ccaa90f1b3f29cc765f95d2af67d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
python-requests/2.32.5
|
Release files / fabric_ceph-1.0.0-py3-none-any.whl
| Download URL | fabric_ceph-1.0.0-py3-none-any.whl |
|---|---|
| Size | 96.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
c737976d1caf358c89f7d7821e2059bff3d1a280477eb4a576bff363cd275fca
|
|
BLAKE2b-256 checksum How to use checksums |
8394ea0b00198cc4c1afb6cee8aa63d8f28f411dc9b3edf088def7f7a1634f71
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
python-requests/2.32.5
|
Security for fabric_ceph 1.0.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.