fixeria has uploaded this change for review.

View Change

[REST] Implement MetricsList

Change-Id: I43114aa8f5f8a388dd6a436f83ad1ee97f1d2ab1
Related: SYS#7066
---
M contrib/openapi.yaml
M contrib/osmo-s1gw-cli.py
M priv/openapi.json
M src/rest_server.erl
4 files changed, 221 insertions(+), 3 deletions(-)

git pull ssh://gerrit.osmocom.org:29418/erlang/osmo-s1gw refs/changes/99/41099/1
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 7d13b08..9888ee9 100755
--- a/contrib/osmo-s1gw-cli.py
+++ b/contrib/osmo-s1gw-cli.py
@@ -73,10 +73,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)

@@ -99,6 +109,31 @@
spec = self.iface.fetch_spec()
self.poutput(json.dumps(spec, indent=4))

+ @staticmethod
+ def metrics_list_item(item: dict) -> dict:
+ 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/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:

To view, visit change 41099. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-MessageType: newchange
Gerrit-Project: erlang/osmo-s1gw
Gerrit-Branch: master
Gerrit-Change-Id: I43114aa8f5f8a388dd6a436f83ad1ee97f1d2ab1
Gerrit-Change-Number: 41099
Gerrit-PatchSet: 1
Gerrit-Owner: fixeria <vyanitskiy@sysmocom.de>