mirror of
https://github.com/45Drives/cockpit-navigator.git
synced 2025-07-31 01:24:37 +02:00
Merge branch 'dev-sam' into merge-cephfs-stats
This commit is contained in:
commit
408de97a6c
2
makefile
2
makefile
@ -23,7 +23,7 @@ install:
|
|||||||
cp -rpf navigator $(DESTDIR)/usr/share/cockpit
|
cp -rpf navigator $(DESTDIR)/usr/share/cockpit
|
||||||
|
|
||||||
uninstall:
|
uninstall:
|
||||||
rm -rf $(DESTDIR)/usr/share/cockpit/samba-manager
|
rm -rf $(DESTDIR)/usr/share/cockpit/navigator
|
||||||
|
|
||||||
install-local:
|
install-local:
|
||||||
mkdir -p $(HOME)/.local/share/cockpit
|
mkdir -p $(HOME)/.local/share/cockpit
|
||||||
|
@ -360,7 +360,7 @@ class NavDir extends NavEntry {
|
|||||||
async cephfs_dir_stats() {
|
async cephfs_dir_stats() {
|
||||||
try {
|
try {
|
||||||
var proc = await cockpit.spawn(
|
var proc = await cockpit.spawn(
|
||||||
["cephfs-dir-stats", "-j", this.path_str()],
|
["/usr/share/cockpit/navigator/scripts/cephfs-dir-stats.py", "-j", this.path_str()],
|
||||||
{err: "ignore"}
|
{err: "ignore"}
|
||||||
);
|
);
|
||||||
return JSON.parse(proc)[0];
|
return JSON.parse(proc)[0];
|
||||||
|
139
navigator/scripts/cephfs-dir-stats.py
Executable file
139
navigator/scripts/cephfs-dir-stats.py
Executable file
@ -0,0 +1,139 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import re
|
||||||
|
import subprocess
|
||||||
|
import math
|
||||||
|
import json
|
||||||
|
from optparse import OptionParser
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Name: dir_attributes
|
||||||
|
# Args: path to directory, command to run
|
||||||
|
# Desc: executes getfattr to retrive ceph dir attribute and returns as dictionary
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
|
||||||
|
def dir_attributes(path, type, command):
|
||||||
|
attrs = {}
|
||||||
|
try:
|
||||||
|
child = subprocess.Popen(
|
||||||
|
["getfattr", "-n", "ceph." + type + "." + command, path], stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
|
||||||
|
output_string, err = child.communicate()
|
||||||
|
except OSError:
|
||||||
|
print("Error executing getfattr. Is xattr installed?")
|
||||||
|
sys.exit(1)
|
||||||
|
if child.wait() != 0:
|
||||||
|
if err.find("No such attribute") != -1:
|
||||||
|
return {}
|
||||||
|
else:
|
||||||
|
print("Error executing getfattr. Is xattr installed?")
|
||||||
|
sys.exit(1)
|
||||||
|
# return {}
|
||||||
|
fields = re.findall(
|
||||||
|
r"^ceph\.[dir|quota]+\.([^=]+)=\"([^\"]+)\"$", output_string, re.MULTILINE)
|
||||||
|
if len(fields) == 0:
|
||||||
|
print(f'No ceph xattrs, is {path} in a ceph filesystem?')
|
||||||
|
sys.exit(1)
|
||||||
|
attrs[fields[0][0]] = fields[0][1]
|
||||||
|
return attrs
|
||||||
|
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Name: run_dir_commands
|
||||||
|
# Args: path to directory
|
||||||
|
# Desc: calls dir_attributes and quota_attrivutes multiple times each with a different command
|
||||||
|
# Retu: Returns the outputs of all commands in a dictionary
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
|
||||||
|
def run_dir_commands(path):
|
||||||
|
outputs = {'path':path}
|
||||||
|
dirList = ["entries", "files", "rbytes", "rentries",
|
||||||
|
"rfiles", "rsubdirs", "subdirs", "layout.pool"]
|
||||||
|
quotaList = ["max_files", "max_bytes"]
|
||||||
|
for items in dirList:
|
||||||
|
outputs.update(dir_attributes(path, "dir", items))
|
||||||
|
for items in quotaList:
|
||||||
|
outputs.update(dir_attributes(path, "quota", items))
|
||||||
|
|
||||||
|
if "rbytes" in outputs.keys():
|
||||||
|
outputs["rbytes"] = format_bytes(int(outputs["rbytes"]))
|
||||||
|
if "max_bytes" in outputs.keys():
|
||||||
|
outputs["max_bytes"] = format_bytes(int(outputs["max_bytes"]))
|
||||||
|
|
||||||
|
return outputs
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Name: display_attributes
|
||||||
|
# Args: path to directory
|
||||||
|
# Desc: calls run_dir_commands and prints output
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
|
||||||
|
def display_attributes(path):
|
||||||
|
attrs = run_dir_commands(path)
|
||||||
|
|
||||||
|
print(path, ":")
|
||||||
|
max_width = len(
|
||||||
|
max(attrs.values(), key=lambda x: len(x.split(" ")[0])).split(" ")[0])
|
||||||
|
print("Files: ", "{0:>{1}}".format(
|
||||||
|
attrs["files"], max_width) if "files" in attrs.keys() else "N/A")
|
||||||
|
print("Directories: ", "{0:>{1}}".format(
|
||||||
|
attrs["subdirs"], max_width) if "subdirs" in attrs.keys() else "N/A")
|
||||||
|
print("Recursive Files: ", "{0:>{1}}".format(
|
||||||
|
attrs["rfiles"], max_width) if "rfiles" in attrs.keys() else "N/A")
|
||||||
|
print("Recursive Directories: ", "{0:>{1}}".format(
|
||||||
|
attrs["rsubdirs"], max_width) if "rsubdirs" in attrs.keys() else "N/A")
|
||||||
|
print("Total Size: ", "{0:>{1}}".format(attrs["rbytes"].split(" ")[
|
||||||
|
0], max_width) + " " + attrs["rbytes"].split(" ")[1] if "rbytes" in attrs.keys() else "N/A")
|
||||||
|
print("Layout Pool: ", "{0:>{1}}".format(
|
||||||
|
attrs["layout.pool"], max_width) if "layout.pool" in attrs.keys() else "N/A")
|
||||||
|
print("Max Files: ", "{0:>{1}}".format(
|
||||||
|
attrs["max_files"], max_width) if "max_files" in attrs.keys() else " N/A")
|
||||||
|
print("Max Bytes: ", "{0:>{1}}".format(
|
||||||
|
attrs["max_bytes"], max_width) if "max_bytes" in attrs.keys() else " N/A")
|
||||||
|
print()
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# Name: format_bytes
|
||||||
|
# Args: integer value in bytes
|
||||||
|
# Desc: formats size_bytes in SI base units and returns as string
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
|
||||||
|
def format_bytes(size_bytes):
|
||||||
|
if size_bytes == 0:
|
||||||
|
return "0 B"
|
||||||
|
size_name = ("B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB")
|
||||||
|
i = int(math.floor(math.log(size_bytes, 1024)))
|
||||||
|
p = math.pow(1024, i)
|
||||||
|
s = round(size_bytes / p, 2)
|
||||||
|
return "%s %s" % (s, size_name[i])
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Name: main (cephfs-dir-stats)
|
||||||
|
# Args: (see parser)
|
||||||
|
# Desc: lists recursive ceph stats of specified directory
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = OptionParser()
|
||||||
|
parser.add_option("-j", "--json", help="output stats in JSON format.", action="store_true", dest="json", default=False)
|
||||||
|
(options, args) = parser.parse_args()
|
||||||
|
if len(args) == 0:
|
||||||
|
args = ["."]
|
||||||
|
if(options.json):
|
||||||
|
obj = []
|
||||||
|
for arg in args:
|
||||||
|
obj.append(run_dir_commands(arg))
|
||||||
|
obj = json.dumps(obj)
|
||||||
|
print(obj)
|
||||||
|
else:
|
||||||
|
for arg in args:
|
||||||
|
display_attributes(arg)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
Loading…
x
Reference in New Issue
Block a user