From 6ecaa3ee4fe7439c47bc02b1d23a3499706a3ba0 Mon Sep 17 00:00:00 2001 From: David Sterba Date: Wed, 7 Dec 2022 18:20:50 +0100 Subject: [PATCH] btrfs-progs: qgroup show: add json output Support json for 'qgroup show' with values printed by "btrfs qgroup -pcre", the accounted size and the limits. It's implemented as a separate call and not sharing the printing routines so any visible changes need to by synchronized. Formatter updates: don't print key name if .out_json is NULL. Example output: # btrfs --format json qgroup show /mnt/path { "__header": { "version": "1" }, "qgroup-show": [ { "qgroupid": "0/5", "referenced": "8831393792", "max_referenced": "none", "exclusive": "8224075776", "max_exclusive": "none", "path": "", "parents": [ ], "children": [ ] }, { "qgroupid": "0/361", "referenced": "611459072", "max_referenced": "none", "exclusive": "65536", "max_exclusive": "none", "path": "subv1", "parents": [ "1/1" ], "children": [ ] }, { "qgroupid": "0/362", "referenced": "611459072", "max_referenced": "none", "exclusive": "65536", "max_exclusive": "none", "path": "snap1-r", "parents": [ ], "children": [ ] }, { "qgroupid": "1/1", "referenced": "611459072", "max_referenced": "none", "exclusive": "65536", "max_exclusive": "none", "path": "", "parents": [ ], "children": [ "0/361" ] } ] } Issue: #555 Signed-off-by: David Sterba --- cmds/qgroup.c | 84 +++++++++++++++++++++++++++++++++++++++++- common/format-output.c | 3 +- 2 files changed, 84 insertions(+), 3 deletions(-) diff --git a/cmds/qgroup.c b/cmds/qgroup.c index 94721e2d..9fe692a2 100644 --- a/cmds/qgroup.c +++ b/cmds/qgroup.c @@ -35,6 +35,7 @@ #include "common/help.h" #include "common/units.h" #include "common/parse-utils.h" +#include "common/format-output.h" #include "common/messages.h" #include "cmds/commands.h" #include "cmds/qgroup.h" @@ -1433,6 +1434,80 @@ static void print_all_qgroups(struct qgroup_lookup *qgroup_lookup) } } +static const struct rowspec qgroup_show_rowspec[] = { + { .key = "qgroupid", .fmt = "qgroupid", .out_json = "qgroupid" }, + { .key = "referenced", .fmt = "%llu", .out_json = "referenced" }, + { .key = "exclusive", .fmt = "%llu", .out_json = "exclusive" }, + { .key = "max_referenced", .fmt = "size", .out_json = "max_referenced" }, + /* Special value if limits not set. */ + { .key = "max_referenced-none", .fmt = "%s", .out_json = "max_referenced" }, + { .key = "max_exclusive", .fmt = "size", .out_json = "max_exclusive" }, + /* Special value if limits not set. */ + { .key = "max_exclusive-none", .fmt = "%s", .out_json = "max_exclusive" }, + { .key = "path", .fmt = "%s", .out_json = "path" }, + { .key = "parents", .fmt = "list", .out_json = "parents" }, + { .key = "children", .fmt = "list", .out_json = "children" }, + /* Workaround for printing qgroupid in the list as a plain value */ + { .key = "qgroupid-list", .fmt = "qgroupid", .out_json = NULL }, + ROWSPEC_END +}; + +static void print_all_qgroups_json(struct qgroup_lookup *qgroup_lookup) +{ + struct rb_node *n; + struct btrfs_qgroup *qgroup; + struct format_ctx fctx; + + fmt_start(&fctx, qgroup_show_rowspec, 24, 0); + fmt_print_start_group(&fctx, "qgroup-show", JSON_TYPE_ARRAY); + + n = rb_first(&qgroup_lookup->root); + while (n) { + struct btrfs_qgroup_list *list = NULL; + + qgroup = rb_entry(n, struct btrfs_qgroup, sort_node); + + fmt_print_start_group(&fctx, NULL, JSON_TYPE_MAP); + + fmt_print(&fctx, "qgroupid", + btrfs_qgroup_level(qgroup->qgroupid), + btrfs_qgroup_subvolid(qgroup->qgroupid)); + fmt_print(&fctx, "referenced", qgroup->info.rfer); + if (qgroup->limit.flags & BTRFS_QGROUP_LIMIT_MAX_RFER) + fmt_print(&fctx, "max_referenced", qgroup->limit.max_rfer); + else + fmt_print(&fctx, "max_referenced-none", "none"); + fmt_print(&fctx, "exclusive", qgroup->info.excl); + if (qgroup->limit.flags & BTRFS_QGROUP_LIMIT_MAX_EXCL) + fmt_print(&fctx, "max_exclusive", qgroup->limit.max_excl); + else + fmt_print(&fctx, "max_exclusive-none", "none"); + fmt_print(&fctx, "path", qgroup->path ?: ""); + + fmt_print_start_group(&fctx, "parents", JSON_TYPE_ARRAY); + list_for_each_entry(list, &qgroup->qgroups, next_qgroup) { + fmt_print(&fctx, "qgroupid-list", + btrfs_qgroup_level(list->qgroup->qgroupid), + btrfs_qgroup_subvolid(list->qgroup->qgroupid)); + } + fmt_print_end_group(&fctx, "parents"); + + fmt_print_start_group(&fctx, "children", JSON_TYPE_ARRAY); + list_for_each_entry(list, &qgroup->members, next_member) { + fmt_print(&fctx, "qgroupid-list", + btrfs_qgroup_level(list->member->qgroupid), + btrfs_qgroup_subvolid(list->member->qgroupid)); + } + fmt_print_end_group(&fctx, "children"); + + fmt_print_end_group(&fctx, NULL); + + n = rb_next(n); + } + fmt_print_end_group(&fctx, "qgroup-show"); + fmt_end(&fctx); +} + static int show_qgroups(int fd, struct btrfs_qgroup_filter_set *filter_set, struct btrfs_qgroup_comparer_set *comp_set) @@ -1447,7 +1522,10 @@ static int show_qgroups(int fd, return ret; __filter_and_sort_qgroups(&qgroup_lookup, &sort_tree, filter_set, comp_set); - print_all_qgroups(&sort_tree); + if (bconf.output_format == CMD_FORMAT_JSON) + print_all_qgroups_json(&sort_tree); + else + print_all_qgroups(&sort_tree); __free_all_qgroups(&qgroup_lookup); return ret; @@ -1855,6 +1933,8 @@ static const char * const cmd_qgroup_show_usage[] = { " you can use '+' or '-' in front of each item.", " (+:ascending, -:descending, ascending default)", "--sync force sync of the filesystem before getting info", + HELPINFO_INSERT_GLOBALS, + HELPINFO_INSERT_FORMAT, NULL }; @@ -1974,7 +2054,7 @@ static int cmd_qgroup_show(const struct cmd_struct *cmd, int argc, char **argv) out: return !!ret; } -static DEFINE_SIMPLE_COMMAND(qgroup_show, "show"); +static DEFINE_COMMAND_WITH_FLAGS(qgroup_show, "show", CMD_FORMAT_JSON); static const char * const cmd_qgroup_limit_usage[] = { "btrfs qgroup limit [options] |none [] ", diff --git a/common/format-output.c b/common/format-output.c index c194a23c..c6ab3bf4 100644 --- a/common/format-output.c +++ b/common/format-output.c @@ -272,7 +272,8 @@ void fmt_print(struct format_ctx *fctx, const char* key, ...) } else { /* Simple key/values */ fmt_separator(fctx); - printf("\"%s\": ", row->out_json); + if (row->out_json) + printf("\"%s\": ", row->out_json); } }