This may be used to output the JSON schema which describes the output of
show info json and show stats json.

The JSON output is without any extra whitespace in order to reduce the
volume of output. For human consumption passing the output through a
pretty printer may be helpful.

e.g.:
$ echo "show schema json" | socat /var/run/haproxy.stat stdio | \
     python -m json.tool

The implementation does not generate the schema. Some consideration could
be given to integrating the output of the schema with the output of
typed and json info and stats. In particular the types (u32, s64, etc...)
and tags.

A sample verification of show info json and show stats json using
the schema is as follows. It uses the jsonschema python module:

cat > jschema.py <<  __EOF__
import json

from jsonschema import validate
from jsonschema.validators import Draft3Validator

with open('schema.txt', 'r') as f:
    schema = json.load(f)
    Draft3Validator.check_schema(schema)

    with open('instance.txt', 'r') as f:
        instance = json.load(f)
        validate(instance, schema, Draft3Validator)
__EOF__

$ echo "show schema json" | socat /var/run/haproxy.stat stdio > schema.txt
$ echo "show info json" | socat /var/run/haproxy.stat stdio > instance.txt
python ./jschema.py
$ echo "show stats json" | socat /var/run/haproxy.stat stdio > instance.txt
python ./jschema.py

Signed-off-by: Simon Horman <ho...@verge.net.au>
---

In this case the pretty printer increases the size of the output by
about 200% illustrating the value of output without whitespace.

  $ echo "show schema json" | socat /var/run/haproxy.stat stdio | wc -c
  2690
  $ echo "show schema json" | socat /var/run/haproxy.stat stdio | \
  python -m json.tool | wc -c
  8587

Changes since RFC:
* Add errors to schema and use in the case where output exceeds
  available buffer space
* Document that consideration should be given to updating
  schema function if struct field is updated
* Correct typos
* Register "show", "schema", json" rather than "show", "schema".
  This allows the parse callback to be omitted and some simplification
  of the dump callback.
* Limit integer values to the range [-(2**53)+1, (2**53)-1] as
  per the recommendation for interoperable integers in
  section 6 of RFC 7159.
---
 doc/management.txt    |  33 ++++++-
 include/types/stats.h |   5 +-
 src/stats.c           | 234 ++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 268 insertions(+), 4 deletions(-)

diff --git a/doc/management.txt b/doc/management.txt
index 623ac6375552..70af03b07271 100644
--- a/doc/management.txt
+++ b/doc/management.txt
@@ -1849,7 +1849,14 @@ show info [typed|json]
       (...)
 
   The format of JSON output is described in a schema which may be output
-  using "show schema json" (to be implemented).
+  using "show schema json".
+
+  The JSON output contains no extra whitespace in order to reduce the
+  volume of output. For human consumption passing the output through a
+  pretty printer may be helpful. Example :
+
+  $ echo "show info json" | socat /var/run/haproxy.sock stdio | \
+    python -m json.tool
 
   The JSON output contains no extra whitespace in order to reduce the
   volume of output. For human consumption passing the output through a
@@ -2128,7 +2135,14 @@ show stat [{<iid>|<proxy>} <type> <sid>] [typed|json]
         (...)
 
   The format of JSON output is described in a schema which may be output
-  using "show schema json" (to be implemented).
+  using "show schema json".
+
+  The JSON output contains no extra whitespace in order to reduce the
+  volume of output. For human consumption passing the output through a
+  pretty printer may be helpful. Example :
+
+  $ echo "show stat json" | socat /var/run/haproxy.sock stdio | \
+    python -m json.tool
 
   The JSON output contains no extra whitespace in order to reduce the
   volume of output. For human consumption passing the output through a
@@ -2237,6 +2251,21 @@ show tls-keys [id|*]
   specified as parameter, it will dump the tickets, using * it will dump every
   keys from every references.
 
+show schema json
+  Dump the schema used for the output of "show info json" and "show stat json".
+
+  The contains no extra whitespace in order to reduce the volume of output.
+  For human consumption passing the output through a pretty printer may be
+  helpful. Example :
+
+  $ echo "show schema json" | socat /var/run/haproxy.sock stdio | \
+    python -m json.tool
+
+  The schema follows "JSON Schema" (json-schema.org) and accordingly
+  verifiers may be used to verify the output of "show info json" and "show
+  stat json" against the schema.
+
+
 shutdown frontend <frontend>
   Completely delete the specified frontend. All the ports it was bound to will
   be released. It will not be possible to enable the frontend anymore after
diff --git a/include/types/stats.h b/include/types/stats.h
index aad694c203c3..70224687123b 100644
--- a/include/types/stats.h
+++ b/include/types/stats.h
@@ -215,8 +215,9 @@ enum field_scope {
        FS_MASK     = 0xFF000000,
 };
 
-/* Please consider updating stats_dump_fields_*() and
- * stats_dump_.*_info_fields() when modifying struct field or related enums.
+/* Please consider updating stats_dump_fields_*(),
+ * stats_dump_.*_info_fields() and stats_*_schema()
+ * when modifying struct field or related enums.
  */
 struct field {
        uint32_t type;
diff --git a/src/stats.c b/src/stats.c
index 0f226fca2c2e..1038f76f073e 100644
--- a/src/stats.c
+++ b/src/stats.c
@@ -3299,6 +3299,234 @@ static int stats_dump_info_to_buffer(struct 
stream_interface *si)
        return 1;
 }
 
+/* This function dumps the schema onto the stream interface's read buffer.
+ * It returns 0 as long as it does not complete, non-zero upon completion.
+ * No state is used.
+ *
+ * Integer values bouned to the range [-(2**53)+1, (2**53)-1] as
+ * per the recommendation for interoperable integers in section 6 of RFC 7159.
+ */
+static void stats_dump_json_schema(struct chunk *out)
+{
+
+       int old_len = out->len;
+
+       chunk_strcat(out,
+                    "{"
+                     "\"$schema\":\"http://json-schema.org/draft-04/schema#\",";
+                     "\"oneOf\":["
+                      "{"
+                       "\"title\":\"Info\","
+                       "\"type\":\"array\","
+                       "\"items\":{"
+                        "\"properties\":{"
+                         "\"title\":\"InfoItem\","
+                         "\"type\":\"object\","
+                         "\"field\":{\"$ref\":\"#/definitions/field\"},"
+                         
"\"processNum\":{\"$ref\":\"#/definitions/processNum\"},"
+                         "\"tags\":{\"$ref\":\"#/definitions/tags\"},"
+                         "\"value\":{\"$ref\":\"#/definitions/typedValue\"}"
+                        "},"
+                        "\"required\":[\"field\",\"processNum\",\"tags\","
+                                      "\"value\"]"
+                       "}"
+                      "},"
+                      "{"
+                       "\"title\":\"Stat\","
+                       "\"type\":\"array\","
+                       "\"items\":{"
+                        "\"title\":\"InfoItem\","
+                        "\"type\":\"object\","
+                        "\"properties\":{"
+                         "\"objType\":{"
+                          "\"enum\":[\"Frontend\",\"Backend\",\"Listener\","
+                                    "\"Server\",\"Unknown\"]"
+                         "},"
+                         "\"proxyId\":{"
+                          "\"type\":\"integer\","
+                          "\"minimum\":0"
+                         "},"
+                         "\"id\":{"
+                          "\"type\":\"integer\","
+                          "\"minimum\":0"
+                         "},"
+                         "\"field\":{\"$ref\":\"#/definitions/field\"},"
+                         
"\"processNum\":{\"$ref\":\"#/definitions/processNum\"},"
+                         "\"tags\":{\"$ref\":\"#/definitions/tags\"},"
+                         
"\"typedValue\":{\"$ref\":\"#/definitions/typedValue\"}"
+                        "},"
+                        "\"required\":[\"objType\",\"proxyId\",\"id\","
+                                      "\"field\",\"processNum\",\"tags\","
+                                      "\"value\"]"
+                       "}"
+                      "},"
+                      "{"
+                       "\"title\":\"Error\","
+                       "\"type\":\"object\","
+                       "\"properties\":{"
+                        "\"errorStr\":{"
+                         "\"type\":\"string\""
+                        "},"
+                        "\"required\":[\"errorStr\"]"
+                       "}"
+                      "}"
+                     "],"
+                     "\"definitions\":{"
+                      "\"field\":{"
+                       "\"type\":\"object\","
+                       "\"pos\":{"
+                        "\"type\":\"integer\","
+                        "\"minimum\":0"
+                       "},"
+                       "\"name\":{"
+                        "\"type\":\"string\""
+                       "},"
+                       "\"required\":[\"pos\",\"name\"]"
+                      "},"
+                      "\"processNum\":{"
+                       "\"type\":\"integer\","
+                       "\"minimum\":1"
+                      "},"
+                      "\"tags\":{"
+                       "\"type\":\"object\","
+                       "\"origin\":{"
+                        "\"type\":\"string\","
+                        "\"enum\":[\"Metric\",\"Status\",\"Key\","
+                                  "\"Config\",\"Product\",\"Unknown\"]"
+                       "},"
+                       "\"nature\":{"
+                        "\"type\":\"string\","
+                        "\"enum\":[\"Gauge\",\"Limit\",\"Min\",\"Max\","
+                                  "\"Rate\",\"Counter\",\"Duration\","
+                                  "\"Age\",\"Time\",\"Name\",\"Output\","
+                                  "\"Avg\", \"Unknown\"]"
+                       "},"
+                       "\"scope\":{"
+                        "\"type\":\"string\","
+                        "\"enum\":[\"Cluster\",\"Process\",\"Service\","
+                                  "\"System\",\"Unknown\"]"
+                       "},"
+                       "\"required\":[\"origin\",\"nature\",\"scope\"]"
+                      "},"
+                      "\"typedValue\":{"
+                       "\"type\":\"object\","
+                       "\"oneOf\":["
+                        
"{\"$ref\":\"#/definitions/typedValue/definitions/s32Value\"},"
+                        
"{\"$ref\":\"#/definitions/typedValue/definitions/s64Value\"},"
+                        
"{\"$ref\":\"#/definitions/typedValue/definitions/u32Value\"},"
+                        
"{\"$ref\":\"#/definitions/typedValue/definitions/u64Value\"},"
+                        
"{\"$ref\":\"#/definitions/typedValue/definitions/strValue\"}"
+                       "],"
+                       "\"definitions\":{"
+                        "\"s32Value\":{"
+                         "\"properties\":{"
+                          "\"type\":{"
+                           "\"type\":\"string\","
+                           "\"enum\":[\"s32\"]"
+                          "},"
+                          "\"value\":{"
+                           "\"type\":\"integer\","
+                           "\"minimum\":-2147483648,"
+                           "\"maximum\":2147483647"
+                          "}"
+                         "},"
+                         "\"required\":[\"type\",\"value\"]"
+                        "},"
+                        "\"s64Value\":{"
+                         "\"properties\":{"
+                          "\"type\":{"
+                           "\"type\":\"string\","
+                           "\"enum\":[\"s64\"]"
+                          "},"
+                          "\"value\":{"
+                           "\"type\":\"integer\","
+                           "\"minimum\":-9007199254740991,"
+                           "\"maximum\":9007199254740991"
+                          "}"
+                         "},"
+                         "\"required\":[\"type\",\"value\"]"
+                        "},"
+                        "\"u32Value\":{"
+                         "\"properties\":{"
+                          "\"type\":{"
+                           "\"type\":\"string\","
+                           "\"enum\":[\"u32\"]"
+                          "},"
+                          "\"value\":{"
+                           "\"type\":\"integer\","
+                           "\"minimum\":0,"
+                           "\"maximum\":4294967295"
+                          "}"
+                         "},"
+                         "\"required\":[\"type\",\"value\"]"
+                        "},"
+                        "\"u64Value\":{"
+                         "\"properties\":{"
+                          "\"type\":{"
+                           "\"type\":\"string\","
+                           "\"enum\":[\"u64\"]"
+                          "},"
+                          "\"value\":{"
+                           "\"type\":\"integer\","
+                           "\"minimum\":0,"
+                           "\"maximum\":9007199254740991"
+                          "}"
+                         "},"
+                         "\"required\":[\"type\",\"value\"]"
+                        "},"
+                        "\"strValue\":{"
+                         "\"properties\":{"
+                          "\"type\":{"
+                           "\"type\":\"string\","
+                           "\"enum\":[\"str\"]"
+                          "},"
+                          "\"value\":{\"type\":\"string\"}"
+                         "},"
+                         "\"required\":[\"type\",\"value\"]"
+                        "},"
+                        "\"unknownValue\":{"
+                         "\"properties\":{"
+                          "\"type\":{"
+                           "\"type\":\"integer\","
+                           "\"minimum\":0"
+                          "},"
+                          "\"value\":{"
+                           "\"type\":\"string\","
+                           "\"enum\":[\"unknown\"]"
+                          "}"
+                         "},"
+                         "\"required\":[\"type\",\"value\"]"
+                        "}"
+                       "}"
+                      "}"
+                     "}"
+                    "}");
+
+       if (old_len == out->len) {
+               chunk_reset(out);
+               chunk_appendf(out,
+                             "{\"errorStr\":\"output buffer too short\"}");
+       }
+}
+
+/* This function dumps the schema onto the stream interface's read buffer.
+ * It returns 0 as long as it does not complete, non-zero upon completion.
+ * No state is used.
+ */
+static int stats_dump_json_schema_to_buffer(struct stream_interface *si)
+{
+       chunk_reset(&trash);
+
+       stats_dump_json_schema(&trash);
+
+       if (bi_putchk(si_ic(si), &trash) == -1) {
+               si_applet_cant_put(si);
+               return 0;
+       }
+
+       return 1;
+}
+
 static int cli_parse_clear_counters(char **args, struct appctx *appctx, void 
*private)
 {
        struct proxy *px;
@@ -3420,11 +3648,17 @@ static int cli_io_handler_dump_stat(struct appctx 
*appctx)
        return stats_dump_stat_to_buffer(appctx->owner, NULL);
 }
 
+static int cli_io_handler_dump_json_schema(struct appctx *appctx)
+{
+       return stats_dump_json_schema_to_buffer(appctx->owner);
+}
+
 /* register cli keywords */
 static struct cli_kw_list cli_kws = {{ },{
        { { "clear", "counters",  NULL }, "clear counters : clear max 
statistics counters (add 'all' for all counters)", cli_parse_clear_counters, 
NULL, NULL },
        { { "show", "info",  NULL }, "show info      : report information about 
the running process", cli_parse_show_info, cli_io_handler_dump_info, NULL },
        { { "show", "stat",  NULL }, "show stat      : report counters for each 
proxy and server", cli_parse_show_stat, cli_io_handler_dump_stat, NULL },
+       { { "show", "schema",  "json", NULL }, "show schema json : report 
schema used for stats", NULL, cli_io_handler_dump_json_schema, NULL },
        {{},}
 }};
 
-- 
2.7.0.rc3.207.g0ac5344


Reply via email to