add display of map information in JSON format
authorTodd Gill <tgill@redhat.com>
Tue, 24 May 2016 18:03:07 +0000 (14:03 -0400)
committerChristophe Varoqui <christophe.varoqui@opensvc.com>
Wed, 1 Jun 2016 14:54:28 +0000 (16:54 +0200)
The changes add these commands:

multipathd show maps json
multipathd show map $map json

Each command will output the requested map(s) in JSON.

For the "show maps json" command, the change pre-allocates
INITIAL_REPLY_LEN * PRINT_JSON_MULTIPLIER(5) * map count.
The JSON text is about 5x the size of the "show maps topology"
text.  Multiplying by the map count helps decrease the number
of realloc calls.

Signed-off-by: Todd Gill <tgill@redhat.com>
Signed-off-by: Gris Ge <fge@redhat.com>
libmultipath/print.c
libmultipath/print.h
multipathd/cli.c
multipathd/cli.h
multipathd/cli_handlers.c
multipathd/cli_handlers.h
multipathd/main.c

index 7fec6e9..2d159ed 100644 (file)
@@ -274,6 +274,61 @@ snprint_multipath_vpr (char * buff, size_t len, struct multipath * mpp)
        return snprintf(buff, len, "##,##");
 }
 
+
+static int
+snprint_multipath_vend (char * buff, size_t len, struct multipath * mpp)
+{
+       struct pathgroup * pgp;
+       struct path * pp;
+       int i, j;
+
+       vector_foreach_slot(mpp->pg, pgp, i) {
+               if (!pgp)
+                       continue;
+               vector_foreach_slot(pgp->paths, pp, j) {
+                       if (strlen(pp->vendor_id))
+                               return snprintf(buff, len, "%s", pp->vendor_id);
+               }
+       }
+       return snprintf(buff, len, "##");
+}
+
+static int
+snprint_multipath_prod (char * buff, size_t len, struct multipath * mpp)
+{
+       struct pathgroup * pgp;
+       struct path * pp;
+       int i, j;
+
+       vector_foreach_slot(mpp->pg, pgp, i) {
+               if (!pgp)
+                       continue;
+               vector_foreach_slot(pgp->paths, pp, j) {
+                       if (strlen(pp->product_id))
+                               return snprintf(buff, len, "%s", pp->product_id);
+               }
+       }
+       return snprintf(buff, len, "##");
+}
+
+static int
+snprint_multipath_rev (char * buff, size_t len, struct multipath * mpp)
+{
+       struct pathgroup * pgp;
+       struct path * pp;
+       int i, j;
+
+       vector_foreach_slot(mpp->pg, pgp, i) {
+               if (!pgp)
+                       continue;
+               vector_foreach_slot(pgp->paths, pp, j) {
+                       if (strlen(pp->rev))
+                               return snprintf(buff, len, "%s", pp->rev);
+               }
+       }
+       return snprintf(buff, len, "##");
+}
+
 static int
 snprint_action (char * buff, size_t len, struct multipath * mpp)
 {
@@ -577,6 +632,9 @@ struct multipath_data mpd[] = {
        {'3', "total_q_time",  0, snprint_total_q_time},
        {'4', "q_timeouts",    0, snprint_q_timeouts},
        {'s', "vend/prod/rev", 0, snprint_multipath_vpr},
+       {'v', "vend",          0, snprint_multipath_vend},
+       {'p', "prod",          0, snprint_multipath_prod},
+       {'e', "rev",           0, snprint_multipath_rev},
        {0, NULL, 0 , NULL}
 };
 
@@ -999,6 +1057,170 @@ snprint_multipath_topology (char * buff, int len, struct multipath * mpp,
        return fwd;
 }
 
+static int
+snprint_json (char * buff, int len, int indent, char *json_str)
+{
+       int fwd = 0, i;
+
+       for (i = 0; i < indent; i++) {
+               fwd += snprintf(buff + fwd, len - fwd, PRINT_JSON_INDENT);
+               if (fwd > len)
+                       return fwd;
+       }
+
+       fwd += snprintf(buff + fwd, len - fwd, "%s", json_str);
+       return fwd;
+}
+
+static int
+snprint_json_header (char * buff, int len)
+{
+       int fwd = 0;
+
+       fwd +=  snprint_json(buff, len, 0, PRINT_JSON_START_ELEM);
+       if (fwd > len)
+               return fwd;
+
+       fwd +=  snprintf(buff + fwd, len  - fwd, PRINT_JSON_START_VERSION,
+                       PRINT_JSON_MAJOR_VERSION, PRINT_JSON_MINOR_VERSION);
+       return fwd;
+}
+
+static int
+snprint_json_elem_footer (char * buff, int len, int indent, int last)
+{
+       int fwd = 0, i;
+
+       for (i = 0; i < indent; i++) {
+               fwd += snprintf(buff + fwd, len - fwd, PRINT_JSON_INDENT);
+               if (fwd > len)
+                       return fwd;
+       }
+
+       if (last == 1)
+               fwd += snprintf(buff + fwd, len - fwd, "%s", PRINT_JSON_END_LAST_ELEM);
+       else
+               fwd += snprintf(buff + fwd, len - fwd, "%s", PRINT_JSON_END_ELEM);
+       return fwd;
+}
+
+static int
+snprint_multipath_fields_json (char * buff, int len,
+               struct multipath * mpp, int last)
+{
+       int i, j, fwd = 0;
+       struct path *pp;
+       struct pathgroup *pgp;
+
+       fwd += snprint_multipath(buff, len, PRINT_JSON_MAP, mpp, 0);
+       if (fwd > len)
+               return fwd;
+
+       fwd += snprint_json(buff + fwd, len - fwd, 2, PRINT_JSON_START_GROUPS);
+       if (fwd > len)
+               return fwd;
+
+       vector_foreach_slot (mpp->pg, pgp, i) {
+
+               pgp->selector = mpp->selector;
+               fwd += snprint_pathgroup(buff + fwd, len - fwd, PRINT_JSON_GROUP, pgp);
+               if (fwd > len)
+                       return fwd;
+
+               fwd += snprintf(buff + fwd, len - fwd, PRINT_JSON_GROUP_NUM, i + 1);
+               if (fwd > len)
+                       return fwd;
+
+               fwd += snprint_json(buff + fwd, len - fwd, 3, PRINT_JSON_START_PATHS);
+               if (fwd > len)
+                       return fwd;
+
+               vector_foreach_slot (pgp->paths, pp, j) {
+                       fwd += snprint_path(buff + fwd, len - fwd, PRINT_JSON_PATH, pp, 0);
+                       if (fwd > len)
+                               return fwd;
+
+                       fwd += snprint_json_elem_footer(buff + fwd,
+                                       len - fwd, 3, j + 1 == VECTOR_SIZE(pgp->paths));
+                       if (fwd > len)
+                               return fwd;
+               }
+               fwd += snprint_json(buff + fwd, len - fwd, 0, PRINT_JSON_END_ARRAY);
+               if (fwd > len)
+                       return fwd;
+
+               fwd +=  snprint_json_elem_footer(buff + fwd,
+                               len - fwd, 2, i + 1 == VECTOR_SIZE(mpp->pg));
+               if (fwd > len)
+                       return fwd;
+       }
+
+       fwd += snprint_json(buff + fwd, len - fwd, 0, PRINT_JSON_END_ARRAY);
+       if (fwd > len)
+               return fwd;
+
+       fwd += snprint_json_elem_footer(buff + fwd, len - fwd, 1, last);
+       return fwd;
+}
+
+int
+snprint_multipath_map_json (char * buff, int len,
+               struct multipath * mpp, int last){
+       int fwd = 0;
+
+       fwd +=  snprint_json_header(buff, len);
+       if (fwd > len)
+               return len;
+
+       fwd +=  snprint_json(buff + fwd, len - fwd, 0, PRINT_JSON_START_MAP);
+       if (fwd > len)
+               return len;
+
+       fwd += snprint_multipath_fields_json(buff + fwd, len - fwd, mpp, 1);
+       if (fwd > len)
+               return len;
+
+       fwd +=  snprint_json(buff + fwd, len - fwd, 0, "\n");
+       if (fwd > len)
+               return len;
+
+       fwd +=  snprint_json(buff + fwd, len - fwd, 0, PRINT_JSON_END_LAST);
+       if (fwd > len)
+               return len;
+       return fwd;
+}
+
+int
+snprint_multipath_topology_json (char * buff, int len, struct vectors * vecs)
+{
+       int i, fwd = 0;
+       struct multipath * mpp;
+
+       fwd +=  snprint_json_header(buff, len);
+       if (fwd > len)
+               return len;
+
+       fwd +=  snprint_json(buff + fwd, len  - fwd, 1, PRINT_JSON_START_MAPS);
+       if (fwd > len)
+               return len;
+
+       vector_foreach_slot(vecs->mpvec, mpp, i) {
+               fwd += snprint_multipath_fields_json(buff + fwd, len - fwd,
+                               mpp, i + 1 == VECTOR_SIZE(vecs->mpvec));
+               if (fwd > len)
+                       return len;
+       }
+
+       fwd +=  snprint_json(buff + fwd, len - fwd, 0, PRINT_JSON_END_ARRAY);
+       if (fwd > len)
+               return len;
+
+       fwd +=  snprint_json(buff + fwd, len - fwd, 0, PRINT_JSON_END_LAST);
+       if (fwd > len)
+               return len;
+       return fwd;
+}
+
 static int
 snprint_hwentry (char * buff, int len, struct hwentry * hwe)
 {
index 8bd0bbc..72758ad 100644 (file)
@@ -7,6 +7,63 @@
 #define PRINT_MAP_PROPS      "size=%S features='%f' hwhandler='%h' wp=%r"
 #define PRINT_PG_INDENT      "policy='%s' prio=%p status=%t"
 
+#define PRINT_JSON_MULTIPLIER     5
+#define PRINT_JSON_MAJOR_VERSION  0
+#define PRINT_JSON_MINOR_VERSION  1
+#define PRINT_JSON_START_VERSION  "   \"major_version\": %d,\n" \
+                                  "   \"minor_version\": %d,\n"
+#define PRINT_JSON_START_ELEM     "{\n"
+#define PRINT_JSON_START_MAP      "   \"map\":"
+#define PRINT_JSON_START_MAPS     "\"maps\": ["
+#define PRINT_JSON_START_PATHS    "\"paths\": ["
+#define PRINT_JSON_START_GROUPS   "\"path_groups\": ["
+#define PRINT_JSON_END_ELEM       "},"
+#define PRINT_JSON_END_LAST_ELEM  "}"
+#define PRINT_JSON_END_LAST       "}\n"
+#define PRINT_JSON_END_ARRAY      "]\n"
+#define PRINT_JSON_INDENT    "   "
+#define PRINT_JSON_MAP       "{\n" \
+                             "      \"name\" : \"%n\",\n" \
+                             "      \"uuid\" : \"%w\",\n" \
+                             "      \"sysfs\" : \"%d\",\n" \
+                             "      \"failback\" : \"%F\",\n" \
+                             "      \"queueing\" : \"%Q\",\n" \
+                             "      \"paths\" : %N,\n" \
+                             "      \"write_prot\" : \"%r\",\n" \
+                             "      \"dm_st\" : \"%t\",\n" \
+                             "      \"features\" : \"%f\",\n" \
+                             "      \"hwhandler\" : \"%h\",\n" \
+                             "      \"action\" : \"%A\",\n" \
+                             "      \"path_faults\" : %0,\n" \
+                             "      \"vend\" : \"%v\",\n" \
+                             "      \"prod\" : \"%p\",\n" \
+                             "      \"rev\" : \"%e\",\n" \
+                             "      \"switch_grp\" : %1,\n" \
+                             "      \"map_loads\" : %2,\n" \
+                             "      \"total_q_time\" : %3,\n" \
+                             "      \"q_timeouts\" : %4,"
+
+#define PRINT_JSON_GROUP     "{\n" \
+                             "         \"selector\" : \"%s\",\n" \
+                             "         \"pri\" : %p,\n" \
+                             "         \"dm_st\" : \"%t\","
+
+#define PRINT_JSON_GROUP_NUM "         \"group\" : %d,\n"
+
+#define PRINT_JSON_PATH      "{\n" \
+                             "            \"dev\" : \"%d\",\n"\
+                             "            \"dev_t\" : \"%D\",\n" \
+                             "            \"dm_st\" : \"%t\",\n" \
+                             "            \"dev_st\" : \"%o\",\n" \
+                             "            \"chk_st\" : \"%T\",\n" \
+                             "            \"checker\" : \"%c\",\n" \
+                             "            \"pri\" : %p,\n" \
+                             "            \"host_wwnn\" : \"%N\",\n" \
+                             "            \"target_wwnn\" : \"%n\",\n" \
+                             "            \"host_wwpn\" : \"%R\",\n" \
+                             "            \"target_wwpn\" : \"%r\",\n" \
+                             "            \"host_adapter\" : \"%a\""
+
 #define MAX_LINE_LEN  80
 #define MAX_LINES     64
 #define MAX_FIELD_LEN 64
@@ -41,6 +98,10 @@ int snprint_path (char *, int, char *, struct path *, int);
 int snprint_multipath (char *, int, char *, struct multipath *, int);
 int snprint_multipath_topology (char *, int, struct multipath * mpp,
                                int verbosity);
+int snprint_multipath_topology_json (char * buff, int len,
+                               struct vectors * vecs);
+int snprint_multipath_map_json (char * buff, int len,
+                               struct multipath * mpp, int last);
 int snprint_defaults (char *, int);
 int snprint_blacklist (char *, int);
 int snprint_blacklist_except (char *, int);
index b9930dc..d7f53ee 100644 (file)
@@ -207,6 +207,7 @@ load_keys (void)
        r += add_key(keys, "setprstatus", SETPRSTATUS, 0);
        r += add_key(keys, "unsetprstatus", UNSETPRSTATUS, 0);
        r += add_key(keys, "format", FMT, 1);
+       r += add_key(keys, "json", JSON, 0);
 
        if (r) {
                free_keys(keys);
@@ -537,8 +538,10 @@ cli_init (void) {
        add_handler(LIST+MAPS+FMT, NULL);
        add_handler(LIST+MAPS+RAW+FMT, NULL);
        add_handler(LIST+MAPS+TOPOLOGY, NULL);
+       add_handler(LIST+MAPS+JSON, NULL);
        add_handler(LIST+TOPOLOGY, NULL);
        add_handler(LIST+MAP+TOPOLOGY, NULL);
+       add_handler(LIST+MAP+JSON, NULL);
        add_handler(LIST+MAP+FMT, NULL);
        add_handler(LIST+MAP+RAW+FMT, NULL);
        add_handler(LIST+CONFIG, NULL);
index 84ca40f..92cb41b 100644 (file)
@@ -36,6 +36,7 @@ enum {
        __SETPRSTATUS,
        __UNSETPRSTATUS,
        __FMT,
+       __JSON,
 };
 
 #define LIST           (1 << __LIST)
@@ -74,6 +75,7 @@ enum {
 #define SETPRSTATUS    (1ULL << __SETPRSTATUS)
 #define UNSETPRSTATUS  (1ULL << __UNSETPRSTATUS)
 #define FMT            (1ULL << __FMT)
+#define JSON           (1ULL << __JSON)
 
 #define INITIAL_REPLY_LEN      1200
 
index 8b3cb9d..a1b7052 100644 (file)
@@ -155,6 +155,70 @@ show_maps_topology (char ** r, int * len, struct vectors * vecs)
        return 0;
 }
 
+int
+show_maps_json (char ** r, int * len, struct vectors * vecs)
+{
+       int i;
+       struct multipath * mpp;
+       char * c;
+       char * reply;
+       unsigned int maxlen = INITIAL_REPLY_LEN *
+                       PRINT_JSON_MULTIPLIER * VECTOR_SIZE(vecs->mpvec);
+       int again = 1;
+
+       vector_foreach_slot(vecs->mpvec, mpp, i) {
+               if (update_multipath(vecs, mpp->alias, 0)) {
+                       return 1;
+               }
+       }
+
+       reply = MALLOC(maxlen);
+
+       while (again) {
+               if (!reply)
+                       return 1;
+
+               c = reply;
+
+               c += snprint_multipath_topology_json(c, maxlen, vecs);
+               again = ((c - reply) == maxlen);
+
+               REALLOC_REPLY(reply, again, maxlen);
+       }
+       *r = reply;
+       *len = (int)(c - reply);
+       return 0;
+}
+
+int
+show_map_json (char ** r, int * len, struct multipath * mpp,
+                  struct vectors * vecs)
+{
+       char * c;
+       char * reply;
+       unsigned int maxlen = INITIAL_REPLY_LEN;
+       int again = 1;
+
+       if (update_multipath(vecs, mpp->alias, 0))
+               return 1;
+       reply = MALLOC(maxlen);
+
+       while (again) {
+               if (!reply)
+                       return 1;
+
+               c = reply;
+
+               c += snprint_multipath_map_json(c, maxlen, mpp, 1);
+               again = ((c - reply) == maxlen);
+
+               REALLOC_REPLY(reply, again, maxlen);
+       }
+       *r = reply;
+       *len = (int)(c - reply);
+       return 0;
+}
+
 int
 show_config (char ** r, int * len)
 {
@@ -290,6 +354,35 @@ cli_list_maps_topology (void * v, char ** reply, int * len, void * data)
        return show_maps_topology(reply, len, vecs);
 }
 
+int
+cli_list_map_json (void * v, char ** reply, int * len, void * data)
+{
+       struct multipath * mpp;
+       struct vectors * vecs = (struct vectors *)data;
+       char * param = get_keyparam(v, MAP);
+
+       param = convert_dev(param, 0);
+       get_path_layout(vecs->pathvec, 0);
+       mpp = find_mp_by_str(vecs->mpvec, param);
+
+       if (!mpp)
+               return 1;
+
+       condlog(3, "list multipath json %s (operator)", param);
+
+       return show_map_json(reply, len, mpp, vecs);
+}
+
+int
+cli_list_maps_json (void * v, char ** reply, int * len, void * data)
+{
+       struct vectors * vecs = (struct vectors *)data;
+
+       condlog(3, "list multipaths json (operator)");
+
+       return show_maps_json(reply, len, vecs);
+}
+
 int
 cli_list_wildcards (void * v, char ** reply, int * len, void * data)
 {
index 5d51018..e838f19 100644 (file)
@@ -13,6 +13,8 @@ int cli_list_maps_status (void * v, char ** reply, int * len, void * data);
 int cli_list_maps_stats (void * v, char ** reply, int * len, void * data);
 int cli_list_map_topology (void * v, char ** reply, int * len, void * data);
 int cli_list_maps_topology (void * v, char ** reply, int * len, void * data);
+int cli_list_map_json (void * v, char ** reply, int * len, void * data);
+int cli_list_maps_json (void * v, char ** reply, int * len, void * data);
 int cli_list_config (void * v, char ** reply, int * len, void * data);
 int cli_list_blacklist (void * v, char ** reply, int * len, void * data);
 int cli_list_devices (void * v, char ** reply, int * len, void * data);
index 2c7486d..3d71c4f 100644 (file)
@@ -1129,9 +1129,11 @@ uxlsnrloop (void * ap)
        set_handler_callback(LIST+MAPS+RAW+FMT, cli_list_maps_raw);
        set_handler_callback(LIST+MAPS+TOPOLOGY, cli_list_maps_topology);
        set_handler_callback(LIST+TOPOLOGY, cli_list_maps_topology);
+       set_handler_callback(LIST+MAPS+JSON, cli_list_maps_json);
        set_handler_callback(LIST+MAP+TOPOLOGY, cli_list_map_topology);
        set_handler_callback(LIST+MAP+FMT, cli_list_map_fmt);
        set_handler_callback(LIST+MAP+RAW+FMT, cli_list_map_fmt);
+       set_handler_callback(LIST+MAP+JSON, cli_list_map_json);
        set_unlocked_handler_callback(LIST+CONFIG, cli_list_config);
        set_unlocked_handler_callback(LIST+BLACKLIST, cli_list_blacklist);
        set_handler_callback(LIST+DEVICES, cli_list_devices);