mmcli: add json output support
Signed-off-by: Maxim Anisimov <maxim.anisimov.ua@gmail.com>
This commit is contained in:

committed by
Aleksander Morgado

parent
b0ec3030a3
commit
d1ac5ca16b
@@ -1071,6 +1071,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 */
|
/* Dump output */
|
||||||
|
|
||||||
@@ -1086,6 +1199,9 @@ mmcli_output_dump (void)
|
|||||||
case MMC_OUTPUT_TYPE_KEYVALUE:
|
case MMC_OUTPUT_TYPE_KEYVALUE:
|
||||||
dump_output_keyvalue ();
|
dump_output_keyvalue ();
|
||||||
break;
|
break;
|
||||||
|
case MMC_OUTPUT_TYPE_JSON:
|
||||||
|
dump_output_json ();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
g_list_free_full (output_items, (GDestroyNotify) output_item_free);
|
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:
|
case MMC_OUTPUT_TYPE_KEYVALUE:
|
||||||
dump_output_list_keyvalue (field);
|
dump_output_list_keyvalue (field);
|
||||||
break;
|
break;
|
||||||
|
case MMC_OUTPUT_TYPE_JSON:
|
||||||
|
dump_output_list_json (field);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
g_list_free_full (output_items, (GDestroyNotify) output_item_free);
|
g_list_free_full (output_items, (GDestroyNotify) output_item_free);
|
||||||
|
@@ -285,6 +285,7 @@ typedef enum {
|
|||||||
MMC_OUTPUT_TYPE_NONE,
|
MMC_OUTPUT_TYPE_NONE,
|
||||||
MMC_OUTPUT_TYPE_HUMAN,
|
MMC_OUTPUT_TYPE_HUMAN,
|
||||||
MMC_OUTPUT_TYPE_KEYVALUE,
|
MMC_OUTPUT_TYPE_KEYVALUE,
|
||||||
|
MMC_OUTPUT_TYPE_JSON
|
||||||
} MmcOutputType;
|
} MmcOutputType;
|
||||||
|
|
||||||
void mmcli_output_set (MmcOutputType type);
|
void mmcli_output_set (MmcOutputType type);
|
||||||
|
16
cli/mmcli.c
16
cli/mmcli.c
@@ -45,6 +45,7 @@ static GCancellable *cancellable;
|
|||||||
|
|
||||||
/* Context */
|
/* Context */
|
||||||
static gboolean output_keyvalue_flag;
|
static gboolean output_keyvalue_flag;
|
||||||
|
static gboolean output_json_flag;
|
||||||
static gboolean verbose_flag;
|
static gboolean verbose_flag;
|
||||||
static gboolean version_flag;
|
static gboolean version_flag;
|
||||||
static gboolean async_flag;
|
static gboolean async_flag;
|
||||||
@@ -55,6 +56,10 @@ static GOptionEntry main_entries[] = {
|
|||||||
"Run action with machine-friendly key-value output",
|
"Run action with machine-friendly key-value output",
|
||||||
NULL
|
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,
|
{ "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose_flag,
|
||||||
"Run action with verbose logs",
|
"Run action with verbose logs",
|
||||||
NULL
|
NULL
|
||||||
@@ -237,14 +242,19 @@ main (gint argc, gchar **argv)
|
|||||||
g_log_set_handler (G_LOG_DOMAIN, G_LOG_LEVEL_MASK, log_handler, NULL);
|
g_log_set_handler (G_LOG_DOMAIN, G_LOG_LEVEL_MASK, log_handler, NULL);
|
||||||
|
|
||||||
/* Setup output */
|
/* 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) {
|
if (verbose_flag) {
|
||||||
g_printerr ("error: cannot set verbose output in keyvalue output type\n");
|
g_printerr ("error: cannot set verbose output in keyvalue output type\n");
|
||||||
exit (EXIT_FAILURE);
|
exit (EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
mmcli_output_set (MMC_OUTPUT_TYPE_KEYVALUE);
|
mmcli_output_set (output_keyvalue_flag ? MMC_OUTPUT_TYPE_KEYVALUE : MMC_OUTPUT_TYPE_JSON);
|
||||||
} else
|
} else {
|
||||||
mmcli_output_set (MMC_OUTPUT_TYPE_HUMAN);
|
mmcli_output_set (MMC_OUTPUT_TYPE_HUMAN);
|
||||||
|
}
|
||||||
|
|
||||||
/* Setup signals */
|
/* Setup signals */
|
||||||
signal (SIGINT, signals_handler);
|
signal (SIGINT, signals_handler);
|
||||||
|
Reference in New Issue
Block a user