Merge pull request #68257 from avanthakkar/add-cluster-qos-cmd

mgr/smb: add cluster-level QoS update command for CephFS shares

Reviewed-by:  Anoop C S <anoopcs@cryptolab.net>
Reviewed-by: Shwetha Acharya <sacharya@redhat.com>
Reviewed-by: John Mulligan <jmulligan@redhat.com>
This commit is contained in:
John Mulligan 2026-05-06 15:15:39 -04:00 committed by GitHub
commit 8bb89be902
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 233 additions and 7 deletions

View File

@ -166,6 +166,56 @@ previous example. Set three CTDB public address values and a custom placement:
--placement="3 label:smb"
Update Cluster QoS
++++++++++++++++++
.. prompt:: bash #
ceph smb cluster update cephfs qos <cluster_id> [--read-iops-limit=<int>] [--write-iops-limit=<int>] [--read-bw-limit=<str>] [--write-bw-limit=<str>] [--read-burst-mult=<int>] [--write-burst-mult=<int>]
Update Quality of Service (QoS) settings for all CephFS-backed shares within a cluster. This command applies the same per-share QoS limits to every share in the specified cluster that has CephFS storage configured. This is particularly useful for clusters with many shares that require uniform QoS policies.
Options: See :ref:`qos-parameters` for detailed descriptions of all QoS parameters.
Examples:
Apply the same IOPS limits to all shares in a cluster:
.. prompt:: bash #
ceph smb cluster update cephfs qos prod \
--read-iops-limit=1000 \
--write-iops-limit=500
Apply bandwidth limits with human-readable units to all shares:
.. prompt:: bash #
ceph smb cluster update cephfs qos prod \
--read-bw-limit="10M" \
--write-bw-limit="5M"
Apply QoS limits with burst multipliers to all shares:
.. prompt:: bash #
ceph smb cluster update cephfs qos prod \
--read-iops-limit=100 \
--write-iops-limit=200 \
--read-burst-mult=20 \
--write-burst-mult=15
Disable QoS for all shares in a cluster:
.. prompt:: bash #
ceph smb cluster update cephfs qos prod \
--read-iops-limit=0 \
--write-iops-limit=0 \
--read-bw-limit=0 \
--write-bw-limit=0
Remove Cluster
++++++++++++++
@ -262,16 +312,14 @@ Create a read-only share at a custom path in the CephFS volume:
ceph smb share create test1 plans cephfs \
--path=/qbranch/top/secret/plans --readonly
Update Share QoS
++++++++++++++++
.. prompt:: bash #
.. _qos-parameters:
ceph smb share update cephfs qos <cluster_id> <share_id> [--read-iops-limit=<int>] [--write-iops-limit=<int>] [--read-bw-limit=<str>] [--write-bw-limit=<str>] [--read-burst-mult=<int>] [--write-burst-mult=<int>]
QoS Parameters
++++++++++++++
Update Quality of Service (QoS) settings for a CephFS-backed share. This allows administrators to apply per-share rate limits on SMB input/output (I/O) operations, specifically limits on IOPS (Input/Output Operations per Second) and bandwidth (in bytes per second) for both read and write operations. Additionally, burst multipliers can be configured to allow temporary bursts above the configured limits.
Options:
The following Quality of Service (QoS) parameters are available for CephFS-backed shares.
All parameters are optional and can be used independently.
read_iops_limit
Optional integer. Maximum number of read operations per second (0 = disabled).
@ -326,6 +374,18 @@ larger bursts but may temporarily consume more resources.
The burst multiplier only affects short-term spikes. The long-term average
throughput remains limited by your configured IOPS and bandwidth limits.
Update Share QoS
++++++++++++++++
.. prompt:: bash #
ceph smb share update cephfs qos <cluster_id> <share_id> [--read-iops-limit=<int>] [--write-iops-limit=<int>] [--read-bw-limit=<str>] [--write-bw-limit=<str>] [--read-burst-mult=<int>] [--write-burst-mult=<int>]
Update Quality of Service (QoS) settings for a CephFS-backed share. This allows administrators to apply per-share rate limits on SMB input/output (I/O) operations, specifically limits on IOPS (Input/Output Operations per Second) and bandwidth (in bytes per second) for both read and write operations. Additionally, burst multipliers can be configured to allow temporary bursts above the configured limits.
Options: See :ref:`qos-parameters` for detailed descriptions of all QoS parameters.
Examples:
Set QoS limits with burst multipliers for a share:

View File

@ -345,6 +345,98 @@ class Module(orchestrator.OrchestratorClientMixin, MgrModule):
[cluster], password_filter_out=password_filter
).one()
@SMBCLICommand('cluster update cephfs qos', perm='rw')
def cluster_update_qos(
self,
cluster_id: str,
read_iops_limit: Optional[int] = None,
write_iops_limit: Optional[int] = None,
read_bw_limit: Optional[str] = None,
write_bw_limit: Optional[str] = None,
read_burst_mult: Optional[int] = None,
write_burst_mult: Optional[int] = None,
) -> Simplified:
"""Update QoS settings for all CephFS shares in a cluster"""
try:
shares = self._handler.matching_resources(
[f'ceph.smb.share.{cluster_id}']
)
active_shares = [
s for s in shares if isinstance(s, resources.Share)
]
if not active_shares:
raise ValueError(f"No shares found for cluster {cluster_id}")
shares_to_update: List[resources.SMBResource] = []
unchanged_shares = []
for share in active_shares:
if not share.cephfs:
unchanged_shares.append(share.share_id)
continue
try:
updated_cephfs = share.cephfs.update_qos(
read_iops_limit=read_iops_limit,
write_iops_limit=write_iops_limit,
read_bw_limit=read_bw_limit,
write_bw_limit=write_bw_limit,
read_burst_mult=read_burst_mult,
write_burst_mult=write_burst_mult,
)
if updated_cephfs != share.cephfs:
updated_share = replace(share, cephfs=updated_cephfs)
shares_to_update.append(updated_share)
else:
unchanged_shares.append(share.share_id)
except ValueError as e:
raise ValueError(
f"Error updating share {share.share_id}: {str(e)}"
)
if not shares_to_update:
return {
"cluster_id": cluster_id,
"message": "No shares required QoS updates",
"unchanged_shares": unchanged_shares,
"total_shares": len(active_shares),
}
result_group = self._apply_res(shares_to_update)
successful_updates = []
failed_updates = []
for result in result_group:
if result.success and hasattr(result.src, 'share_id'):
successful_updates.append(result.src.share_id)
elif hasattr(result.src, 'share_id'):
failed_updates.append(
{"share_id": result.src.share_id, "error": result.msg}
)
return {
"cluster_id": cluster_id,
"successful_updates": successful_updates,
"failed_updates": failed_updates,
"unchanged_shares": unchanged_shares,
"total_shares": len(active_shares),
"success": len(failed_updates) == 0,
}
except resources.InvalidResourceError as err:
return {
"success": False,
"error": str(err),
"resource": err.resource_data,
}
except Exception as e:
return {"success": False, "error": str(e)}
@SMBCLICommand('share ls', perm='r')
def share_ls(self, cluster_id: str) -> List[str]:
"""List smb shares in a cluster by ID"""

View File

@ -1030,6 +1030,80 @@ def test_cmd_share_update_qos(tmodule):
assert updated_share.cephfs.qos.write_burst_mult == 15 # Default
def test_cmd_cluster_update_qos(tmodule):
cluster = _cluster(
cluster_id='qoscluster',
auth_mode=smb.enums.AuthMode.USER,
user_group_settings=[
smb.resources.UserGroupSource(
source_type=smb.resources.UserGroupSourceType.EMPTY,
),
],
)
share1 = smb.resources.Share(
cluster_id='qoscluster',
share_id='share1',
name='Share One',
cephfs=smb.resources.CephFSStorage(
volume='cephfs',
path='/share1',
),
)
share2 = smb.resources.Share(
cluster_id='qoscluster',
share_id='share2',
name='Share Two',
cephfs=smb.resources.CephFSStorage(
volume='cephfs',
path='/share2',
),
)
share3 = smb.resources.Share(
cluster_id='qoscluster',
share_id='share3',
name='Share Three',
cephfs=smb.resources.CephFSStorage(
volume='cephfs',
path='/share3',
),
)
rg = tmodule._handler.apply([cluster, share1, share2, share3])
assert rg.success, rg.to_simplified()
res, body, status = tmodule.cluster_update_qos.command(
cluster_id='qoscluster',
read_iops_limit=100,
write_iops_limit=200,
read_bw_limit="1048576",
write_bw_limit="2097152",
read_burst_mult=20,
write_burst_mult=15,
)
assert res == 0
bdata = json.loads(body)
assert bdata['success']
assert bdata['cluster_id'] == 'qoscluster'
assert bdata['total_shares'] == 3
assert len(bdata['successful_updates']) == 3
assert len(bdata['failed_updates']) == 0
for share_id in ['share1', 'share2', 'share3']:
updated_shares = tmodule._handler.matching_resources(
[f'ceph.smb.share.qoscluster.{share_id}']
)
assert len(updated_shares) == 1
updated_share = updated_shares[0]
assert updated_share.cephfs.qos is not None
assert updated_share.cephfs.qos.read_iops_limit == 100
assert updated_share.cephfs.qos.write_iops_limit == 200
assert updated_share.cephfs.qos.read_bw_limit == "1048576"
assert updated_share.cephfs.qos.write_bw_limit == "2097152"
assert updated_share.cephfs.qos.read_burst_mult == 20
assert updated_share.cephfs.qos.write_burst_mult == 15
def _keybridge_example():
return [
{