fixeria has uploaded this change for review. ( https://gerrit.osmocom.org/c/erlang/osmo-s1gw/+/42416?usp=email )
Change subject: [REST] Add GET /config endpoint for reading the current configuration ......................................................................
[REST] Add GET /config endpoint for reading the current configuration
Change-Id: Ic6c9562a541e4a0728257538887537aac6b99b97 Related: SYS#7066 --- M contrib/openapi.yaml M contrib/osmo-s1gw-cli.py M doc/manuals/chapters/cli.adoc M doc/manuals/chapters/rest.adoc A include/osmo_s1gw.hrl M priv/openapi.json M src/osmo_s1gw_sup.erl M src/rest_server.erl 8 files changed, 472 insertions(+), 13 deletions(-)
git pull ssh://gerrit.osmocom.org:29418/erlang/osmo-s1gw refs/changes/16/42416/1
diff --git a/contrib/openapi.yaml b/contrib/openapi.yaml index 6ab1346..c50ccce 100644 --- a/contrib/openapi.yaml +++ b/contrib/openapi.yaml @@ -66,6 +66,18 @@ '200': $ref: '#/components/responses/OperationResult'
+ /config: + get: + summary: Get the current runtime configuration + operationId: ConfigRead + responses: + '200': + description: Current runtime configuration + content: + application/json: + schema: + $ref: '#/components/schemas/Config' + /mme-list: get: summary: Get a list of registered MMEs @@ -427,6 +439,96 @@ type: integer description: SCTP association identifier of the S1GW-MME connection
+ Config: + type: object + properties: + sctp_server: + $ref: '#/components/schemas/SctpServerCfg' + sctp_client: + $ref: '#/components/schemas/SctpClientCfg' + pfcp: + $ref: '#/components/schemas/PfcpCfg' + gtpu_kpi: + $ref: '#/components/schemas/GtpuKpiCfg' + rest: + $ref: '#/components/schemas/RestCfg' + + SctpSockOptsCfg: + type: object + properties: + sctp_nodelay: + type: boolean + description: Disable/enable SCTP Nagle algorithm + recbuf: + type: integer + description: Receive buffer size (bytes) + sndbuf: + type: integer + description: Send buffer size (bytes) + + SctpServerCfg: + type: object + properties: + laddr: + type: string + description: Local (bind) IP address for eNB-facing SCTP server + lport: + type: integer + description: Local (bind) port for eNB-facing SCTP server + sockopts: + $ref: '#/components/schemas/SctpSockOptsCfg' + + SctpClientCfg: + description: > + Legacy/deprecated section kept for backwards compatibility. + MME pool entries are managed via the /mme-list endpoint. + type: object + properties: + laddr: + type: string + description: Local (bind) IP address for MME-facing SCTP connections + raddr: + type: string + description: Default remote (MME) IP address + rport: + type: integer + description: Default remote (MME) port + sockopts: + $ref: '#/components/schemas/SctpSockOptsCfg' + + PfcpCfg: + type: object + properties: + laddr: + type: string + description: Local IP address for PFCP communication + raddr: + type: string + description: Remote IP address of the UPF (PFCP peer) + + GtpuKpiCfg: + type: object + properties: + enable: + type: boolean + description: Whether GTP-U KPI reporting is enabled + table_name: + type: string + description: nftables table name for GTP-U KPI counters + interval: + type: integer + description: KPI polling interval in milliseconds + + RestCfg: + type: object + properties: + port: + type: integer + description: REST server listen port + swagger_ui: + type: boolean + description: Whether the Swagger UI is enabled + ErabList: type: array items: diff --git a/contrib/osmo-s1gw-cli.py b/contrib/osmo-s1gw-cli.py index 265c5fc..5c37bdc 100755 --- a/contrib/osmo-s1gw-cli.py +++ b/contrib/osmo-s1gw-cli.py @@ -69,6 +69,11 @@ ''' Send an HTTP DELETE request to the given endpoint (path) ''' return self.send_req('DELETE', path, data)
+ def config_read(self) -> RESTResponse: + ''' ConfigRead :: Get the current runtime configuration ''' + with self.send_get_req('config') as f: + return json.load(f) + def fetch_spec(self) -> RESTResponse: ''' Fetch the OpenAPI specification (JSON) ''' with self.send_get_req('swagger/spec.json') as f: @@ -161,6 +166,7 @@ class OsmoS1GWCli(cmd2.Cmd): DESC = 'Interactive CLI for OsmoS1GW'
+ CAT_CONFIG = 'Configuration commands' CAT_METRICS = 'Metrics commands' CAT_PFCP = 'PFCP related commands' CAT_MME = 'MME related commands' @@ -184,6 +190,17 @@
self.iface = RestIface(argv.HOST, argv.port)
+ @cmd2.with_category(CAT_CONFIG) + def do_config_show(self, opts) -> None: + ''' Show the current runtime configuration ''' + data = self.iface.config_read() + for section, params in data.items(): + self.poutput(f'\n[{section}]') + table = [[k, v] for k, v in params.items()] + self.poutput(tabulate.tabulate(table, + headers=['Parameter', 'Value'], + tablefmt=self.tablefmt)) + def do_fetch_openapi_spec(self, opts): ''' Fetch the OpenAPI specification (JSON), dump as text ''' spec = self.iface.fetch_spec() diff --git a/doc/manuals/chapters/cli.adoc b/doc/manuals/chapters/cli.adoc index 5d2bd6e..0b9dade 100644 --- a/doc/manuals/chapters/cli.adoc +++ b/doc/manuals/chapters/cli.adoc @@ -48,6 +48,50 @@ [[cli_commands]] === Commands
+==== `config_show` + +Display the effective runtime configuration (see <<rest_config>>). + +Example: + +---- +OsmoS1GW# config_show + +[gtpu_kpi] +| Parameter | Value | +|-------------|-----------| +| enable | False | +| interval | 3000 | +| table_name | osmo-s1gw | + +[pfcp] +| Parameter | Value | +|-------------|------------| +| laddr | 127.0.3.1 | +| raddr | 127.0.3.10 | + +[rest] +| Parameter | Value | +|-------------|---------| +| port | 8080 | +| swagger_ui | True | + +[sctp_client] +| Parameter | Value | +|-------------|----------------------------------------------------------| +| laddr | 127.0.2.1 | +| raddr | 127.0.2.10 | +| rport | 36412 | +| sockopts | {'recbuf': 65536, 'sctp_nodelay': True, 'sndbuf': 65536} | + +[sctp_server] +| Parameter | Value | +|-------------|----------------------------------------------------------| +| laddr | 127.0.1.1 | +| lport | 36412 | +| sockopts | {'recbuf': 65536, 'sctp_nodelay': True, 'sndbuf': 65536} | +---- + ==== `fetch_openapi_spec`
Fetch the OpenAPI specification (JSON) from the server and display it. diff --git a/doc/manuals/chapters/rest.adoc b/doc/manuals/chapters/rest.adoc index 2707a41..2518070 100644 --- a/doc/manuals/chapters/rest.adoc +++ b/doc/manuals/chapters/rest.adoc @@ -49,6 +49,72 @@ | Process ID | `pid:<x>.<y>.<z>` | `pid:0.33.1` |===
+[[rest_config]] +=== Configuration + +==== `GET /config` — Runtime Configuration + +Returns the effective runtime configuration that OsmoS1GW is currently +using, with all defaults applied. This reflects the values read via +`osmo_s1gw:get_env/2` at startup, which may differ from the raw +`sys.config` if legacy environment variables were merged in. + +Response (HTTP 200): + +---- +{ + "sctp_server": { + "laddr": "127.0.1.1", + "lport": 36412, + "sockopts": {"recbuf": 65536, "sndbuf": 65536, "sctp_nodelay": true} + }, + "sctp_client": { + "laddr": "127.0.2.1", + "raddr": "127.0.2.10", + "rport": 36412, + "sockopts": {"recbuf": 65536, "sndbuf": 65536, "sctp_nodelay": true} + }, + "pfcp": { + "laddr": "127.0.1.1", + "raddr": "127.0.1.2" + }, + "gtpu_kpi": { + "enable": false, + "table_name": "osmo-s1gw", + "interval": 3000 + }, + "rest": { + "port": 8080, + "swagger_ui": true + } +} +---- + +`sctp_server`:: Configuration of the eNB-facing SCTP server. + `laddr`::: Local bind address. + `lport`::: Local bind port. + `sockopts`::: Effective SCTP socket options, including defaults. + +`sctp_client`:: Legacy/deprecated section reflecting the MME-facing SCTP + client defaults. MME pool entries are managed via <<rest_mme>>. + `laddr`::: Local bind address. + `raddr`::: Default remote (MME) address. + `rport`::: Default remote (MME) port. + `sockopts`::: Effective SCTP socket options, including defaults. + +`pfcp`:: + `laddr`::: Local PFCP bind address. + `raddr`::: Remote UPF (PFCP peer) address. + +`gtpu_kpi`:: + `enable`::: Whether GTP-U KPI reporting via nftables counters is active. + `table_name`::: nftables table name used for GTP-U KPI counters. + `interval`::: KPI polling interval in milliseconds. + +`rest`:: + `port`::: REST server listen port. + `swagger_ui`::: Whether the Swagger UI endpoint is enabled. + [[rest_metrics]] === Metrics
diff --git a/include/osmo_s1gw.hrl b/include/osmo_s1gw.hrl new file mode 100644 index 0000000..f9cbe14 --- /dev/null +++ b/include/osmo_s1gw.hrl @@ -0,0 +1,50 @@ +%% Copyright (C) 2025 by sysmocom - s.f.m.c. GmbH info@sysmocom.de +%% Author: Vadim Yanitskiy vyanitskiy@sysmocom.de +%% +%% All Rights Reserved +%% +%% SPDX-License-Identifier: AGPL-3.0-or-later +%% +%% This program is free software; you can redistribute it and/or modify +%% it under the terms of the GNU Affero General Public License as +%% published by the Free Software Foundation; either version 3 of the +%% License, or (at your option) any later version. +%% +%% This program is distributed in the hope that it will be useful, +%% but WITHOUT ANY WARRANTY; without even the implied warranty of +%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +%% GNU General Public License for more details. +%% +%% You should have received a copy of the GNU Affero General Public License +%% along with this program. If not, see https://www.gnu.org/licenses/. +%% +%% Additional Permission under GNU AGPL version 3 section 7: +%% +%% If you modify this Program, or any covered work, by linking or +%% combining it with runtime libraries of Erlang/OTP as released by +%% Ericsson on https://www.erlang.org (or a modified version of these +%% libraries), containing parts covered by the terms of the Erlang Public +%% License (https://www.erlang.org/EPLICENSE), the licensors of this +%% Program grant you additional permission to convey the resulting work +%% without the need to license the runtime libraries of Erlang/OTP under +%% the GNU Affero General Public License. Corresponding Source for a +%% non-source form of such a combination shall include the source code +%% for the parts of the runtime libraries of Erlang/OTP used as well as +%% that of the covered work. + +%% Default values for application environment parameters (osmo_s1gw:get_env/2) + +-define(ENV_DEFAULT_S1GW_BIND_ADDR, "127.0.1.1"). +-define(ENV_DEFAULT_S1GW_BIND_PORT, 36412). +-define(ENV_DEFAULT_MME_LOC_ADDR, "127.0.2.1"). +-define(ENV_DEFAULT_MME_REM_ADDR, "127.0.2.10"). +-define(ENV_DEFAULT_MME_REM_PORT, 36412). +-define(ENV_DEFAULT_PFCP_LOC_ADDR, "127.0.1.1"). +-define(ENV_DEFAULT_PFCP_REM_ADDR, "127.0.1.2"). +-define(ENV_DEFAULT_GTPU_KPI_ENABLE, false). +-define(ENV_DEFAULT_GTPU_KPI_TABLE_NAME, "osmo-s1gw"). +-define(ENV_DEFAULT_GTPU_KPI_INTERVAL, 3000). +-define(ENV_DEFAULT_REST_SRV_PORT, 8080). +-define(ENV_DEFAULT_REST_SRV_SWAGGER_UI, true). + +%% vim:set ts=4 sw=4 et: diff --git a/priv/openapi.json b/priv/openapi.json index 87aafac..8289c02 100644 --- a/priv/openapi.json +++ b/priv/openapi.json @@ -100,6 +100,24 @@ } } }, + "/config": { + "get": { + "summary": "Get the current runtime configuration", + "operationId": "ConfigRead", + "responses": { + "200": { + "description": "Current runtime configuration", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Config" + } + } + } + } + } + } + }, "/mme-list": { "get": { "summary": "Get a list of registered MMEs", @@ -630,6 +648,123 @@ } } }, + "Config": { + "type": "object", + "properties": { + "sctp_server": { + "$ref": "#/components/schemas/SctpServerCfg" + }, + "sctp_client": { + "$ref": "#/components/schemas/SctpClientCfg" + }, + "pfcp": { + "$ref": "#/components/schemas/PfcpCfg" + }, + "gtpu_kpi": { + "$ref": "#/components/schemas/GtpuKpiCfg" + }, + "rest": { + "$ref": "#/components/schemas/RestCfg" + } + } + }, + "SctpSockOptsCfg": { + "type": "object", + "properties": { + "sctp_nodelay": { + "type": "boolean", + "description": "Disable/enable SCTP Nagle algorithm" + }, + "recbuf": { + "type": "integer", + "description": "Receive buffer size (bytes)" + }, + "sndbuf": { + "type": "integer", + "description": "Send buffer size (bytes)" + } + } + }, + "SctpServerCfg": { + "type": "object", + "properties": { + "laddr": { + "type": "string", + "description": "Local (bind) IP address for eNB-facing SCTP server" + }, + "lport": { + "type": "integer", + "description": "Local (bind) port for eNB-facing SCTP server" + }, + "sockopts": { + "$ref": "#/components/schemas/SctpSockOptsCfg" + } + } + }, + "SctpClientCfg": { + "description": "Legacy/deprecated section kept for backwards compatibility. MME pool entries are managed via the /mme-list endpoint.\n", + "type": "object", + "properties": { + "laddr": { + "type": "string", + "description": "Local (bind) IP address for MME-facing SCTP connections" + }, + "raddr": { + "type": "string", + "description": "Default remote (MME) IP address" + }, + "rport": { + "type": "integer", + "description": "Default remote (MME) port" + }, + "sockopts": { + "$ref": "#/components/schemas/SctpSockOptsCfg" + } + } + }, + "PfcpCfg": { + "type": "object", + "properties": { + "laddr": { + "type": "string", + "description": "Local IP address for PFCP communication" + }, + "raddr": { + "type": "string", + "description": "Remote IP address of the UPF (PFCP peer)" + } + } + }, + "GtpuKpiCfg": { + "type": "object", + "properties": { + "enable": { + "type": "boolean", + "description": "Whether GTP-U KPI reporting is enabled" + }, + "table_name": { + "type": "string", + "description": "nftables table name for GTP-U KPI counters" + }, + "interval": { + "type": "integer", + "description": "KPI polling interval in milliseconds" + } + } + }, + "RestCfg": { + "type": "object", + "properties": { + "port": { + "type": "integer", + "description": "REST server listen port" + }, + "swagger_ui": { + "type": "boolean", + "description": "Whether the Swagger UI is enabled" + } + } + }, "ErabList": { "type": "array", "items": { diff --git a/src/osmo_s1gw_sup.erl b/src/osmo_s1gw_sup.erl index 875e19b..502ba04 100644 --- a/src/osmo_s1gw_sup.erl +++ b/src/osmo_s1gw_sup.erl @@ -38,21 +38,10 @@ -export([init/1, start_link/0]).
+-include("osmo_s1gw.hrl"). -include("s1ap.hrl").
-define(SERVER, ?MODULE). --define(ENV_DEFAULT_S1GW_BIND_ADDR, "127.0.1.1"). --define(ENV_DEFAULT_S1GW_BIND_PORT, ?S1AP_PORT). --define(ENV_DEFAULT_MME_LOC_ADDR, "127.0.2.1"). --define(ENV_DEFAULT_MME_REM_ADDR, "127.0.2.10"). --define(ENV_DEFAULT_MME_REM_PORT, ?S1AP_PORT). --define(ENV_DEFAULT_PFCP_LOC_ADDR, "127.0.1.1"). --define(ENV_DEFAULT_PFCP_REM_ADDR, "127.0.1.2"). --define(ENV_DEFAULT_GTPU_KPI_ENABLE, false). --define(ENV_DEFAULT_GTPU_KPI_TABLE_NAME, "osmo-s1gw"). --define(ENV_DEFAULT_GTPU_KPI_INTERVAL, 3000). --define(ENV_DEFAULT_REST_SRV_PORT, 8080). --define(ENV_DEFAULT_REST_SRV_SWAGGER_UI, true).
%% ------------------------------------------------------------------ %% supervisor API diff --git a/src/rest_server.erl b/src/rest_server.erl index 79090e8..dae276f 100644 --- a/src/rest_server.erl +++ b/src/rest_server.erl @@ -34,7 +34,8 @@
-module(rest_server).
--export([metrics_list/1, +-export([config_read/1, + metrics_list/1, pfcp_assoc_state/1, pfcp_heartbeat/1, mme_list/1, @@ -52,6 +53,7 @@
-include_lib("kernel/include/logger.hrl").
+-include("osmo_s1gw.hrl"). -include("s1gw_metrics.hrl").
@@ -59,6 +61,17 @@ %% public API %% ------------------------------------------------------------------
+%% ConfigRead :: Get the current runtime configuration +config_read(#{}) -> + {200, [], #{ + <<"sctp_server">> => config_sctp_server(), + <<"sctp_client">> => config_sctp_client(), + <<"pfcp">> => config_pfcp(), + <<"gtpu_kpi">> => config_gtpu_kpi(), + <<"rest">> => config_rest() + }}. + + %% MetricsList :: Get a list of metrics metrics_list(#{query_parameters := QP}) -> L0 = case proplists:get_value(<< "type" >>, QP, << "all" >>) of @@ -221,6 +234,49 @@ %% private API %% ------------------------------------------------------------------
+-spec config_sctp_server() -> map(). +config_sctp_server() -> + Cfg = osmo_s1gw:get_env(sctp_server, #{}), + SockOptsCfg = maps:get(sockopts, Cfg, #{}), + #{<<"laddr">> => bval(maps:get(laddr, Cfg, ?ENV_DEFAULT_S1GW_BIND_ADDR)), + <<"lport">> => maps:get(lport, Cfg, ?ENV_DEFAULT_S1GW_BIND_PORT), + <<"sockopts">> => sockopts_to_map(sctp_common:gen_sockopts(SockOptsCfg))}. + + +-spec config_sctp_client() -> map(). +config_sctp_client() -> + Cfg = osmo_s1gw:get_env(sctp_client, #{}), + SockOpts = maps:get(sockopts, Cfg, []), + #{<<"laddr">> => bval(maps:get(laddr, Cfg, ?ENV_DEFAULT_MME_LOC_ADDR)), + <<"raddr">> => bval(maps:get(raddr, Cfg, ?ENV_DEFAULT_MME_REM_ADDR)), + <<"rport">> => maps:get(rport, Cfg, ?ENV_DEFAULT_MME_REM_PORT), + <<"sockopts">> => sockopts_to_map(SockOpts)}. + + +-spec config_pfcp() -> map(). +config_pfcp() -> + #{<<"laddr">> => bval(osmo_s1gw:get_env(pfcp_loc_addr, ?ENV_DEFAULT_PFCP_LOC_ADDR)), + <<"raddr">> => bval(osmo_s1gw:get_env(pfcp_rem_addr, ?ENV_DEFAULT_PFCP_REM_ADDR))}. + + +-spec config_gtpu_kpi() -> map(). +config_gtpu_kpi() -> + #{<<"enable">> => osmo_s1gw:get_env(gtpu_kpi_enable, ?ENV_DEFAULT_GTPU_KPI_ENABLE), + <<"table_name">> => bval(osmo_s1gw:get_env(gtpu_kpi_table_name, ?ENV_DEFAULT_GTPU_KPI_TABLE_NAME)), + <<"interval">> => osmo_s1gw:get_env(gtpu_kpi_interval, ?ENV_DEFAULT_GTPU_KPI_INTERVAL)}. + + +-spec config_rest() -> map(). +config_rest() -> + #{<<"port">> => osmo_s1gw:get_env(rest_srv_port, ?ENV_DEFAULT_REST_SRV_PORT), + <<"swagger_ui">> => osmo_s1gw:get_env(rest_srv_swagger_ui, ?ENV_DEFAULT_REST_SRV_SWAGGER_UI)}. + + +-spec sockopts_to_map([gen_sctp:option()]) -> map(). +sockopts_to_map(SockOpts) -> + maps:from_list([{atom_to_binary(K), V} || {K, V} <- SockOpts]). + + -spec metric_list(list()) -> [map()]. metric_list(Path) -> L = exometer:get_values(Path),