Skip to content

Commit 31aae74

Browse files
Merge pull request #15 from CHTC/quota-usage-reporting
(INF-2680) Ceph Quota Reporting script: Selective cephfs mounts
2 parents b966798 + c14722b commit 31aae74

File tree

2 files changed

+34
-27
lines changed

2 files changed

+34
-27
lines changed

ceph-quota-usage/README.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -72,20 +72,22 @@ top_level
7272

7373
And call the script from the command line as such:
7474

75-
`/path/to/cephfs_quota_usage.py -c HTC:HTC-user:htc-cephfs HPC:HPC-readonly:hpc-cephfs -d HTC:/foo/bar/baz/ HPC:/foo/bar/baz/ HPC:/fizz/buzz/`
75+
`/path/to/cephfs_quota_usage.py -c HTC:HTC-user:htc-cephfs:/ HPC:HPC-readonly:hpc-cephfs:/ -d HTC:/foo/bar/baz/ HPC:/foo/bar/baz/ HPC:/fizz/buzz/`
7676

7777
This would have the script report on the `/foo/bar/baz/` directory of the `HTC` cluster and the `/foo/bar/baz/` and `/fizz/buzz/` directories of the `HPC` cluster, with default values for output-file-pattern, sender and receivers.
7878

79+
This example command will mount the entire root directories of both cephfs clusters, but the `/` in both cluster definitions can be substituted for comma-separated lists of paths for the scipt to mount instead. As an example defining the clusters `... -c HTC:HTC-user:htc-cephfs:/foo/bar HPC:HPC-readonly:hpc-cephfs:/foo,/baz ...` will cause the scipt to mount the `/foo/bar` directory of the HTC cluster and the `/foo` and `/baz` directories of the HPC cluster.
80+
7981

8082
## Usage
8183
To set which clusters and directories the script will report on, which email addresses will receive the reports (with what reply addresses), and what filenames will be used for the reports, several command line options are available.
8284

8385
- "-c", "--clusters":
84-
After specifying this option, space-delimited list the colon-split pairs of cluster reference, client name, and filesystem name to be used to access that cluster.
86+
After specifying this option, space-delimited list the colon-split pairs of cluster reference, client name, filesystem name and directory list to be used to access that cluster.
8587

8688
Example usages:
87-
"... -c <name-of-directory-with-cluster-information>:<name-of-client>:<name-of-filesystem>"
88-
"... -c <cluster1_identifier>:readonlyuser:cephfs <cluster2_identifier>:clusterclient:storagefs"
89+
"... -c <name-of-directory-with-cluster-information>:<name-of-client>:<name-of-filesystem>:<comma-seperated-list-of-dirs-to-mount>"
90+
"... -c <cluster1_identifier>:readonlyuser:cephfs:/bar,/baz,/buzz <cluster2_identifier>:clusterclient:storagefs:/foo,/bar"
8991

9092
- "-d", "--directories":
9193
After specifying this option, space-delimited list the colon-split pairs of cluster reference and absolute path to a directory on that cluster to include in the report for that cluster (sub-directories of directories specified with this option are also automatically included for reporting).

ceph-quota-usage/cephfs_quota_usage.py

Lines changed: 28 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
DEFAULT_REPORT_PATTERN = "Quota_Usage_Report"
3131
DEFAULT_SENDER_ADDRESS = "[email protected]"
3232
DEFAULT_RECEIVER_ADDRESSES = ["[email protected]"]
33-
DEFAULT_CLUSTERS = ["HTC:INF-896:htc-cephfs", "HPC:quotareport:cephfs"]
33+
DEFAULT_CLUSTERS = ["HTC:INF-896:htc-cephfs:/staging,/projects", "HPC:quotareport:cephfs:/"]
3434

3535

3636
class Options:
@@ -42,6 +42,7 @@ class Options:
4242
receivers = None
4343
cluster_clients = None
4444
filesystem_names = None
45+
cluster_mount_paths = None
4546
sort_by = "bytes_used"
4647
sort_reverse = True
4748

@@ -82,12 +83,16 @@ def parse_args(args):
8283
cluster_clients = dict()
8384
# Create Cluster-Identifier to Filesystem-Name dictionary
8485
filesystem_names = dict()
86+
# Create Cluster-Identifier to Mount-Paths dictionary
87+
cluster_mount_paths = dict()
8588
for cluster in parsed_args.clusters:
8689
cluster_client_split = str(cluster).split(":")
8790
cluster_clients[cluster_client_split[0]] = cluster_client_split[1]
8891
filesystem_names[cluster_client_split[0]] = cluster_client_split[2]
92+
cluster_mount_paths[cluster_client_split[0]] = cluster_client_split[3].split(",")
8993
options.cluster_clients = cluster_clients
9094
options.filesystem_names = filesystem_names
95+
options.cluster_mount_paths = cluster_mount_paths
9196

9297

9398
# TODO: better class name
@@ -98,7 +103,7 @@ class CephFS_Wrapper:
98103
cluster = None
99104
fs = None
100105

101-
def __init__(self, cluster_identifier, client_name, filesytem_name):
106+
def __init__(self, cluster_identifier, client_name, filesytem_name, mount_path):
102107
cluster = rados.Rados(
103108
name=f"client.{client_name}",
104109
clustername="ceph",
@@ -108,7 +113,7 @@ def __init__(self, cluster_identifier, client_name, filesytem_name):
108113
self.cluster = cluster
109114
self.cluster.connect()
110115
fs = cephfs.LibCephFS(rados_inst=self.cluster)
111-
fs.mount(b"/", bytes(filesytem_name.encode()))
116+
fs.mount(bytes(mount_path.encode()), bytes(filesytem_name.encode()))
112117
self.fs = fs
113118

114119
def __del__(self):
@@ -163,7 +168,7 @@ def get_report_entry(self, path):
163168
bytes_entry = self.get_quota_usage_entry(path, "bytes", "ceph.quota.max_bytes", "ceph.dir.rbytes")
164169
files_entry = self.get_quota_usage_entry(path, "files", "ceph.quota.max_files", "ceph.dir.rfiles")
165170
rctime = round(float(self.get_xattr(path, "ceph.dir.rctime")))
166-
dir_backing_pool = self.get_xattr(path, "ceph.dir.layout.pool")
171+
dir_backing_pool = json.loads(self.get_xattr(path, "ceph.dir.layout.json"))["pool_name"]
167172

168173
# Gibibyte conversion for byte quota and usage
169174
if bytes_entry:
@@ -175,20 +180,11 @@ def get_report_entry(self, path):
175180
if rctime and not rctime is "":
176181
last_modified_date = datetime.datetime.utcfromtimestamp(rctime).strftime("%Y-%m-%d")
177182

178-
backing_pool = None
179-
if not dir_backing_pool is "":
180-
backing_pool = dir_backing_pool
181-
else:
182-
for parent in Path(path).parents:
183-
if not self.get_xattr(str(parent), "ceph.dir.layout.pool") is "":
184-
backing_pool = self.get_xattr(str(parent), "ceph.dir.layout.pool")
185-
break
186-
187-
if not None in (bytes_entry, files_entry, last_modified_date, backing_pool):
183+
if not None in (bytes_entry, files_entry, last_modified_date):
188184
row.update(bytes_entry)
189185
row.update(files_entry)
190186
row["last_modified_date"] = last_modified_date
191-
row["backing_pool"] = backing_pool
187+
row["backing_pool"] = dir_backing_pool
192188
return row
193189
else:
194190
return None
@@ -246,15 +242,21 @@ def write_to_file(filename, header, rows):
246242
writer.writerow(row.values())
247243

248244

249-
def get_quota_rows(cluster_fs, cluster_name):
245+
def get_quota_rows(cluster_fs, cluster_name, mount_path):
250246
rows = []
251247
toplevel_quota_usages = []
252248
subdir_quota_usages = []
253249
for path in options.report_dirs[cluster_name]:
254-
toplevel_entry = cluster_fs.get_report_entry(path)
255-
if toplevel_entry:
256-
toplevel_quota_usages.append(toplevel_entry)
257-
subdir_quota_usages.extend(cluster_fs.get_report_entries_dir(path))
250+
if os.path.commonpath([mount_path, path]) == mount_path:
251+
mount_relative_path = os.path.relpath(path, mount_path)
252+
toplevel_entry = cluster_fs.get_report_entry(mount_relative_path)
253+
if toplevel_entry:
254+
toplevel_entry["path"] = path
255+
toplevel_quota_usages.append(toplevel_entry)
256+
dir_entries = cluster_fs.get_report_entries_dir(mount_relative_path)
257+
for entry in dir_entries:
258+
entry["path"] = os.path.normpath(os.path.join(mount_path, entry["path"]))
259+
subdir_quota_usages.extend(dir_entries)
258260

259261
rows.extend(toplevel_quota_usages)
260262

@@ -284,8 +286,6 @@ def create_filename(cluster, pattern):
284286

285287

286288
def create_report_files_for_cluster(cluster):
287-
cluster_fs = CephFS_Wrapper(cluster, options.cluster_clients[cluster], options.filesystem_names[cluster])
288-
289289
quotas_header = (
290290
"Path",
291291
"Byte Quota (Gibibytes)",
@@ -297,7 +297,12 @@ def create_report_files_for_cluster(cluster):
297297
"Last Modified",
298298
"Backing Pool",
299299
)
300-
quota_rows = get_quota_rows(cluster_fs, cluster)
300+
301+
quota_rows = []
302+
for mount_path in options.cluster_mount_paths[cluster]:
303+
cluster_fs = CephFS_Wrapper(cluster, options.cluster_clients[cluster], options.filesystem_names[cluster], mount_path)
304+
quota_rows.extend(get_quota_rows(cluster_fs, cluster, mount_path))
305+
301306
quota_filename = create_filename(cluster, options.report_file_pattern)
302307
write_to_file(quota_filename, quotas_header, quota_rows)
303308

0 commit comments

Comments
 (0)