fixeria has submitted this change. ( https://gerrit.osmocom.org/c/erlang/osmo-s1gw/+/42429?usp=email )
Change subject: [REST] Implement eNB/MME selection by addr-port ......................................................................
[REST] Implement eNB/MME selection by addr-port
Add support for selecting an MME or eNB by remote address and port in the REST API and CLI. The selector format is `addr:IP:PORT` for MMEs and `enb-conn:IP:PORT` for eNBs, where IP can be an IPv4 or IPv6 address. The colon is used as the address/port separator.
Change-Id: If02c8de1e1b7214bba868eee35233a79d0704dc5 Related: SYS#7066 --- M contrib/openapi.yaml M contrib/osmo-s1gw-cli.py M doc/manuals/chapters/cli.adoc M priv/openapi.json M src/rest_server.erl 5 files changed, 99 insertions(+), 33 deletions(-)
Approvals: Jenkins Builder: Verified fixeria: Looks good to me, approved pespin: Looks good to me, but someone else must approve
diff --git a/contrib/openapi.yaml b/contrib/openapi.yaml index c50ccce..d07dd21 100644 --- a/contrib/openapi.yaml +++ b/contrib/openapi.yaml @@ -244,9 +244,9 @@ description: MME name example: mme0 - type: string - pattern: '^addr:[0-9a-f:.]+-[0-9]+$' + pattern: '^addr:[0-9a-f:.]+:[0-9]+$' description: MME remote address/port - example: addr:192.168.1.1-36412 + example: addr:192.168.1.1:36412
EnbId: name: EnbId @@ -272,9 +272,9 @@ description: eNB/MME SCTP association identifier example: enb-aid:42 - type: string - pattern: '^enb-conn:[0-9a-f:.]+-[0-9]+$' + pattern: '^enb-conn:[0-9a-f:.]+:[0-9]+$' description: eNB connection address/port - example: enb-conn:192.168.1.1-34650 + example: enb-conn:192.168.1.1:34650
ErabId: name: ErabId diff --git a/contrib/osmo-s1gw-cli.py b/contrib/osmo-s1gw-cli.py index 5c37bdc..51a9c4f 100755 --- a/contrib/osmo-s1gw-cli.py +++ b/contrib/osmo-s1gw-cli.py @@ -117,14 +117,14 @@ with self.send_post_req('mme-list', mme_info) as f: return f.status
- def mme_info(self, name: str) -> RESTResponse: + def mme_info(self, mme_id: str) -> RESTResponse: ''' MmeInfo :: Get information about a specific MME ''' - with self.send_get_req(f'mme/name:{name}') as f: + with self.send_get_req(f'mme/{mme_id}') as f: return json.load(f)
- def mme_delete(self, name: str) -> int: - ''' MmeInfo :: Get information about a specific MME ''' - with self.send_delete_req(f'mme/name:{name}') as f: + def mme_delete(self, mme_id: str) -> int: + ''' MmeDelete :: Delete an MME from the pool ''' + with self.send_delete_req(f'mme/{mme_id}') as f: return f.status
def enb_list(self) -> RESTResponse: @@ -349,6 +349,18 @@ self.iface.mme_add(mme_info)
@staticmethod + def gen_mme_id(opts) -> str: + ''' Generate the MmeId parameter value (for URL) ''' + if opts.name is not None: + return f'name:{opts.name}' + elif opts.addr is not None: + addr, _, port = opts.addr.rpartition(':') + if addr and port.isdigit(): + return f'addr:{opts.addr}' + return f'addr:{opts.addr}:36412' + raise ValueError # shall not happen + + @staticmethod def add_mme_id_group(parser): ''' Add argparse group for the MmeId parameter ''' mme_id_group = parser.add_argument_group('MME ID') @@ -356,7 +368,10 @@ mme_id_group.add_argument('-N', '--name', type=str, help='MME name (example: mme0)') - # TODO: address/port + mme_id_group.add_argument('-a', '--addr', + type=str, metavar='ADDR[:PORT]', + help='MME address with optional port (default: 36412), ' + 'e.g. 192.168.1.1 or 192.168.1.1:36412') return mme_id_group
mme_info_parser = cmd2.Cmd2ArgumentParser() @@ -366,14 +381,14 @@ @cmd2.with_category(CAT_MME) def do_mme_info(self, opts) -> None: ''' Get information about a specific MME ''' - data = self.iface.mme_info(opts.name) + data = self.iface.mme_info(self.gen_mme_id(opts)) self.mme_info_print(data)
@cmd2.with_argparser(mme_info_parser) @cmd2.with_category(CAT_MME) def do_mme_delete(self, opts) -> None: ''' Delete an MME from the pool ''' - self.iface.mme_delete(opts.name) + self.iface.mme_delete(self.gen_mme_id(opts))
@staticmethod def enb_list_item(item: dict) -> dict: @@ -429,6 +444,8 @@ return f'enb-sctp-aid:{opts.enb_sctp_aid}' elif opts.mme_sctp_aid is not None: return f'mme-sctp-aid:{opts.mme_sctp_aid}' + elif opts.enb_conn is not None: + return f'enb-conn:{opts.enb_conn}' raise ValueError # shall not happen
@staticmethod @@ -451,6 +468,9 @@ enb_id_group.add_argument('--mme-sctp-aid', type=int, metavar='AID', help='MME association identifier (example: 42)') + enb_id_group.add_argument('--enb-conn', + type=str, metavar='ADDR:PORT', + help='eNB connection address/port (example: 192.168.1.1:34650)') return enb_id_group
enb_info_parser = cmd2.Cmd2ArgumentParser() diff --git a/doc/manuals/chapters/cli.adoc b/doc/manuals/chapters/cli.adoc index 418cd4b..279c445 100644 --- a/doc/manuals/chapters/cli.adoc +++ b/doc/manuals/chapters/cli.adoc @@ -218,13 +218,15 @@
==== `mme_info`
-Show configuration details for a specific MME, selected by name. +Show configuration details for a specific MME.
---- -Usage: mme_info [-h] -N NAME +Usage: mme_info [-h] (-N NAME | -a ADDR[:PORT])
MME ID: - -N, --name NAME MME name (example: mme0) + -N, --name NAME MME name (example: mme0) + -a, --addr ADDR[:PORT] MME address with optional port (default: 36412), + e.g. 192.168.1.1 or 192.168.1.1:36412 ----
Example: @@ -241,13 +243,15 @@
==== `mme_delete`
-Remove an MME from the pool, selected by name. +Remove an MME from the pool.
---- -Usage: mme_delete [-h] -N NAME +Usage: mme_delete [-h] (-N NAME | -a ADDR[:PORT])
MME ID: - -N, --name NAME MME name (example: mme0) + -N, --name NAME MME name (example: mme0) + -a, --addr ADDR[:PORT] MME address with optional port (default: 36412), + e.g. 192.168.1.1 or 192.168.1.1:36412 ----
Example: @@ -287,14 +291,16 @@
---- Usage: enb_info [-h] (-H HANDLE | -P PID | -G GENBID | - --enb-sctp-aid AID | --mme-sctp-aid AID) + --enb-sctp-aid AID | --mme-sctp-aid AID | + --enb-conn ADDR:PORT)
eNB ID: - -H, --handle HANDLE eNB handle (example: 0) - -P, --pid PID eNB process ID (example: 0.33.1) - -G, --genbid GENBID Global-eNB-ID (example: 262-42-1337) - --enb-sctp-aid AID eNB SCTP association identifier - --mme-sctp-aid AID MME SCTP association identifier + -H, --handle HANDLE eNB handle (example: 0) + -P, --pid PID eNB process ID (example: 0.33.1) + -G, --genbid GENBID Global-eNB-ID (example: 262-42-1337) + --enb-sctp-aid AID eNB SCTP association identifier + --mme-sctp-aid AID MME SCTP association identifier + --enb-conn ADDR:PORT eNB connection address/port (example: 192.168.1.1:34650) ----
Example: selecting by handle. @@ -315,8 +321,8 @@
==== `enb_delete`
-Force-disconnect an eNB, terminating its SCTP connection. The same -selectors as `enb_info` are accepted. +Force-disconnect an eNB, terminating its SCTP connection. Accepts the +same selectors as `enb_info`.
==== `enb_erab_list`
@@ -326,7 +332,8 @@ Usage: enb_erab_list [-h] [-f] [-S {pid,state,mme_ue_id,erab_id}] [--reverse] (-H HANDLE | -P PID | -G GENBID | - --enb-sctp-aid AID | --mme-sctp-aid AID) + --enb-sctp-aid AID | --mme-sctp-aid AID | + --enb-conn ADDR:PORT)
optional arguments: -f, --full Print full table including PFCP and GTP-U F-TEID columns diff --git a/priv/openapi.json b/priv/openapi.json index 8289c02..cab9fc0 100644 --- a/priv/openapi.json +++ b/priv/openapi.json @@ -379,9 +379,9 @@ }, { "type": "string", - "pattern": "^addr:[0-9a-f:.]+-[0-9]+$", + "pattern": "^addr:[0-9a-f:.]+:[0-9]+$", "description": "MME remote address/port", - "example": "addr:192.168.1.1-36412" + "example": "addr:192.168.1.1:36412" } ] } @@ -419,9 +419,9 @@ }, { "type": "string", - "pattern": "^enb-conn:[0-9a-f:.]+-[0-9]+$", + "pattern": "^enb-conn:[0-9a-f:.]+:[0-9]+$", "description": "eNB connection address/port", - "example": "enb-conn:192.168.1.1-34650" + "example": "enb-conn:192.168.1.1:34650" } ] } diff --git a/src/rest_server.erl b/src/rest_server.erl index 550da3e..0beaaca 100644 --- a/src/rest_server.erl +++ b/src/rest_server.erl @@ -272,6 +272,25 @@ <<"swagger_ui">> => osmo_s1gw:get_env(rest_srv_swagger_ui, ?ENV_DEFAULT_REST_SRV_SWAGGER_UI)}.
+%% parse address/port from a string like "192.168.1.1:36412" +-spec parse_addr_port(Data) -> {ok, {Addr, Port}} | error + when Data :: binary() | string(), + Addr :: inet:ip_address(), + Port :: inet:port_number(). +parse_addr_port(Data) when is_binary(Data) -> + parse_addr_port(binary_to_list(Data)); + +parse_addr_port(Data) when is_list(Data) -> + case string:split(Data, ":", trailing) of + [AddrStr, PortStr] -> + case {inet:parse_address(AddrStr), string:to_integer(PortStr)} of + {{ok, Addr}, {Port, []}} -> {ok, {Addr, Port}}; + _ -> error + end; + _ -> error + end. + + -spec sockopts_to_map([gen_sctp:option()]) -> map(). sockopts_to_map(SockOpts) -> maps:from_list([{atom_to_binary(K), V} || {K, V} <- SockOpts]). @@ -368,7 +387,19 @@ MmeName = binary_to_list(Val), mme_registry:fetch_mme_info(MmeName);
-%% TODO: '^addr:[0-9a-f:.]+-[0-9]+$' +fetch_mme_info(<< "addr:", Val/bytes >>) -> + case parse_addr_port(Val) of + {ok, {Addr, Port}} -> + Fun = fun(#{raddr := R, rport := P}) -> R =:= Addr andalso P =:= Port end, + case lists:filter(Fun, mme_registry:fetch_mme_list()) of + [MmeInfo | _] -> {ok, MmeInfo}; + [] -> error + end; + error -> + ?LOG_ERROR("Failed to parse MME addr:port from ~p", [Val]), + error + end; + fetch_mme_info(ID) -> ?LOG_ERROR("Unhandled MME ID ~p", [ID]), error. @@ -454,7 +485,15 @@ Aid = binary_to_integer(Val), enb_registry:fetch_enb_list({mme_sctp_aid, Aid});
-%% TODO: '^enb-conn:[0-9:.]+-[0-9]+$' +fetch_enb_info(<< "enb-conn:", Val/bytes >>) -> + case parse_addr_port(Val) of + {ok, AddrPort} -> + enb_registry:fetch_enb_list({enb_addr_port, AddrPort}); + error -> + ?LOG_ERROR("Failed to parse eNB addr:port from ~p", [Val]), + error + end; + fetch_enb_info(ID) -> ?LOG_ERROR("Unhandled eNB ID ~p", [ID]), error.