summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Rajnoha <prajnoha@redhat.com>2016-05-02 14:21:40 +0200
committerPeter Rajnoha <prajnoha@redhat.com>2016-06-02 14:08:11 +0200
commite05a821c2a759e4f1c5e37275c13d179a599976f (patch)
treec19c2c17b9d300ed697629b46aff8085831d96a7
parentf63a0ddcb5516e68b3357138c00ef4dd24e2a2cf (diff)
downloadlvm2-e05a821c2a759e4f1c5e37275c13d179a599976f.tar.gz
libdm: report: implement DM_REPORT_GROUP_JSON for JSON report output
-rw-r--r--libdm/libdm-report.c243
1 files changed, 231 insertions, 12 deletions
diff --git a/libdm/libdm-report.c b/libdm/libdm-report.c
index 831b36c44..8b3738155 100644
--- a/libdm/libdm-report.c
+++ b/libdm/libdm-report.c
@@ -4203,6 +4203,16 @@ static int _sort_rows(struct dm_report *rh)
#define STANDARD_QUOTE "\'"
#define STANDARD_PAIR "="
+#define JSON_INDENT_UNIT 4
+#define JSON_SPACE " "
+#define JSON_QUOTE "\""
+#define JSON_PAIR ":"
+#define JSON_SEPARATOR ","
+#define JSON_OBJECT_START "{"
+#define JSON_OBJECT_END "}"
+#define JSON_ARRAY_START "["
+#define JSON_ARRAY_END "]"
+
#define UNABLE_TO_EXTEND_OUTPUT_LINE_MSG "dm_report: Unable to extend output line"
static int _is_native_report(struct dm_report *rh)
@@ -4211,6 +4221,12 @@ static int _is_native_report(struct dm_report *rh)
(rh->group_item->group->type == DM_REPORT_GROUP_EXTENDED);
}
+static int _is_json_report(struct dm_report *rh)
+{
+ return rh->group_item &&
+ (rh->group_item->group->type == DM_REPORT_GROUP_JSON);
+}
+
/*
* Produce report output
*/
@@ -4225,7 +4241,16 @@ static int _output_field(struct dm_report *rh, struct dm_report_field *field)
char *buf = NULL;
size_t buf_size = 0;
- if (rh->flags & DM_REPORT_OUTPUT_FIELD_NAME_PREFIX) {
+ if (_is_json_report(rh)) {
+ if (!dm_pool_grow_object(rh->mem, JSON_QUOTE, 1) ||
+ !dm_pool_grow_object(rh->mem, fields[field->props->field_num].id, 0) ||
+ !dm_pool_grow_object(rh->mem, JSON_QUOTE, 1) ||
+ !dm_pool_grow_object(rh->mem, JSON_PAIR, 1) ||
+ !dm_pool_grow_object(rh->mem, JSON_QUOTE, 1)) {
+ log_error("dm_report: Unable to extend output line");
+ return 0;
+ }
+ } else if (rh->flags & DM_REPORT_OUTPUT_FIELD_NAME_PREFIX) {
if (!(field_id = dm_strdup(fields[field->props->field_num].id))) {
log_error("dm_report: Failed to copy field name");
return 0;
@@ -4300,12 +4325,19 @@ static int _output_field(struct dm_report *rh, struct dm_report_field *field)
}
}
- if ((rh->flags & DM_REPORT_OUTPUT_FIELD_NAME_PREFIX) &&
- !(rh->flags & DM_REPORT_OUTPUT_FIELD_UNQUOTED))
- if (!dm_pool_grow_object(rh->mem, STANDARD_QUOTE, 1)) {
+ if (rh->flags & DM_REPORT_OUTPUT_FIELD_NAME_PREFIX) {
+ if (!(rh->flags & DM_REPORT_OUTPUT_FIELD_UNQUOTED)) {
+ if (!dm_pool_grow_object(rh->mem, STANDARD_QUOTE, 1)) {
+ log_error(UNABLE_TO_EXTEND_OUTPUT_LINE_MSG);
+ goto bad;
+ }
+ }
+ } else if (_is_json_report(rh)) {
+ if (!dm_pool_grow_object(rh->mem, JSON_QUOTE, 1)) {
log_error(UNABLE_TO_EXTEND_OUTPUT_LINE_MSG);
goto bad;
}
+ }
dm_free(buf);
return 1;
@@ -4407,39 +4439,78 @@ static int _output_as_columns(struct dm_report *rh)
struct dm_list *fh, *rowh, *ftmp, *rtmp;
struct row *row = NULL;
struct dm_report_field *field;
+ struct dm_list *last_row;
+ int do_field_delim;
+ char *line;
/* If headings not printed yet, calculate field widths and print them */
if (!(rh->flags & RH_HEADINGS_PRINTED))
_report_headings(rh);
/* Print and clear buffer */
+ last_row = dm_list_last(&rh->rows);
dm_list_iterate_safe(rowh, rtmp, &rh->rows) {
if (!dm_pool_begin_object(rh->mem, 512)) {
log_error("dm_report: Unable to allocate output line");
return 0;
}
+
+ if (_is_json_report(rh)) {
+ if (!dm_pool_grow_object(rh->mem, JSON_OBJECT_START, 0)) {
+ log_error(UNABLE_TO_EXTEND_OUTPUT_LINE_MSG);
+ goto bad;
+ }
+ }
+
row = dm_list_item(rowh, struct row);
+ do_field_delim = 0;
+
dm_list_iterate_safe(fh, ftmp, &row->fields) {
field = dm_list_item(fh, struct dm_report_field);
if (field->props->flags & FLD_HIDDEN)
continue;
+ if (do_field_delim) {
+ if (_is_json_report(rh)) {
+ if (!dm_pool_grow_object(rh->mem, JSON_SEPARATOR, 0) ||
+ !dm_pool_grow_object(rh->mem, JSON_SPACE, 0)) {
+ log_error(UNABLE_TO_EXTEND_OUTPUT_LINE_MSG);
+ goto bad;
+ }
+ } else {
+ if (!dm_pool_grow_object(rh->mem, rh->separator, 0)) {
+ log_error(UNABLE_TO_EXTEND_OUTPUT_LINE_MSG);
+ goto bad;
+ }
+ }
+ } else
+ do_field_delim = 1;
+
if (!_output_field(rh, field))
goto bad;
- if (!dm_list_end(&row->fields, fh))
- if (!dm_pool_grow_object(rh->mem, rh->separator, 0)) {
- log_error(UNABLE_TO_EXTEND_OUTPUT_LINE_MSG);
- goto bad;
- }
-
dm_list_del(&field->list);
}
+
+ if (_is_json_report(rh)) {
+ if (!dm_pool_grow_object(rh->mem, JSON_OBJECT_END, 0)) {
+ log_error(UNABLE_TO_EXTEND_OUTPUT_LINE_MSG);
+ goto bad;
+ }
+ if (rowh != last_row &&
+ !dm_pool_grow_object(rh->mem, JSON_SEPARATOR, 0)) {
+ log_error(UNABLE_TO_EXTEND_OUTPUT_LINE_MSG);
+ goto bad;
+ }
+ }
+
if (!dm_pool_grow_object(rh->mem, "\0", 1)) {
log_error("dm_report: Unable to terminate output line");
goto bad;
}
- log_print("%s", (char *) dm_pool_end_object(rh->mem));
+
+ line = (char *) dm_pool_end_object(rh->mem);
+ log_print("%*s", rh->group_item ? rh->group_item->group->indent + (int) strlen(line) : 0, line);
dm_list_del(&row->list);
}
@@ -4469,10 +4540,78 @@ static struct report_group_item *_get_topmost_report_group_item(struct dm_report
return item;
}
+static int _json_output_array_start(struct dm_pool *mem, struct report_group_item *item)
+{
+ const char *name = (const char *) item->data;
+ char *output;
+
+ if (!dm_pool_begin_object(mem, 32)) {
+ log_error(UNABLE_TO_EXTEND_OUTPUT_LINE_MSG);
+ return 0;
+ }
+
+ if (!dm_pool_grow_object(mem, JSON_QUOTE, 1) ||
+ !dm_pool_grow_object(mem, name, 0) ||
+ !dm_pool_grow_object(mem, JSON_QUOTE JSON_PAIR JSON_SPACE JSON_ARRAY_START, 0) ||
+ !dm_pool_grow_object(mem, "\0", 1) ||
+ !(output = dm_pool_end_object(mem))) {
+ log_error(UNABLE_TO_EXTEND_OUTPUT_LINE_MSG);
+ goto bad;
+ }
+
+ if (item->parent->store.finished_count > 0)
+ log_print("%*s", item->group->indent + (int) sizeof(JSON_SEPARATOR) - 1, JSON_SEPARATOR);
+
+ if (item->parent->parent && item->parent->data) {
+ log_print("%*s", item->group->indent + (int) sizeof(JSON_OBJECT_START) - 1, JSON_OBJECT_START);
+ item->group->indent += JSON_INDENT_UNIT;
+ }
+
+ log_print("%*s", item->group->indent + (int) strlen(output), output);
+ item->group->indent += JSON_INDENT_UNIT;
+
+ dm_pool_free(mem, output);
+ return 1;
+bad:
+ dm_pool_abandon_object(mem);
+ return 0;
+}
+
+static int _prepare_json_report_output(struct dm_report *rh)
+{
+ if (rh->group_item->output_done && dm_list_empty(&rh->rows))
+ return 1;
+
+ /*
+ * If this report is in JSON group, it must be at the
+ * top of the stack of reports so the output from
+ * different reports do not interleave with each other.
+ */
+ if (_get_topmost_report_group_item(rh->group_item->group) != rh->group_item) {
+ log_error("dm_report: dm_report_output: interleaved reports detected for JSON output");
+ return 0;
+ }
+
+ if (rh->group_item->needs_closing) {
+ log_error("dm_report: dm_report_output: unfinished JSON output detected");
+ return 0;
+ }
+
+ if (!_json_output_array_start(rh->mem, rh->group_item))
+ return_0;
+
+ rh->group_item->needs_closing = 1;
+ return 1;
+}
+
int dm_report_output(struct dm_report *rh)
{
int r;
+ if (_is_json_report(rh) &&
+ !_prepare_json_report_output(rh))
+ return_0;
+
if (dm_list_empty(&rh->rows)) {
r = 1;
goto out;
@@ -4483,7 +4622,7 @@ int dm_report_output(struct dm_report *rh)
if (_is_native_report(rh)) {
if (rh->group_item->parent->store.finished_count > 0)
- log_print("");
+ log_print("%s", "");
log_print("Report: %s", (const char *) rh->group_item->data);
}
@@ -4502,6 +4641,13 @@ static int _report_group_create_native(struct dm_report_group *group)
return 1;
}
+static int _report_group_create_json(struct dm_report_group *group)
+{
+ log_print(JSON_OBJECT_START);
+ group->indent += JSON_INDENT_UNIT;
+ return 1;
+}
+
struct dm_report_group *dm_report_group_create(dm_report_group_type_t type, void *data)
{
struct dm_report_group *group;
@@ -4534,6 +4680,10 @@ struct dm_report_group *dm_report_group_create(dm_report_group_type_t type, void
if (!_report_group_create_native(group))
goto_bad;
break;
+ case DM_REPORT_GROUP_JSON:
+ if (!_report_group_create_json(group))
+ goto_bad;
+ break;
default:
goto_bad;
}
@@ -4549,6 +4699,40 @@ static int _report_group_push_native(struct report_group_item *item, const char
return 1;
}
+static int _report_group_push_json(struct report_group_item *item, const char *name)
+{
+ if (name && !(item->data = dm_pool_strdup(item->group->mem, name))) {
+ log_error("dm_report: failed to duplicate json item name");
+ return 0;
+ }
+
+ if (item->report) {
+ item->report->flags &= ~(DM_REPORT_OUTPUT_ALIGNED |
+ DM_REPORT_OUTPUT_HEADINGS |
+ DM_REPORT_OUTPUT_COLUMNS_AS_ROWS);
+ item->report->flags |= DM_REPORT_OUTPUT_BUFFERED;
+ } else {
+ if (name) {
+ if (!_json_output_array_start(item->group->mem, item))
+ return_0;
+ } else {
+ if (!item->parent->parent) {
+ log_error("dm_report: can't use unnamed object at top level of JSON output");
+ return 0;
+ }
+ if (item->parent->store.finished_count > 0)
+ log_print("%*s", item->group->indent + (int) sizeof(JSON_SEPARATOR) - 1, JSON_SEPARATOR);
+ log_print("%*s", item->group->indent + (int) sizeof(JSON_OBJECT_START) - 1, JSON_OBJECT_START);
+ item->group->indent += JSON_INDENT_UNIT;
+ }
+
+ item->output_done = 1;
+ item->needs_closing = 1;
+ }
+
+ return 1;
+}
+
int dm_report_group_push(struct dm_report_group *group, struct dm_report *report, void *data)
{
struct report_group_item *item, *tmp_item;
@@ -4582,6 +4766,10 @@ int dm_report_group_push(struct dm_report_group *group, struct dm_report *report
if (!_report_group_push_native(item, data))
goto_bad;
break;
+ case DM_REPORT_GROUP_JSON:
+ if (!_report_group_push_json(item, data))
+ goto_bad;
+ break;
default:
goto_bad;
}
@@ -4598,6 +4786,23 @@ static int _report_group_pop_native(struct report_group_item *item)
return 1;
}
+static int _report_group_pop_json(struct report_group_item *item)
+{
+ if (item->output_done && item->needs_closing) {
+ if (item->data) {
+ item->group->indent -= JSON_INDENT_UNIT;
+ log_print("%*s", item->group->indent + (int) sizeof(JSON_ARRAY_END) - 1, JSON_ARRAY_END);
+ }
+ if (item->parent->data && item->parent->parent) {
+ item->group->indent -= JSON_INDENT_UNIT;
+ log_print("%*s", item->group->indent + (int) sizeof(JSON_OBJECT_END) - 1, JSON_OBJECT_END);
+ }
+ item->needs_closing = 0;
+ }
+
+ return 1;
+}
+
int dm_report_group_pop(struct dm_report_group *group)
{
struct report_group_item *item;
@@ -4615,6 +4820,10 @@ int dm_report_group_pop(struct dm_report_group *group)
if (!_report_group_pop_native(item))
return_0;
break;
+ case DM_REPORT_GROUP_JSON:
+ if (!_report_group_pop_json(item))
+ return_0;
+ break;
default:
return 0;
}
@@ -4638,6 +4847,12 @@ static int _report_group_destroy_native(void)
return 1;
}
+static int _report_group_destroy_json(void)
+{
+ log_print(JSON_OBJECT_END);
+ return 1;
+}
+
int dm_report_group_destroy(struct dm_report_group *group)
{
struct report_group_item *item, *tmp_item;
@@ -4658,6 +4873,10 @@ int dm_report_group_destroy(struct dm_report_group *group)
if (!_report_group_destroy_native())
goto_out;
break;
+ case DM_REPORT_GROUP_JSON:
+ if (!_report_group_destroy_json())
+ goto_out;
+ break;
default:
goto_out;
}