aboutsummaryrefslogtreecommitdiff
path: root/cli
diff options
context:
space:
mode:
authorMaxim Anisimov <maxim.anisimov.ua@gmail.com>2019-08-08 11:12:33 +0300
committerAleksander Morgado <aleksander@aleksander.es>2019-08-28 18:17:55 +0200
commitd1ac5ca16bfbc2475eb379b48e981e035f908b50 (patch)
treef94a5b4fb4fa6bcf88834c41ced191a6f9a994c9 /cli
parentb0ec3030a3e5519d75c2268fa20062edd2dacf4e (diff)
mmcli: add json output support
Signed-off-by: Maxim Anisimov <maxim.anisimov.ua@gmail.com>
Diffstat (limited to 'cli')
-rw-r--r--cli/mmcli-output.c119
-rw-r--r--cli/mmcli-output.h1
-rw-r--r--cli/mmcli.c16
3 files changed, 133 insertions, 3 deletions
diff --git a/cli/mmcli-output.c b/cli/mmcli-output.c
index 7699f25e..5b2af008 100644
--- a/cli/mmcli-output.c
+++ b/cli/mmcli-output.c
@@ -1072,6 +1072,119 @@ dump_output_list_keyvalue (MmcF field)
}
/******************************************************************************/
+/* Json-friendly output */
+
+static gint
+list_sort_by_keys (const OutputItem *item_a,
+ const OutputItem *item_b)
+{
+ return g_strcmp0(field_infos[item_a->field].key, field_infos[item_b->field].key);
+}
+
+static void
+dump_output_json (void)
+{
+ GList *l;
+ MmcF current_field = MMC_F_UNKNOWN;
+ gchar **current_path = NULL;
+ guint cur_dlen = 0;
+ GRegex *escape_regex;
+
+ output_items = g_list_sort (output_items, (GCompareFunc) list_sort_by_keys);
+ escape_regex = g_regex_new("'", G_REGEX_MULTILINE | G_REGEX_RAW, 0, NULL);
+
+ g_print("{");
+ for (l = output_items; l; l = g_list_next (l)) {
+ OutputItem *item_l = (OutputItem *)(l->data);
+
+ if (current_field != item_l->field) {
+ guint new_dlen;
+ guint iter = 0;
+ gchar **new_path;
+
+ new_path = g_strsplit(field_infos[item_l->field].key, ".", -1);
+ new_dlen = g_strv_length(new_path) - 1;
+ if (current_path) {
+ guint min_dlen = MIN (cur_dlen, new_dlen);
+ while(iter < min_dlen && g_strcmp0(current_path[iter], new_path[iter]) == 0)
+ iter++;
+
+ g_strfreev(current_path);
+
+ if (iter < min_dlen || new_dlen < cur_dlen)
+ for(min_dlen = iter; min_dlen < cur_dlen; min_dlen++)
+ g_print("}");
+
+ g_print(",");
+ }
+
+ while(iter < new_dlen)
+ g_print("\"%s\":{", new_path[iter++]);
+
+ cur_dlen = new_dlen;
+ current_path = new_path;
+ current_field = item_l->field;
+ } else {
+ g_print(",");
+ }
+ if (item_l->type == VALUE_TYPE_SINGLE) {
+ OutputItemSingle *single = (OutputItemSingle *) item_l;
+ gchar *escaped = NULL;
+
+ if (single->value)
+ escaped = g_regex_replace_literal(escape_regex, single->value, -1, 0, "\"", 0, NULL);
+
+ g_print ("\"%s\":\"%s\"", current_path[cur_dlen], escaped ? escaped : "--");
+ g_free (escaped);
+ } else if (item_l->type == VALUE_TYPE_MULTIPLE) {
+ OutputItemMultiple *multiple = (OutputItemMultiple *) item_l;
+ guint i, n = multiple->values ? g_strv_length (multiple->values) : 0;
+
+ g_print ("\"%s\":[", current_path[cur_dlen]);
+ for(i = 0; i < n; i++) {
+ gchar *escaped = g_regex_replace_literal(escape_regex, multiple->values[i], -1, 0, "\"", 0, NULL);
+ g_print("\"%s\"", escaped);
+ if (i < n - 1)
+ g_print(",");
+ g_free (escaped);
+ }
+ g_print("]");
+ } else {
+ g_assert_not_reached ();
+ }
+ }
+ while(cur_dlen--)
+ g_print("}");
+ g_print("}\n");
+ if (current_path)
+ g_strfreev(current_path);
+ g_regex_unref(escape_regex);
+}
+
+static void
+dump_output_list_json (MmcF field)
+{
+ GList *l;
+ g_assert (field != MMC_F_UNKNOWN);
+ g_print("{\"%s\":[", field_infos[field].key);
+ for (l = output_items; l; l = g_list_next (l)) {
+ OutputItem *item_l;
+ OutputItemListitem *listitem;
+ item_l = (OutputItem *)(l->data);
+ g_assert (item_l->type == VALUE_TYPE_LISTITEM);
+ listitem = (OutputItemListitem *)item_l;
+ g_assert (listitem->value);
+
+ /* All items must be of same type */
+ g_assert_cmpint (item_l->field, ==, field);
+ g_print("\"%s\"", listitem->value);
+ if (g_list_next(l))
+ g_print(",");
+ }
+ g_print("]}\n");
+}
+
+/******************************************************************************/
/* Dump output */
void
@@ -1086,6 +1199,9 @@ mmcli_output_dump (void)
case MMC_OUTPUT_TYPE_KEYVALUE:
dump_output_keyvalue ();
break;
+ case MMC_OUTPUT_TYPE_JSON:
+ dump_output_json ();
+ break;
}
g_list_free_full (output_items, (GDestroyNotify) output_item_free);
@@ -1106,6 +1222,9 @@ mmcli_output_list_dump (MmcF field)
case MMC_OUTPUT_TYPE_KEYVALUE:
dump_output_list_keyvalue (field);
break;
+ case MMC_OUTPUT_TYPE_JSON:
+ dump_output_list_json (field);
+ break;
}
g_list_free_full (output_items, (GDestroyNotify) output_item_free);
diff --git a/cli/mmcli-output.h b/cli/mmcli-output.h
index 024d2440..135d429a 100644
--- a/cli/mmcli-output.h
+++ b/cli/mmcli-output.h
@@ -285,6 +285,7 @@ typedef enum {
MMC_OUTPUT_TYPE_NONE,
MMC_OUTPUT_TYPE_HUMAN,
MMC_OUTPUT_TYPE_KEYVALUE,
+ MMC_OUTPUT_TYPE_JSON
} MmcOutputType;
void mmcli_output_set (MmcOutputType type);
diff --git a/cli/mmcli.c b/cli/mmcli.c
index 6953672d..ab08f33d 100644
--- a/cli/mmcli.c
+++ b/cli/mmcli.c
@@ -45,6 +45,7 @@ static GCancellable *cancellable;
/* Context */
static gboolean output_keyvalue_flag;
+static gboolean output_json_flag;
static gboolean verbose_flag;
static gboolean version_flag;
static gboolean async_flag;
@@ -55,6 +56,10 @@ static GOptionEntry main_entries[] = {
"Run action with machine-friendly key-value output",
NULL
},
+ { "output-json", 'J', 0, G_OPTION_ARG_NONE, &output_json_flag,
+ "Run action with machine-friendly json output",
+ NULL
+ },
{ "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose_flag,
"Run action with verbose logs",
NULL
@@ -237,14 +242,19 @@ main (gint argc, gchar **argv)
g_log_set_handler (G_LOG_DOMAIN, G_LOG_LEVEL_MASK, log_handler, NULL);
/* Setup output */
- if (output_keyvalue_flag) {
+ if (output_keyvalue_flag && output_json_flag) {
+ g_printerr ("error: only one output type supported at the same time\n");
+ exit (EXIT_FAILURE);
+ }
+ if (output_keyvalue_flag || output_json_flag) {
if (verbose_flag) {
g_printerr ("error: cannot set verbose output in keyvalue output type\n");
exit (EXIT_FAILURE);
}
- mmcli_output_set (MMC_OUTPUT_TYPE_KEYVALUE);
- } else
+ mmcli_output_set (output_keyvalue_flag ? MMC_OUTPUT_TYPE_KEYVALUE : MMC_OUTPUT_TYPE_JSON);
+ } else {
mmcli_output_set (MMC_OUTPUT_TYPE_HUMAN);
+ }
/* Setup signals */
signal (SIGINT, signals_handler);