fixeria has submitted this change. ( https://gerrit.osmocom.org/c/erlang/osmo-s1gw/+/41103?usp=email )
Change subject: [REST] Implement EnbErabList ......................................................................
[REST] Implement EnbErabList
Change-Id: I846974dd79e737acaac17918d18da55967a9abf2 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, 323 insertions(+), 1 deletion(-)
Approvals: osmith: Looks good to me, but someone else must approve laforge: Looks good to me, but someone else must approve Jenkins Builder: Verified fixeria: Looks good to me, approved
diff --git a/contrib/openapi.yaml b/contrib/openapi.yaml index 24bc2c8..0041274 100644 --- a/contrib/openapi.yaml +++ b/contrib/openapi.yaml @@ -92,6 +92,22 @@ '404': description: Unsuccessful outcome (eNB not found)
+ /enb/{EnbId}/erab-list: + get: + summary: Get E-RAB list for a specific eNB + operationId: EnbErabList + parameters: + - $ref: '#/components/parameters/EnbId' + responses: + '200': + description: Successful outcome (E-RAB list) + content: + application/json: + schema: + $ref: '#/components/schemas/ErabList' + '404': + description: Unsuccessful outcome (eNB not found) + components: responses: OperationResult: @@ -248,3 +264,62 @@ mme_sctp_aid: type: integer description: SCTP association identifier of the S1GW-MME connection + + ErabList: + type: array + items: + $ref: '#/components/schemas/ErabItem' + + FTEID: + type: object + required: + - teid + - tla + properties: + teid: + type: integer + description: GTP-U TEID + tla: + type: string + description: GTP-U TLA (Transport Layer Address) + + ErabItem: + type: object + required: + - mme_ue_id + - erab_id + - state + - pid + properties: + mme_ue_id: + type: integer + description: MME-UE-S1AP-ID + erab_id: + type: integer + description: E-RAB-ID + state: + type: string + description: E-RAB FSM state + example: erab_wait_release_rsp + pid: + type: string + pattern: '^<[0-9]+.[0-9]+.[0-9]+>$' + description: E-RAB FSM process ID + pfcp_lseid: + type: integer + description: PFCP SEID (local) + pfcp_rseid: + type: integer + description: PFCP SEID (remote) + f_teid_u2c: + $ref: '#/components/schemas/FTEID' + description: GTP-U F-TEID for UPF -> Core + f_teid_c2u: + $ref: '#/components/schemas/FTEID' + description: GTP-U F-TEID for UPF <- Core + f_teid_a2u: + $ref: '#/components/schemas/FTEID' + description: GTP-U F-TEID for UPF <- Access + f_teid_u2a: + $ref: '#/components/schemas/FTEID' + description: GTP-U F-TEID for UPF -> Access diff --git a/contrib/osmo-s1gw-cli.py b/contrib/osmo-s1gw-cli.py index 56d08c3..5b749aa 100755 --- a/contrib/osmo-s1gw-cli.py +++ b/contrib/osmo-s1gw-cli.py @@ -112,6 +112,11 @@ with self.send_get_req(f'enb/{enb_id}') as f: return json.load(f)
+ def enb_erab_list(self, enb_id: str) -> RESTResponse: + ''' EnbErabList :: Get E-RAB list for a specific eNB ''' + with self.send_get_req(f'enb/{enb_id}/erab-list') as f: + return json.load(f) +
class OsmoS1GWCli(cmd2.Cmd): DESC = 'Interactive CLI for OsmoS1GW' @@ -283,6 +288,51 @@ data = self.iface.enb_info(enb_id) self.enb_info_print(data)
+ @staticmethod + def erab_list_item(item: dict) -> dict: + ''' Generate a table row for the given E-RAB (brief) ''' + return { + 'PID': item.get('pid'), + 'MME-UE-S1AP-ID': item.get('mme_ue_id'), + 'E-RAB-ID': item.get('erab_id'), + 'State': item.get('state'), + } + + @classmethod + def erab_list_item_full(cls, item: dict) -> dict: + ''' Generate a table row for the given E-RAB (full) ''' + f_teid = lambda params: '0x{teid:08x}@{tla}'.format(**params) + seid = lambda val: f'0x{val:016x}' + return { + **cls.erab_list_item(item), + 'SEID (local)': seid(item.get('pfcp_lseid')) if 'pfcp_lseid' in item else None, + 'SEID (remote)': seid(item.get('pfcp_rseid')) if 'pfcp_rseid' in item else None, + 'U2C F-TEID': f_teid(item.get('f_teid_u2c')) if 'f_teid_u2c' in item else None, + 'C2U F-TEID': f_teid(item.get('f_teid_c2u')) if 'f_teid_c2u' in item else None, + 'A2U F-TEID': f_teid(item.get('f_teid_a2u')) if 'f_teid_a2u' in item else None, + 'U2A F-TEID': f_teid(item.get('f_teid_u2a')) if 'f_teid_u2a' in item else None, + } + + def erab_list_print(self, items: list[dict], full: bool) -> None: + ''' Print a list of E-RABs in tabular form ''' + func = self.erab_list_item_full if full else self.erab_list_item + self.poutput(tabulate.tabulate(map(func, items), + headers='keys', tablefmt=self.tablefmt)) + + enb_erab_list_parser = cmd2.Cmd2ArgumentParser() + enb_erab_list_parser.add_argument('-f', '--full', + action='store_true', + help='Print full table (more columns)') + add_enb_id_group(enb_erab_list_parser) + + @cmd2.with_argparser(enb_erab_list_parser) + @cmd2.with_category(CAT_ENB) + def do_enb_erab_list(self, opts) -> None: + ''' Get E-RAB list for a specific eNB ''' + enb_id = self.gen_enb_id(opts) + data = self.iface.enb_erab_list(enb_id) + self.erab_list_print(data, opts.full) +
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 3ae2da6..6509563 100644 --- a/doc/osmo-s1gw-cli.md +++ b/doc/osmo-s1gw-cli.md @@ -236,3 +236,35 @@ EXCEPTION of type 'HTTPError' occurred with message: HTTP Error 400: Bad Request To enable full traceback, run the following command: 'set debug true' ``` + +### `enb_erab_list` + +Get E-RAB list for a specific eNB. + +``` +Usage: enb_erab_list [-h] [-f] (-H HANDLE | -P PID | -G GENBID | --enb-sctp-aid AID | --mme-sctp-aid AID) + +Get E-RAB list for a specific eNB + +optional arguments: + -h, --help show this help message and exit + -f, --full Print full table (more columns) + +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 association identifier (example: 42) + --mme-sctp-aid AID MME association identifier (example: 42) +``` + +Example: Obtaining E-RAB list for an eNB with the given Global-eNB-ID. + +``` +OsmoS1GW# enb_erab_list -G 001-01-0 +| PID | MME-UE-S1AP-ID | E-RAB-ID | State | +|-----------|------------------|------------|------------| +| <0.708.0> | 4242 | 1 | erab_setup | +| <0.707.0> | 4242 | 0 | erab_setup | +| <0.709.0> | 4242 | 2 | erab_setup | +``` diff --git a/priv/openapi.json b/priv/openapi.json index 3e8f22e..07a11b8 100644 --- a/priv/openapi.json +++ b/priv/openapi.json @@ -140,6 +140,32 @@ } } } + }, + "/enb/{EnbId}/erab-list": { + "get": { + "summary": "Get E-RAB list for a specific eNB", + "operationId": "EnbErabList", + "parameters": [ + { + "$ref": "#/components/parameters/EnbId" + } + ], + "responses": { + "200": { + "description": "Successful outcome (E-RAB list)", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErabList" + } + } + } + }, + "404": { + "description": "Unsuccessful outcome (eNB not found)" + } + } + } } }, "components": { @@ -358,6 +384,82 @@ "description": "SCTP association identifier of the S1GW-MME connection" } } + }, + "ErabList": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ErabItem" + } + }, + "FTEID": { + "type": "object", + "required": [ + "teid", + "tla" + ], + "properties": { + "teid": { + "type": "integer", + "description": "GTP-U TEID" + }, + "tla": { + "type": "string", + "description": "GTP-U TLA (Transport Layer Address)" + } + } + }, + "ErabItem": { + "type": "object", + "required": [ + "mme_ue_id", + "erab_id", + "state", + "pid" + ], + "properties": { + "mme_ue_id": { + "type": "integer", + "description": "MME-UE-S1AP-ID" + }, + "erab_id": { + "type": "integer", + "description": "E-RAB-ID" + }, + "state": { + "type": "string", + "description": "E-RAB FSM state", + "example": "erab_wait_release_rsp" + }, + "pid": { + "type": "string", + "pattern": "^<[0-9]+\.[0-9]+\.[0-9]+>$", + "description": "E-RAB FSM process ID" + }, + "pfcp_lseid": { + "type": "integer", + "description": "PFCP SEID (local)" + }, + "pfcp_rseid": { + "type": "integer", + "description": "PFCP SEID (remote)" + }, + "f_teid_u2c": { + "$ref": "#/components/schemas/FTEID", + "description": "GTP-U F-TEID for UPF -> Core" + }, + "f_teid_c2u": { + "$ref": "#/components/schemas/FTEID", + "description": "GTP-U F-TEID for UPF <- Core" + }, + "f_teid_a2u": { + "$ref": "#/components/schemas/FTEID", + "description": "GTP-U F-TEID for UPF <- Access" + }, + "f_teid_u2a": { + "$ref": "#/components/schemas/FTEID", + "description": "GTP-U F-TEID for UPF -> Access" + } + } } } } diff --git a/src/rest_server.erl b/src/rest_server.erl index f546ad9..2f805da 100644 --- a/src/rest_server.erl +++ b/src/rest_server.erl @@ -38,7 +38,8 @@ pfcp_assoc_state/1, pfcp_heartbeat/1, enb_list/1, - enb_info/1 + enb_info/1, + enb_erab_list/1 ]).
-include_lib("kernel/include/logger.hrl"). @@ -105,6 +106,20 @@ end.
+%% EnbErabList :: Get E-RAB list for a specific eNB +enb_erab_list(#{path_parameters := PP}) -> + [{<< "EnbId" >>, << ID/bytes >>}] = PP, + case fetch_enb_info(ID) of + [EnbInfo | _] -> + Rsp = fetch_erab_list(EnbInfo), + {200, [], Rsp}; + [] -> + {404, [], undefined}; + error -> + {500, [], undefined} + end. + + %% ------------------------------------------------------------------ %% private API %% ------------------------------------------------------------------ @@ -230,6 +245,54 @@ error.
+-spec fetch_erab_list(enb_registry:enb_info()) -> [map()]. +fetch_erab_list(#{mme_conn_info := ConnInfo}) -> + Pid = maps:get(handler, ConnInfo), %% s1ap_proxy process pid + ERABs = s1ap_proxy:fetch_erab_list(Pid), + lists:map(fun erab_list_item/1, ERABs); + +fetch_erab_list(_) -> []. + + +-spec erab_list_item({term(), pid()}) -> map(). +erab_list_item({_, Pid}) -> + %% XXX: E-RAB FSM process might be dead here + Info = erab_fsm:fetch_info(Pid), + {MmeUeId, ErabId} = maps:get(uid, Info), + M0 = #{mme_ue_id => MmeUeId, + erab_id => ErabId, + state => maps:get(state, Info), + pid => pid_to_list(Pid)}, + M1 = erab_list_item_add_seid(Info, M0), + M2 = erab_list_item_add_f_teid(f_teid_u2c, Info, M1), + M3 = erab_list_item_add_f_teid(f_teid_c2u, Info, M2), + M4 = erab_list_item_add_f_teid(f_teid_a2u, Info, M3), + M5 = erab_list_item_add_f_teid(f_teid_u2a, Info, M4), + rsp_map(M5). + + +-spec erab_list_item_add_seid(erab_fsm:erab_info(), map()) -> map(). +erab_list_item_add_seid(Info, M0) -> + %% local SEID is always known/present + M1 = M0#{pfcp_lseid => maps:get(seid_loc, Info)}, + %% remote SEID may or may not be known/present + case maps:find(seid_rem, Info) of + {ok, SEID} -> M1#{pfcp_rseid => SEID}; + error -> M1 + end. + + +-spec erab_list_item_add_f_teid(atom(), erab_fsm:erab_info(), map()) -> map(). +erab_list_item_add_f_teid(Key, Info, M0) -> + case maps:find(Key, Info) of + {ok, {TEID, AddrBin}} -> + Addr = list_to_tuple(binary_to_list(AddrBin)), + M0#{Key => #{teid => TEID, + tla => inet:ntoa(Addr)}}; + error -> M0 + end. + + -spec parse_pid(binary() | list()) -> pid(). parse_pid(Data) when is_binary(Data) -> parse_pid(binary_to_list(Data));