[v3] vis: Add an output format which is json conformant.

Message ID 1381137782-3676-1-git-send-email-andrew@lunn.ch (mailing list archive)
State Accepted, archived
Headers

Commit Message

Andrew Lunn Oct. 7, 2013, 9:23 a.m. UTC
  The current json output consists of individual lines which are json
conformant, but there is no conformancy between lines. Add a new
output format, which as a whole is json conformant.

Signed-off-by: Andrew Lunn <andrew@lunn.ch>
---

Since v1:
 Add routing algorithm to output
 Add batadv-vis version to output
 Update man page

Since v2:
 Change cjson to jasondoc, as suggested by Nicolás Echániz

 README               |  44 +++++++-
 vis/man/batadv-vis.8 |  45 +++++++-
 vis/vis.c            | 302 ++++++++++++++++++++++++++++++++++++++++++---------
 vis/vis.h            |   3 +-
 4 files changed, 337 insertions(+), 57 deletions(-)
  

Comments

Simon Wunderlich Oct. 13, 2013, 9:51 p.m. UTC | #1
On Mon, Oct 07, 2013 at 11:23:02AM +0200, Andrew Lunn wrote:
> The current json output consists of individual lines which are json
> conformant, but there is no conformancy between lines. Add a new
> output format, which as a whole is json conformant.
> 
> Signed-off-by: Andrew Lunn <andrew@lunn.ch>

Applied in 38eae4d.

Thanks,
	Simon
  

Patch

diff --git a/README b/README
index 50c36ad..83cdaf8 100644
--- a/README
+++ b/README
@@ -152,7 +152,7 @@  digraph {
 [...]
 }
 
-For a json formatted output, use:
+For a json line formatted output, use:
 
  $ batadv-vis -f json
 { "primary" : "fe:f0:00:00:04:01" }
@@ -169,6 +169,48 @@  For a json formatted output, use:
 { "primary" : "fe:f0:00:00:08:01" }
 [...]
 
+and for output where the complete document is json, use:
+
+ $ batadv-vis -f jsondoc
+{
+  "source_version" : "2013.3.0-14-gcd34783",
+  "algorithm" : 4,
+  "vis" : [
+    { "primary" : "fe:f0:00:00:04:01",
+      "neighbors" : [
+         { "router" : "fe:f0:00:00:04:01",
+           "neighbor" : "fe:f0:00:00:05:01",
+           "metric" : "1.000" },
+         { "router" : "fe:f0:00:00:04:01",
+           "neighbor" : "fe:f0:00:00:03:01",
+           "metric" : "1.008" }
+      ],
+      "clients" : [
+         "00:00:43:05:00:04",
+         "fe:f1:00:00:04:01"
+      ]
+    },
+    { "primary" : "fe:f0:00:00:02:01",
+      "neighbors" : [
+         { "router" : "fe:f0:00:00:02:01",
+           "neighbor" : "fe:f0:00:00:03:01",
+  	 "metric" : "1.000" },
+         { "router" : "fe:f0:00:00:02:01",
+           "neighbor" : "fe:f0:00:00:01:01",
+           "metric" : "1.016" },
+         { "router" : "fe:f0:00:00:02:01",
+           "neighbor" : "fe:f0:00:00:08:01",
+           "metric" : "1.000" }
+      ],
+      "clients" : [
+        "fe:f1:00:00:02:01",
+        "00:00:43:05:00:02"
+      ]
+    },
+    { "primary" : "fe:f0:00:00:08:01",
+[...]
+
+
 License
 -------
 
diff --git a/vis/man/batadv-vis.8 b/vis/man/batadv-vis.8
index dfa2604..807c3b1 100644
--- a/vis/man/batadv-vis.8
+++ b/vis/man/batadv-vis.8
@@ -51,7 +51,7 @@  information from batman-adv every 10 seconds and set it in alfred via unix
 socket. The alfred server must run too to get this information set.
 .TP
 \fB\-f\fP, \fB\-\-format\fP \fIformat\fP
-Specify the output format for client mode (either "json" or "dot")
+Specify the output format for client mode (either "json", "jsondoc" or "dot")
 .
 .SH EXAMPLES
 Start an batadv\-vis server which is fetching the information for bat0:
@@ -106,6 +106,49 @@  To get a json vis output:
      [...]
 .fi
 .br
+
+To get output where the complete document is json:
+.br
+\fB     batadv-vis -f jsondoc\fP
+.nf
+     {
+       "source_version" : "2013.3.0-14-gcd34783",
+       "algorithm" : 4,
+       "vis" : [
+         { "primary" : "fe:f0:00:00:04:01",
+           "neighbors" : [
+              { "router" : "fe:f0:00:00:04:01",
+                "neighbor" : "fe:f0:00:00:05:01",
+                "metric" : "1.000" },
+              { "router" : "fe:f0:00:00:04:01",
+                "neighbor" : "fe:f0:00:00:03:01",
+                "metric" : "1.008" }
+           ],
+           "clients" : [
+              "00:00:43:05:00:04",
+              "fe:f1:00:00:04:01"
+           ]
+         },
+         { "primary" : "fe:f0:00:00:02:01",
+           "neighbors" : [
+              { "router" : "fe:f0:00:00:02:01",
+                "neighbor" : "fe:f0:00:00:03:01",
+       	 "metric" : "1.000" },
+              { "router" : "fe:f0:00:00:02:01",
+                "neighbor" : "fe:f0:00:00:01:01",
+                "metric" : "1.016" },
+              { "router" : "fe:f0:00:00:02:01",
+                "neighbor" : "fe:f0:00:00:08:01",
+                "metric" : "1.000" }
+           ],
+           "clients" : [
+             "fe:f1:00:00:02:01",
+             "00:00:43:05:00:02"
+           ]
+         },
+         { "primary" : "fe:f0:00:00:08:01",
+     [...]
+
 .
 .SH SEE ALSO
 .BR alfred (8),
diff --git a/vis/vis.c b/vis/vis.c
index ad7f530..9c7d7a9 100644
--- a/vis/vis.c
+++ b/vis/vis.c
@@ -26,6 +26,7 @@ 
 #include <linux/if.h>
 #include <netinet/in.h>
 #include <signal.h>
+#include <stdbool.h>
 #include <stddef.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -511,18 +512,254 @@  static struct vis_v1 *vis_receive_answer_packet(int sock, uint16_t *len)
 	return (struct vis_v1 *) data->data;
 }
 
+static void vis_dot_preamble(void)
+{
+	printf("digraph {\n");
+}
+
+static void vis_dot_interfaces(uint8_t iface_n, struct vis_iface *ifaces)
+{
+	int i;
+
+	printf("\tsubgraph \"cluster_%s\" {\n", mac_to_str(ifaces[0].mac));
+	for (i = 0; i < iface_n; i++)
+		printf("\t\t\"%s\"%s\n", mac_to_str(ifaces[i].mac),
+		       i ? " [peripheries=2]":"");
+	printf("\t}\n");
+}
+
+static void vis_dot_entries(uint8_t entries_n, struct vis_entry *vis_entries,
+			    uint8_t iface_n, struct vis_iface *ifaces)
+{
+	int i;
+
+	for (i = 0; i < entries_n; i++) {
+		if (vis_entries[i].ifindex == 255) {
+			printf("\t\"%s\" ", mac_to_str(ifaces[0].mac));
+			printf("-> \"%s\" [label=\"TT\"]\n",
+			       mac_to_str(vis_entries[i].mac));
+		} else {
+			if (vis_entries[i].ifindex >= iface_n) {
+				fprintf(stderr, "ERROR: bad ifindex ...\n");
+				continue;
+			}
+			if (vis_entries[i].qual == 0) {
+				fprintf(stderr, "ERROR: quality = 0?\n");
+				continue;
+			}
+			printf("\t\"%s\" ",
+			       mac_to_str(ifaces[vis_entries[i].ifindex].mac));
+			printf("-> \"%s\" [label=\"%3.3f\"]\n",
+			       mac_to_str(vis_entries[i].mac),
+			       255.0 / ((float)vis_entries[i].qual));
+		}
+	}
+}
+
+static void vis_dot_postamble(void)
+{
+	printf("}\n");
+}
+
+static void vis_json_preamble(void)
+{
+}
+
+static void vis_json_interfaces(uint8_t iface_n, struct vis_iface *ifaces)
+{
+	int i;
+
+	printf("{ \"primary\" : \"%s\" }\n", mac_to_str(ifaces[0].mac));
+	for (i = 1; i < iface_n; i++) {
+		printf("{ \"secondary\" : \"%s\"", mac_to_str(ifaces[i].mac));
+		printf(", \"of\" : \"%s\" }\n", mac_to_str(ifaces[0].mac));
+	}
+}
+
+static void vis_json_entries(uint8_t entries_n, struct vis_entry *vis_entries,
+			     uint8_t iface_n, struct vis_iface *ifaces)
+{
+	int i;
+
+	for (i = 0; i < entries_n; i++) {
+		if (vis_entries[i].ifindex == 255) {
+			printf("{ \"router\" : \"%s\"",
+			       mac_to_str(ifaces[0].mac));
+			printf(", \"gateway\" : \"%s\", \"label\" : \"TT\" }\n",
+			       mac_to_str(vis_entries[i].mac));
+		} else {
+			if (vis_entries[i].ifindex >= iface_n) {
+				fprintf(stderr, "ERROR: bad ifindex ...\n");
+				continue;
+			}
+			if (vis_entries[i].qual == 0) {
+				fprintf(stderr, "ERROR: quality = 0?\n");
+				continue;
+			}
+			printf("{ \"router\" : \"%s\"",
+			       mac_to_str(ifaces[vis_entries[i].ifindex].mac));
+			printf(", \"neighbor\" : \"%s\", \"label\" : \"%3.3f\" }\n",
+			       mac_to_str(vis_entries[i].mac),
+			       255.0 / ((float)vis_entries[i].qual));
+		}
+	}
+}
+
+static void vis_json_postamble(void)
+{
+}
+
+static void vis_jsondoc_preamble(void)
+{
+	printf("{\n");
+	printf("  \"source_version\" : \"%s\",\n", SOURCE_VERSION);
+	printf("  \"algorithm\" : 4,\n");
+	printf("  \"vis\" : [\n");
+}
+
+static void vis_jsondoc_interfaces(uint8_t iface_n, struct vis_iface *ifaces)
+{
+	int i;
+	static bool first_interface = true;
+
+	if (first_interface)
+		first_interface = false;
+	else
+		printf(",\n");
+
+	printf("    { \"primary\" : \"%s\",\n", mac_to_str(ifaces[0].mac));
+	if (iface_n > 1) {
+		printf("      \"secondary\" : [ ");
+		for (i = 1; i < iface_n; i++) {
+			printf("\"%s\"", mac_to_str(ifaces[i].mac));
+			if ( i < iface_n - 1)
+				printf(",");
+		}
+		printf("\n       ],\n");
+	}
+}
+
+static void vis_jsondoc_entries(uint8_t entries_n,
+				struct vis_entry *vis_entries,
+				uint8_t iface_n, struct vis_iface *ifaces)
+{
+	bool first_neighbor = true;
+	bool first_tt = true;
+	int i;
+
+	printf("      \"neighbors\" : [\n");
+
+	for (i = 0; i < entries_n; i++) {
+		if (vis_entries[i].ifindex == 255) {
+			continue;
+		}
+
+		if (vis_entries[i].ifindex >= iface_n) {
+			fprintf(stderr, "ERROR: bad ifindex ...\n");
+			continue;
+		}
+		if (vis_entries[i].qual == 0) {
+			fprintf(stderr, "ERROR: quality = 0?\n");
+			continue;
+		}
+
+		if (first_neighbor)
+			first_neighbor = false;
+		else
+			printf(",\n");
+
+		printf("         { \"router\" : \"%s\",\n",
+		       mac_to_str(ifaces[vis_entries[i].ifindex].mac));
+		printf("           \"neighbor\" : \"%s\",\n",
+		       mac_to_str(vis_entries[i].mac));
+		printf("           \"metric\" : \"%3.3f\" }",
+		       255.0 / ((float)vis_entries[i].qual));
+	}
+
+	printf("\n      ],\n");
+
+	printf("      \"clients\" : [\n");
+
+	for (i = 0; i < entries_n; i++) {
+		if (vis_entries[i].ifindex == 255) {
+			if (first_tt)
+				first_tt = false;
+			else
+				printf(",\n");
+
+			printf("        \"%s\"",
+			       mac_to_str(vis_entries[i].mac));
+		}
+	}
+	printf("\n      ]\n");
+	printf("    }");
+}
+
+static void vis_jsondoc_postamble(void)
+{
+	printf("\n  ]\n");
+	printf("}\n");
+}
+
+struct vis_print_ops
+{
+	void (*preamble)(void);
+	void (*interfaces)(uint8_t iface_n, struct vis_iface *ifaces);
+	void (*entries)(uint8_t entries_n, struct vis_entry *vis_entries,
+			uint8_t iface_n, struct vis_iface *ifaces);
+	void (*postamble)(void);
+};
+
+static const struct vis_print_ops vis_dot_ops =
+{
+	vis_dot_preamble,
+	vis_dot_interfaces,
+	vis_dot_entries,
+	vis_dot_postamble
+};
+
+static const struct vis_print_ops vis_json_ops =
+{
+	vis_json_preamble,
+	vis_json_interfaces,
+	vis_json_entries,
+	vis_json_postamble
+};
+
+static const struct vis_print_ops vis_jsondoc_ops =
+{
+	vis_jsondoc_preamble,
+	vis_jsondoc_interfaces,
+	vis_jsondoc_entries,
+	vis_jsondoc_postamble
+};
+
 static int vis_read_answer(struct globals *globals)
 {
+	const struct vis_print_ops *ops;
 	struct vis_v1 *vis_data;
 	uint16_t len;
 	struct vis_iface *ifaces;
 	struct vis_entry *vis_entries;
-	int i;
 
-	if (globals->vis_format == FORMAT_DOT)
-		printf("digraph {\n");
+	switch (globals->vis_format) {
+	case FORMAT_DOT:
+		ops = &vis_dot_ops;
+		break;
+	case FORMAT_JSON:
+		ops = &vis_json_ops;
+		break;
+	case FORMAT_JSONDOC:
+		ops = &vis_jsondoc_ops;
+		break;
+	default:
+		return -1;
+	}
+
+	ops->preamble();
 
-	while ((vis_data = vis_receive_answer_packet(globals->unix_sock, &len)) != NULL) {
+	while ((vis_data =
+		vis_receive_answer_packet(globals->unix_sock, &len)) != NULL) {
 		if (len < sizeof(*vis_data))
 			return -1;
 
@@ -536,60 +773,15 @@  static int vis_read_answer(struct globals *globals)
 		ifaces = vis_data->ifaces;
 		vis_entries = (struct vis_entry *) &ifaces[vis_data->iface_n];
 
-		if (globals->vis_format == FORMAT_DOT) {
-			printf("\tsubgraph \"cluster_%s\" {\n", mac_to_str(ifaces[0].mac));
-			for (i = 0; i < vis_data->iface_n; i++)
-				printf("\t\t\"%s\"%s\n", mac_to_str(ifaces[i].mac),
-				       i ? " [peripheries=2]":"");
-			printf("\t}\n");
-		} else if (globals->vis_format == FORMAT_JSON) {
-			printf("{ \"primary\" : \"%s\" }\n", mac_to_str(ifaces[0].mac));
-			for (i = 1; i < vis_data->iface_n; i++) {
-				printf("{ \"secondary\" : \"%s\"", mac_to_str(ifaces[i].mac));
-				printf(", \"of\" : \"%s\" }\n", mac_to_str(ifaces[0].mac));
-			}
-		}
+		ops->interfaces(vis_data->iface_n, ifaces);
 
 		if (vis_data->entries_n == 0)
 			continue;
 
-                for (i = 0; i < vis_data->entries_n; i++) {
-			if (vis_entries[i].ifindex == 255) {
-				if (globals->vis_format == FORMAT_DOT) {
-					printf("\t\"%s\" ", mac_to_str(ifaces[0].mac));
-					printf("-> \"%s\" [label=\"TT\"]\n", mac_to_str(vis_entries[i].mac));
-				} else if (globals->vis_format == FORMAT_JSON) {
-					printf("{ \"router\" : \"%s\"", mac_to_str(ifaces[0].mac));
-					printf(", \"gateway\" : \"%s\", \"label\" : \"TT\" }\n", mac_to_str(vis_entries[i].mac));
-				}
-			} else {
-				if (vis_entries[i].ifindex >= vis_data->iface_n) {
-					fprintf(stderr, "ERROR: bad ifindex ...\n");
-					continue;
-				}
-				if (vis_entries[i].qual == 0) {
-					fprintf(stderr, "ERROR: quality = 0?\n");
-					continue;
-				}
-				if (globals->vis_format == FORMAT_DOT) {
-					printf("\t\"%s\" ",
-					       mac_to_str(ifaces[vis_entries[i].ifindex].mac));
-					printf("-> \"%s\" [label=\"%3.3f\"]\n",
-					       mac_to_str(vis_entries[i].mac),
-					       255.0 / ((float)vis_entries[i].qual));
-				} else if (globals->vis_format == FORMAT_JSON) {
-					printf("{ \"router\" : \"%s\"",
-					       mac_to_str(ifaces[vis_entries[i].ifindex].mac));
-					printf(", \"neighbor\" : \"%s\", \"label\" : \"%3.3f\" }\n",
-					       mac_to_str(vis_entries[i].mac),
-					       255.0 / ((float)vis_entries[i].qual));
-
-				}
-			}
-		}
+		ops->entries(vis_data->entries_n, vis_entries,
+			     vis_data->iface_n, ifaces);
 	}
-	if (globals->vis_format == FORMAT_DOT)
-		printf("}\n");
+	ops->postamble();
 
 	return 0;
 }
@@ -611,7 +803,7 @@  static void vis_usage(void)
 	printf("Usage: batadv-vis [options]\n");
 	printf("  -i, --interface             specify the batman-adv interface configured on the system (default: bat0)\n");
 	printf("  -s, --server                start up in server mode, which regularly updates vis data from batman-adv\n");
-	printf("  -f, --format <format>       specify the output format for client mode (either \"json\" or \"dot\")\n");
+	printf("  -f, --format <format>       specify the output format for client mode (either \"json\", \"jsondoc\" or \"dot\")\n");
 	printf("  -v, --version               print the version\n");
 	printf("  -h, --help                  this help\n");
 	printf("\n");
@@ -649,6 +841,8 @@  static struct globals *vis_init(int argc, char *argv[])
 		case 'f':
 			if (strncmp(optarg, "dot", 3) == 0)
 				globals->vis_format = FORMAT_DOT;
+			else if (strncmp(optarg, "jsondoc", 7) == 0)
+				globals->vis_format = FORMAT_JSONDOC;
 			else if (strncmp(optarg, "json", 4) == 0)
 				globals->vis_format = FORMAT_JSON;
 			else {
diff --git a/vis/vis.h b/vis/vis.h
index 6ba70c6..4ccbdad 100644
--- a/vis/vis.h
+++ b/vis/vis.h
@@ -49,7 +49,8 @@  enum opmode {
 
 enum vis_format {
 	FORMAT_DOT,
-	FORMAT_JSON
+	FORMAT_JSON,
+	FORMAT_JSONDOC,
 };
 
 struct vis_iface {