laforge has submitted this change. (
https://gerrit.osmocom.org/c/erlang/osmo-s1gw/+/41035?usp=email )
Change subject: enb_registry: track eNB uptime
......................................................................
enb_registry: track eNB uptime
Now that we have the global eNB registry, it makes more sense to
perform uptime tracking there instead of spawning a separate
enb_uptime process for each eNB connection.
Change-Id: I94fd06e559ae52d4d9c8b22e618e48dff718b53c
Related: SYS#7594, SYS#7066
---
M src/enb_registry.erl
D src/enb_uptime.erl
M src/s1ap_proxy.erl
3 files changed, 59 insertions(+), 109 deletions(-)
Approvals:
Jenkins Builder: Verified
laforge: Looks good to me, approved
pespin: Looks good to me, but someone else must approve
diff --git a/src/enb_registry.erl b/src/enb_registry.erl
index 4495d44..f2be559 100644
--- a/src/enb_registry.erl
+++ b/src/enb_registry.erl
@@ -51,6 +51,11 @@
-include_lib("kernel/include/logger.hrl").
+-include("s1gw_metrics.hrl").
+
+%% Heartbeat interval
+-define(HEARTBEAT_INTERVAL, 5_000).
+
-type enb_handle() :: non_neg_integer().
@@ -73,6 +78,7 @@
mon_ref := reference(), %% monitor()
reference
state := enb_state(), %% connection state
reg_time := integer(), %% registration time
(monotonic)
+ uptime := non_neg_integer(), %% seconds since
reg_time
genb_id_str => string(), %% Global-eNB-ID
enb_id => s1ap_proxy:enb_id(), %% eNB-ID
plmn_id => s1ap_proxy:plmn_id(), %% PLMN-ID
@@ -141,6 +147,7 @@
%% ------------------------------------------------------------------
init([]) ->
+ spawn_link(fun() -> heartbeat(?HEARTBEAT_INTERVAL) end),
{ok, #state{enbs = maps:new(),
pids = maps:new(),
next_handle = 0}}.
@@ -163,7 +170,8 @@
pid => Pid,
mon_ref => MonRef,
state => connecting,
- reg_time => erlang:monotonic_time()},
+ reg_time => erlang:monotonic_time(),
+ uptime => 0},
?LOG_INFO("eNB (handle=~p, pid ~p) registered", [Handle, Pid]),
{reply, {ok, Handle}, S#state{enbs = ENBs#{Handle => EnbInfo},
pids = PIDs#{Pid => Handle},
@@ -178,6 +186,7 @@
{ok, #{pid := Pid,
mon_ref := MonRef}} ->
erlang:demonitor(MonRef, [flush]),
+ enb_metrics_reset(maps:get(Handle, ENBs)),
?LOG_INFO("eNB (handle=~p) unregistered", [Handle]),
{reply, ok, S#state{enbs = maps:remove(Handle, ENBs),
pids = maps:remove(Pid, PIDs)}};
@@ -227,12 +236,19 @@
{ok, EnbInfo0} ->
?LOG_INFO("eNB (handle=~p) event: ~p", [Handle, Event]),
EnbInfo1 = enb_handle_event(EnbInfo0, Event),
+ enb_metrics_register(EnbInfo1),
{noreply, S#state{enbs = maps:update(Handle, EnbInfo1, ENBs)}};
error ->
?LOG_ERROR("eNB (handle=~p) is *not* registered", [Handle]),
{noreply, S}
end;
+handle_cast(heartbeat,
+ #state{enbs = ENBs} = S) ->
+ T = erlang:monotonic_time(),
+ Fun = fun(Handle, EnbInfo) -> enb_report_uptime(Handle, EnbInfo, T) end,
+ {noreply, S#state{enbs = maps:map(Fun, ENBs)}};
+
handle_cast(Info, S) ->
?LOG_ERROR("unknown ~p(): ~p", [?FUNCTION_NAME, Info]),
{noreply, S}.
@@ -245,6 +261,7 @@
case maps:find(Pid, PIDs) of
{ok, Pid} ->
Handle = maps:get(Pid, PIDs),
+ enb_metrics_reset(maps:get(Handle, ENBs)),
?LOG_INFO("eNB (handle=~p, pid=~p) has been unregistered", [Handle,
Pid]),
{noreply, S#state{enbs = maps:remove(Handle, ENBs),
pids = maps:remove(Pid, PIDs)}};
@@ -267,6 +284,20 @@
%% private API
%% ------------------------------------------------------------------
+-spec enb_metrics_register(enb_info()) -> term().
+enb_metrics_register(#{genb_id_str := GlobalENBId}) ->
+ catch exometer:new(?S1GW_CTR_ENB_UPTIME(GlobalENBId), counter);
+
+enb_metrics_register(_) -> ok.
+
+
+-spec enb_metrics_reset(enb_info()) -> term().
+enb_metrics_reset(#{genb_id_str := GlobalENBId}) ->
+ s1gw_metrics:ctr_reset(?S1GW_CTR_ENB_UPTIME(GlobalENBId));
+
+enb_metrics_reset(_) -> ok.
+
+
-spec enb_handle_event(enb_info(), enb_event()) -> enb_info().
enb_handle_event(EnbInfo, {connecting, ConnInfo}) ->
EnbInfo#{state => connecting,
@@ -316,4 +347,28 @@
maps:get(Field, M, undefined) =:= Value.
+-spec enb_report_uptime(Handle, EnbInfo, T1) -> enb_info()
+ when Handle :: enb_handle(),
+ EnbInfo :: enb_info(),
+ T1 :: integer().
+enb_report_uptime(Handle, #{reg_time := T0} = EnbInfo, T1) ->
+ Uptime = erlang:convert_time_unit(T1 - T0, native, second),
+ ?LOG_DEBUG("eNB (handle=~p) uptime ~p s", [Handle, Uptime]),
+ %% update ?S1GW_CTR_ENB_UPTIME
+ case EnbInfo of
+ #{genb_id_str := GlobalENBId} ->
+ Current = s1gw_metrics:get_current_value(?S1GW_CTR_ENB_UPTIME(GlobalENBId)),
+ s1gw_metrics:ctr_inc(?S1GW_CTR_ENB_UPTIME(GlobalENBId), Uptime - Current);
+ _ -> nop
+ end,
+ EnbInfo#{uptime => Uptime}.
+
+
+-spec heartbeat(timeout()) -> no_return().
+heartbeat(Tval) ->
+ timer:sleep(Tval),
+ gen_server:cast(?MODULE, ?FUNCTION_NAME),
+ heartbeat(Tval). %% keep going
+
+
%% vim:set ts=4 sw=4 et:
diff --git a/src/enb_uptime.erl b/src/enb_uptime.erl
deleted file mode 100644
index ac71ebd..0000000
--- a/src/enb_uptime.erl
+++ /dev/null
@@ -1,100 +0,0 @@
-%% Copyright (C) 2025 by sysmocom - s.f.m.c. GmbH <info(a)sysmocom.de>
-%% Author: Vadim Yanitskiy <vyanitskiy(a)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.
-
--module(enb_uptime).
-
--export([start_link/0,
- genb_id_ind/2,
- shutdown/1]).
-
--include_lib("kernel/include/logger.hrl").
-
--include("s1gw_metrics.hrl").
-
--define(INTERVAL, 5_000).
-
-
-%% ------------------------------------------------------------------
-%% public API
-%% ------------------------------------------------------------------
-
--spec start_link() -> pid().
-start_link() ->
- T0 = erlang:monotonic_time(),
- spawn_link(fun() -> loop(#{time => T0}) end).
-
-
--spec genb_id_ind(pid(), string()) -> ok.
-genb_id_ind(Pid, GlobalENBId) ->
- Pid ! {?FUNCTION_NAME, GlobalENBId},
- ok.
-
-
--spec shutdown(pid()) -> ok.
-shutdown(Pid) ->
- Pid ! stop,
- ok.
-
-
-%% ------------------------------------------------------------------
-%% private API
-%% ------------------------------------------------------------------
-
-loop(#{genb_id := GlobalENBId, time := T0} = S) ->
- receive
- stop ->
- s1gw_metrics:ctr_reset(?S1GW_CTR_ENB_UPTIME(GlobalENBId)),
- ok;
- Event ->
- ?LOG_ERROR("Rx unexpected event: ~p", [Event])
- after ?INTERVAL ->
- T1 = erlang:monotonic_time(),
- Diff = erlang:convert_time_unit(T1 - T0, native, second),
- s1gw_metrics:ctr_inc(?S1GW_CTR_ENB_UPTIME(GlobalENBId), Diff),
- loop(S#{time => T1})
- end;
-
-loop(#{} = S) ->
- receive
- {genb_id_ind, GlobalENBId} ->
- catch exometer:new(?S1GW_CTR_ENB_UPTIME(GlobalENBId), counter),
- loop(S#{genb_id => GlobalENBId});
- stop ->
- ok;
- Event ->
- ?LOG_ERROR("Rx unexpected event: ~p", [Event])
- end.
-
-
-%% vim:set ts=4 sw=4 et:
diff --git a/src/s1ap_proxy.erl b/src/s1ap_proxy.erl
index 252e237..bcba952 100644
--- a/src/s1ap_proxy.erl
+++ b/src/s1ap_proxy.erl
@@ -82,8 +82,7 @@
mme_ue_id :: undefined | mme_ue_id(),
enb_ue_id :: undefined | enb_ue_id(),
erab_id :: undefined | erab_id(),
- path :: [s1ap_ie_id()],
- enb_uptime :: pid()
+ path :: [s1ap_ie_id()]
}).
-type proxy_state() :: #proxy_state{}.
@@ -147,8 +146,7 @@
init([EnbHandle, ConnInfo]) ->
process_flag(trap_exit, true),
- {ok, #proxy_state{enb_uptime = enb_uptime:start_link(),
- enb_handle = EnbHandle,
+ {ok, #proxy_state{enb_handle = EnbHandle,
conn_info = ConnInfo,
erabs = dict:new(),
path = []}}.
@@ -193,8 +191,7 @@
{noreply, S}.
-terminate(Reason, #proxy_state{} = S) ->
- enb_uptime:shutdown(S#proxy_state.enb_uptime),
+terminate(Reason, #proxy_state{}) ->
?LOG_NOTICE("Terminating, reason ~p", [Reason]),
ok.
@@ -407,8 +404,6 @@
enb_registry:enb_event(S#proxy_state.enb_handle,
{s1setup, enb_info(S)}),
gtpu_kpi_enb_register(S),
- enb_uptime:genb_id_ind(S#proxy_state.enb_uptime,
- S#proxy_state.genb_id_str),
%% there's nothing to patch in this PDU, so we forward it as-is
{forward, S};
--
To view, visit
https://gerrit.osmocom.org/c/erlang/osmo-s1gw/+/41035?usp=email
To unsubscribe, or for help writing mail filters, visit
https://gerrit.osmocom.org/settings?usp=email
Gerrit-MessageType: merged
Gerrit-Project: erlang/osmo-s1gw
Gerrit-Branch: master
Gerrit-Change-Id: I94fd06e559ae52d4d9c8b22e618e48dff718b53c
Gerrit-Change-Number: 41035
Gerrit-PatchSet: 5
Gerrit-Owner: fixeria <vyanitskiy(a)sysmocom.de>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: jolly <andreas(a)eversberg.eu>
Gerrit-Reviewer: laforge <laforge(a)osmocom.org>
Gerrit-Reviewer: pespin <pespin(a)sysmocom.de>