fixeria has submitted this change. ( https://gerrit.osmocom.org/c/erlang/osmo-s1gw/+/41099?usp=email )
Change subject: [REST] Implement MetricsList ......................................................................
[REST] Implement MetricsList
Change-Id: I43114aa8f5f8a388dd6a436f83ad1ee97f1d2ab1 Related: SYS#7066 --- M contrib/openapi.yaml M contrib/osmo-s1gw-cli.py M doc/osmo-s1gw-cli.md M priv/openapi.json M src/rest_server.erl 5 files changed, 265 insertions(+), 3 deletions(-)
Approvals: fixeria: Looks good to me, approved Jenkins Builder: Verified osmith: Looks good to me, but someone else must approve laforge: Looks good to me, but someone else must approve
diff --git a/contrib/openapi.yaml b/contrib/openapi.yaml index c7f7ec8..696e7a2 100644 --- a/contrib/openapi.yaml +++ b/contrib/openapi.yaml @@ -4,5 +4,52 @@ title: 'OsmoS1GW REST interface'
paths: + /metrics-list: + get: + summary: Get a list of metrics + operationId: MetricsList + parameters: + - name: type + in: query + description: Metric type + required: false + schema: + type: string + enum: [all, counter, gauge] + default: all + - name: path + in: query + description: Metrics path + required: false + schema: + type: string + example: s1ap.proxy + responses: + '200': + description: A list of metrics + content: + application/json: + schema: + $ref: '#/components/schemas/MetricsList'
components: + schemas: + MetricsList: + type: array + items: + $ref: '#/components/schemas/MetricsItem' + + MetricsItem: + type: object + required: + - type + - name + - value + properties: + type: + type: string + enum: [counter, gauge] + name: + type: string + value: + type: integer diff --git a/contrib/osmo-s1gw-cli.py b/contrib/osmo-s1gw-cli.py index 8007314..76c444e 100755 --- a/contrib/osmo-s1gw-cli.py +++ b/contrib/osmo-s1gw-cli.py @@ -74,10 +74,20 @@ with self.send_get_req('swagger/spec.json') as f: return json.load(f)
+ def metrics_list(self, type: str = 'all', path: str = '') -> RESTResponse: + ''' MetricsList :: Get a list of metrics ''' + query = {'type' : type} + if path: + query['path'] = path + with self.send_get_req('metrics-list', query) as f: + return json.load(f) +
class OsmoS1GWCli(cmd2.Cmd): DESC = 'Interactive CLI for OsmoS1GW'
+ CAT_METRICS = 'Metrics commands' + def __init__(self, argv): super().__init__(allow_cli_args=False, include_py=True)
@@ -100,6 +110,32 @@ spec = self.iface.fetch_spec() self.poutput(json.dumps(spec, indent=4))
+ @staticmethod + def metrics_list_item(item: dict) -> dict: + ''' Generate a table row for the given metric ''' + return { + 'Name': item.get('name'), + 'Type': item.get('type'), + 'Value': item.get('value'), + } + + metrics_list_parser = cmd2.Cmd2ArgumentParser() + metrics_list_parser.add_argument('-t', '--type', + type=str, default='all', + choices=('all', 'counter', 'gauge'), + help='Metric type (default: %(default)s)') + metrics_list_parser.add_argument('PATH', + type=str, default='', nargs='?', + help='Metric path') + + @cmd2.with_argparser(metrics_list_parser) + @cmd2.with_category(CAT_METRICS) + def do_metrics_list(self, opts) -> None: + ''' Get a list of metrics ''' + data = self.iface.metrics_list(opts.type, opts.PATH) + self.poutput(tabulate.tabulate(map(self.metrics_list_item, data), + headers='keys', tablefmt=self.tablefmt)) +
ap = argparse.ArgumentParser(prog='osmo-s1gw-cli', description=OsmoS1GWCli.DESC)
diff --git a/doc/osmo-s1gw-cli.md b/doc/osmo-s1gw-cli.md index 9cbac7b..d5ceb94 100644 --- a/doc/osmo-s1gw-cli.md +++ b/doc/osmo-s1gw-cli.md @@ -81,3 +81,46 @@ ### `fetch_openapi_spec`
Fetch the OpenAPI specification (JSON), dump as text. + +### `metrics_list` + +Get a list of metrics. + +``` +Usage: metrics_list [-h] [-t {all, counter, gauge}] [PATH] + +Get a list of metrics + +positional arguments: + PATH Metric path + +optional arguments: + -h, --help show this help message and exit + -t, --type {all, counter, gauge} + Metric type (default: all) +``` + +Example: Obtaining PFCP association related metrics. + +``` +OsmoS1GW# metrics_list pfcp.assoc +| Name | Type | Value | +|-------------------------------|---------|---------| +| pfcp.assoc_setup_req.timeout | counter | 21 | +| pfcp.assoc_setup_req.tx | counter | 22 | +| pfcp.assoc_setup_resp.rx | counter | 0 | +| pfcp.assoc_setup_resp.rx_ack | counter | 0 | +| pfcp.assoc_setup_resp.rx_nack | counter | 0 | +| pfcp.associated | gauge | 0 | +``` + +Example: Obtaining all gauges. + +``` +OsmoS1GW# metrics_list --type gauge +| Name | Type | Value | +|----------------------------------|--------|---------| +| pfcp.associated | gauge | 0 | +| s1ap.enb.num_sctp_connections | gauge | 0 | +| s1ap.proxy.uplink_packets_queued | gauge | 0 | +``` diff --git a/priv/openapi.json b/priv/openapi.json index 25ff62a..7323e9a 100644 --- a/priv/openapi.json +++ b/priv/openapi.json @@ -4,6 +4,84 @@ "version": "0.1.0", "title": "OsmoS1GW REST interface" }, - "paths": null, - "components": null + "paths": { + "/metrics-list": { + "get": { + "summary": "Get a list of metrics", + "operationId": "MetricsList", + "parameters": [ + { + "name": "type", + "in": "query", + "description": "Metric type", + "required": false, + "schema": { + "type": "string", + "enum": [ + "all", + "counter", + "gauge" + ], + "default": "all" + } + }, + { + "name": "path", + "in": "query", + "description": "Metrics path", + "required": false, + "schema": { + "type": "string", + "example": "s1ap.proxy" + } + } + ], + "responses": { + "200": { + "description": "A list of metrics", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MetricsList" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "MetricsList": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MetricsItem" + } + }, + "MetricsItem": { + "type": "object", + "required": [ + "type", + "name", + "value" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "counter", + "gauge" + ] + }, + "name": { + "type": "string" + }, + "value": { + "type": "integer" + } + } + } + } + } } \ No newline at end of file diff --git a/src/rest_server.erl b/src/rest_server.erl index c4e0373..ee0a7de 100644 --- a/src/rest_server.erl +++ b/src/rest_server.erl @@ -34,19 +34,77 @@
-module(rest_server).
--export([]). +-export([metrics_list/1 + ]).
-include_lib("kernel/include/logger.hrl").
+-include("s1gw_metrics.hrl"). +
%% ------------------------------------------------------------------ %% public API %% ------------------------------------------------------------------
+%% MetricsList :: Get a list of metrics +metrics_list(#{query_parameters := QP}) -> + L0 = case proplists:get_value(<< "type" >>, QP, << "all" >>) of + << "counter" >> -> metric_list([ctr]); + << "gauge" >> -> metric_list([gauge]); + << "all" >> -> metric_list([]) + end, + L1 = case proplists:get_value(<< "path" >>, QP) of + undefined -> L0; + Path -> lists:filter(fun(M) -> metric_filter_path(M, Path) end, L0) + end, + {200, [], L1}. +
%% ------------------------------------------------------------------ %% private API %% ------------------------------------------------------------------
+-spec metric_list(list()) -> [map()]. +metric_list(Path) -> + L = exometer:get_values(Path), + lists:map(fun metric_item/1, L). + + +-spec metric_item({Name, Props}) -> map() + when Name :: s1gw_metrics:metric(), + Props :: proplists:proplist(). +metric_item({Name, Props}) -> + Value = proplists:get_value(value, Props), + #{<< "type" >> => metric_type(Name), + << "name" >> => metric_name(Name), + << "value" >> => Value}. + + +-spec metric_type(s1gw_metrics:metric()) -> binary(). +metric_type([ctr | _]) -> << "counter" >>; +metric_type([gauge | _]) -> << "gauge" >>. + + +-spec metric_filter_path(map(), binary()) -> boolean(). +metric_filter_path(#{<< "name" >> := Name}, Path) -> + Len = erlang:min(byte_size(Name), byte_size(Path)), + binary:part(Name, 0, Len) =:= Path. + + +-spec metric_name(s1gw_metrics:metric()) -> binary(). +metric_name([_Type | P0]) -> + %% turn each list member into a string + P1 = lists:map(fun thing_to_list/1, P0), + %% put the dot-separator in between + P2 = string:join(P1, "."), + list_to_binary(P2). + + +%% stolen from exometer_report_statsd.git +thing_to_list(X) when is_atom(X) -> atom_to_list(X); +thing_to_list(X) when is_integer(X) -> integer_to_list(X); +thing_to_list(X) when is_binary(X) -> X; +thing_to_list(X) when is_list(X) -> X. +
%% vim:set ts=4 sw=4 et: