MEDIUM: stats: Add show json schema

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 <horms@verge.net.au>
This commit is contained in:
Simon Horman 2017-01-04 09:37:26 +01:00 committed by Willy Tarreau
parent 05ee213f8b
commit 6f6bb380ef
3 changed files with 268 additions and 4 deletions

View File

@ -1858,7 +1858,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
@ -2137,7 +2144,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
@ -2246,6 +2260,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

View File

@ -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;

View File

@ -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 },
{{},}
}};