Merge pull request #7823 from EMQ-YangM/authz_add_metrics
feat: new authz metrics
This commit is contained in:
commit
7061d94cf9
|
@ -393,4 +393,171 @@ Filter supports the following placeholders:
|
||||||
zh: """查询语句"""
|
zh: """查询语句"""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#==== metrics field
|
||||||
|
|
||||||
|
metrics {
|
||||||
|
desc {
|
||||||
|
en: """The metrics of the resource."""
|
||||||
|
zh: """资源统计指标。"""
|
||||||
|
}
|
||||||
|
label: {
|
||||||
|
en: """Metrics"""
|
||||||
|
zh: """指标"""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
node_metrics {
|
||||||
|
desc {
|
||||||
|
en: """The metrics of the resource for each node."""
|
||||||
|
zh: """每个节点上资源的统计指标。"""
|
||||||
|
}
|
||||||
|
label: {
|
||||||
|
en: """Resource Metrics in Node"""
|
||||||
|
zh: """节点资源指标"""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
status {
|
||||||
|
desc {
|
||||||
|
en: """The status of the resource."""
|
||||||
|
zh: """资源状态。"""
|
||||||
|
}
|
||||||
|
label: {
|
||||||
|
en: """Status"""
|
||||||
|
zh: """状态"""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
node_status {
|
||||||
|
desc {
|
||||||
|
en: """The status of the resource for each node."""
|
||||||
|
zh: """每个节点上资源的状态。"""
|
||||||
|
}
|
||||||
|
label: {
|
||||||
|
en: """Resource Status in Node"""
|
||||||
|
zh: """节点资源状态"""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
node_error {
|
||||||
|
desc {
|
||||||
|
en: """The error of node."""
|
||||||
|
zh: """节点上产生的错误。"""
|
||||||
|
}
|
||||||
|
label: {
|
||||||
|
en: """Error in Node"""
|
||||||
|
zh: """节点产生的错误"""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
matched {
|
||||||
|
desc {
|
||||||
|
en: """Count of this resource is queried."""
|
||||||
|
zh: """请求命中次数。"""
|
||||||
|
}
|
||||||
|
label: {
|
||||||
|
en: """Matched"""
|
||||||
|
zh: """已命中"""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
success {
|
||||||
|
desc {
|
||||||
|
en: """Count of query success."""
|
||||||
|
zh: """请求成功次数。"""
|
||||||
|
}
|
||||||
|
label: {
|
||||||
|
en: """Success"""
|
||||||
|
zh: """成功"""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
failed {
|
||||||
|
desc {
|
||||||
|
en: """Count of query failed."""
|
||||||
|
zh: """请求失败次数。"""
|
||||||
|
}
|
||||||
|
label: {
|
||||||
|
en: """Failed"""
|
||||||
|
zh: """失败"""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rate {
|
||||||
|
desc {
|
||||||
|
en: """The rate of matched, times/second."""
|
||||||
|
zh: """命中速率,单位:次/秒。"""
|
||||||
|
}
|
||||||
|
label: {
|
||||||
|
en: """Rate"""
|
||||||
|
zh: """速率"""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rate_max {
|
||||||
|
desc {
|
||||||
|
en: """The max rate of matched, times/second."""
|
||||||
|
zh: """最大命中速率,单位:次/秒。"""
|
||||||
|
}
|
||||||
|
label: {
|
||||||
|
en: """Max Rate"""
|
||||||
|
zh: """最大速率"""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rate_last5m {
|
||||||
|
desc {
|
||||||
|
en: """The average rate of matched in the last 5 minutes, times/second."""
|
||||||
|
zh: """5分钟内平均命中速率,单位:次/秒。"""
|
||||||
|
}
|
||||||
|
label: {
|
||||||
|
en: """Rate in Last 5min"""
|
||||||
|
zh: """5分钟内速率"""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
node {
|
||||||
|
desc {
|
||||||
|
en: """Node name."""
|
||||||
|
zh: """节点名称。"""
|
||||||
|
}
|
||||||
|
label: {
|
||||||
|
en: """Node Name."""
|
||||||
|
zh: """节点名称。"""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ignore {
|
||||||
|
desc {
|
||||||
|
en: """Not match any rules."""
|
||||||
|
zh: """没有匹配到任何规则。"""
|
||||||
|
}
|
||||||
|
label: {
|
||||||
|
en: """Not Match Any Rules"""
|
||||||
|
zh: """没有匹配到任何规则。"""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
allow {
|
||||||
|
desc {
|
||||||
|
en: """Authorize allow."""
|
||||||
|
zh: """鉴权成功。"""
|
||||||
|
}
|
||||||
|
label: {
|
||||||
|
en: """Authorize Allow"""
|
||||||
|
zh: """鉴权成功"""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deny {
|
||||||
|
desc {
|
||||||
|
en: """Authorize Deny."""
|
||||||
|
zh: """鉴权失败"""
|
||||||
|
}
|
||||||
|
label: {
|
||||||
|
en: """Authorize Deny"""
|
||||||
|
zh: """鉴权失败"""
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -171,6 +171,14 @@ do_post_config_update({?CMD_MOVE, _Type, _Where} = Cmd, _Sources) ->
|
||||||
do_move(Cmd, InitedSources);
|
do_move(Cmd, InitedSources);
|
||||||
do_post_config_update({?CMD_PREPEND, RawNewSource}, Sources) ->
|
do_post_config_update({?CMD_PREPEND, RawNewSource}, Sources) ->
|
||||||
InitedNewSource = init_source(get_source_by_type(type(RawNewSource), Sources)),
|
InitedNewSource = init_source(get_source_by_type(type(RawNewSource), Sources)),
|
||||||
|
%% create metrics
|
||||||
|
TypeName = type(RawNewSource),
|
||||||
|
ok = emqx_plugin_libs_metrics:create_metrics(
|
||||||
|
authz_metrics,
|
||||||
|
TypeName,
|
||||||
|
[matched, allow, deny, ignore],
|
||||||
|
[matched]
|
||||||
|
),
|
||||||
[InitedNewSource] ++ lookup();
|
[InitedNewSource] ++ lookup();
|
||||||
do_post_config_update({?CMD_APPEND, RawNewSource}, Sources) ->
|
do_post_config_update({?CMD_APPEND, RawNewSource}, Sources) ->
|
||||||
InitedNewSource = init_source(get_source_by_type(type(RawNewSource), Sources)),
|
InitedNewSource = init_source(get_source_by_type(type(RawNewSource), Sources)),
|
||||||
|
@ -185,6 +193,8 @@ do_post_config_update({{?CMD_REPLACE, Type}, RawNewSource}, Sources) ->
|
||||||
do_post_config_update({{?CMD_DELETE, Type}, _RawNewSource}, _Sources) ->
|
do_post_config_update({{?CMD_DELETE, Type}, _RawNewSource}, _Sources) ->
|
||||||
OldInitedSources = lookup(),
|
OldInitedSources = lookup(),
|
||||||
{OldSource, Front, Rear} = take(Type, OldInitedSources),
|
{OldSource, Front, Rear} = take(Type, OldInitedSources),
|
||||||
|
%% delete metrics
|
||||||
|
ok = emqx_plugin_libs_metrics:clear_metrics(authz_metrics, Type),
|
||||||
ok = ensure_resource_deleted(OldSource),
|
ok = ensure_resource_deleted(OldSource),
|
||||||
clear_certs(OldSource),
|
clear_certs(OldSource),
|
||||||
Front ++ Rear;
|
Front ++ Rear;
|
||||||
|
@ -247,6 +257,7 @@ init_sources(Sources) ->
|
||||||
true -> ?SLOG(info, #{msg => "disabled_sources_ignored", sources => Disabled});
|
true -> ?SLOG(info, #{msg => "disabled_sources_ignored", sources => Disabled});
|
||||||
false -> ok
|
false -> ok
|
||||||
end,
|
end,
|
||||||
|
ok = lists:foreach(fun init_metrics/1, Sources),
|
||||||
lists:map(fun init_source/1, Sources).
|
lists:map(fun init_source/1, Sources).
|
||||||
|
|
||||||
init_source(#{enable := false} = Source) ->
|
init_source(#{enable := false} = Source) ->
|
||||||
|
@ -255,6 +266,15 @@ init_source(#{type := Type} = Source) ->
|
||||||
Module = authz_module(Type),
|
Module = authz_module(Type),
|
||||||
Module:init(Source).
|
Module:init(Source).
|
||||||
|
|
||||||
|
init_metrics(Source) ->
|
||||||
|
TypeName = type(Source),
|
||||||
|
emqx_plugin_libs_metrics:create_metrics(
|
||||||
|
authz_metrics,
|
||||||
|
TypeName,
|
||||||
|
[matched, allow, deny, ignore],
|
||||||
|
[matched]
|
||||||
|
).
|
||||||
|
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
%% AuthZ callbacks
|
%% AuthZ callbacks
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
|
@ -290,6 +310,7 @@ authorize(
|
||||||
ipaddr => IpAddress,
|
ipaddr => IpAddress,
|
||||||
topic => Topic
|
topic => Topic
|
||||||
}),
|
}),
|
||||||
|
emqx_plugin_libs_metrics:inc(authz_metrics, AuthzSource, allow),
|
||||||
emqx_metrics:inc(?METRIC_ALLOW),
|
emqx_metrics:inc(?METRIC_ALLOW),
|
||||||
{stop, allow};
|
{stop, allow};
|
||||||
{{matched, deny}, AuthzSource} ->
|
{{matched, deny}, AuthzSource} ->
|
||||||
|
@ -303,6 +324,7 @@ authorize(
|
||||||
ipaddr => IpAddress,
|
ipaddr => IpAddress,
|
||||||
topic => Topic
|
topic => Topic
|
||||||
}),
|
}),
|
||||||
|
emqx_plugin_libs_metrics:inc(authz_metrics, AuthzSource, deny),
|
||||||
emqx_metrics:inc(?METRIC_DENY),
|
emqx_metrics:inc(?METRIC_DENY),
|
||||||
{stop, deny};
|
{stop, deny};
|
||||||
nomatch ->
|
nomatch ->
|
||||||
|
@ -332,9 +354,13 @@ do_authorize(
|
||||||
[Connector = #{type := Type} | Tail]
|
[Connector = #{type := Type} | Tail]
|
||||||
) ->
|
) ->
|
||||||
Module = authz_module(Type),
|
Module = authz_module(Type),
|
||||||
|
emqx_plugin_libs_metrics:inc(authz_metrics, Type, matched),
|
||||||
case Module:authorize(Client, PubSub, Topic, Connector) of
|
case Module:authorize(Client, PubSub, Topic, Connector) of
|
||||||
nomatch -> do_authorize(Client, PubSub, Topic, Tail);
|
nomatch ->
|
||||||
Matched -> {Matched, Type}
|
emqx_plugin_libs_metrics:inc(authz_metrics, Type, ignore),
|
||||||
|
do_authorize(Client, PubSub, Topic, Tail);
|
||||||
|
Matched ->
|
||||||
|
{Matched, Type}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
get_enabled_authzs() ->
|
get_enabled_authzs() ->
|
||||||
|
|
|
@ -143,7 +143,7 @@ schema("/authorization/sources/:type/status") ->
|
||||||
responses =>
|
responses =>
|
||||||
#{
|
#{
|
||||||
200 => emqx_dashboard_swagger:schema_with_examples(
|
200 => emqx_dashboard_swagger:schema_with_examples(
|
||||||
hoconsc:ref(emqx_authn_schema, "metrics_status_fields"),
|
hoconsc:ref(emqx_authz_schema, "metrics_status_fields"),
|
||||||
status_metrics_example()
|
status_metrics_example()
|
||||||
),
|
),
|
||||||
400 => emqx_dashboard_swagger:error_codes(
|
400 => emqx_dashboard_swagger:error_codes(
|
||||||
|
@ -258,24 +258,7 @@ source(delete, #{bindings := #{type := Type}}) ->
|
||||||
update_config({?CMD_DELETE, Type}, #{}).
|
update_config({?CMD_DELETE, Type}, #{}).
|
||||||
|
|
||||||
source_status(get, #{bindings := #{type := Type}}) ->
|
source_status(get, #{bindings := #{type := Type}}) ->
|
||||||
BinType = atom_to_binary(Type, utf8),
|
lookup_from_all_nodes(Type).
|
||||||
case get_raw_source(BinType) of
|
|
||||||
[] ->
|
|
||||||
{404, #{
|
|
||||||
code => <<"NOT_FOUND">>,
|
|
||||||
message => <<"Not found", BinType/binary>>
|
|
||||||
}};
|
|
||||||
[#{<<"type">> := <<"file">>}] ->
|
|
||||||
{400, #{
|
|
||||||
code => <<"BAD_REQUEST">>,
|
|
||||||
message => <<"Not Support Status">>
|
|
||||||
}};
|
|
||||||
[_] ->
|
|
||||||
case emqx_authz:lookup(Type) of
|
|
||||||
#{annotations := #{id := ResourceId}} -> lookup_from_all_nodes(ResourceId);
|
|
||||||
_ -> {400, #{code => <<"BAD_REQUEST">>, message => <<"Resource Disable">>}}
|
|
||||||
end
|
|
||||||
end.
|
|
||||||
|
|
||||||
move_source(Method, #{bindings := #{type := Type} = Bindings} = Req) when
|
move_source(Method, #{bindings := #{type := Type} = Bindings} = Req) when
|
||||||
is_atom(Type)
|
is_atom(Type)
|
||||||
|
@ -321,39 +304,51 @@ move_source(post, #{bindings := #{type := Type}, body := #{<<"position">> := Pos
|
||||||
%% Internal functions
|
%% Internal functions
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
lookup_from_local_node(ResourceId) ->
|
lookup_from_local_node(Type) ->
|
||||||
NodeId = node(self()),
|
NodeId = node(self()),
|
||||||
|
try emqx_authz:lookup(Type) of
|
||||||
|
#{annotations := #{id := ResourceId}} ->
|
||||||
|
Metrics = emqx_plugin_libs_metrics:get_metrics(authz_metrics, Type),
|
||||||
case emqx_resource:get_instance(ResourceId) of
|
case emqx_resource:get_instance(ResourceId) of
|
||||||
{error, not_found} -> {error, {NodeId, not_found_resource}};
|
{error, not_found} ->
|
||||||
{ok, _, #{status := Status, metrics := Metrics}} -> {ok, {NodeId, Status, Metrics}}
|
{error, {NodeId, not_found_resource}};
|
||||||
|
{ok, _, #{status := Status, metrics := ResourceMetrics}} ->
|
||||||
|
{ok, {NodeId, Status, Metrics, ResourceMetrics}}
|
||||||
|
end;
|
||||||
|
_ ->
|
||||||
|
Metrics = emqx_plugin_libs_metrics:get_metrics(authz_metrics, Type),
|
||||||
|
{ok, {NodeId, connected, Metrics, #{}}}
|
||||||
|
catch
|
||||||
|
_:Reason -> {error, {NodeId, list_to_binary(io_lib:format("~p", [Reason]))}}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
lookup_from_all_nodes(ResourceId) ->
|
lookup_from_all_nodes(Type) ->
|
||||||
Nodes = mria_mnesia:running_nodes(),
|
Nodes = mria_mnesia:running_nodes(),
|
||||||
case is_ok(emqx_authz_proto_v1:lookup_from_all_nodes(Nodes, ResourceId)) of
|
case is_ok(emqx_authz_proto_v1:lookup_from_all_nodes(Nodes, Type)) of
|
||||||
{ok, ResList} ->
|
{ok, ResList} ->
|
||||||
{StatusMap, MetricsMap, _} = make_result_map(ResList),
|
{StatusMap, MetricsMap, ResourceMetricsMap, ErrorMap} = make_result_map(ResList),
|
||||||
AggregateStatus = aggregate_status(maps:values(StatusMap)),
|
AggregateStatus = aggregate_status(maps:values(StatusMap)),
|
||||||
AggregateMetrics = aggregate_metrics(maps:values(MetricsMap)),
|
AggregateMetrics = aggregate_metrics(maps:values(MetricsMap)),
|
||||||
|
AggregateResourceMetrics = aggregate_metrics(maps:values(ResourceMetricsMap)),
|
||||||
Fun = fun(_, V1) -> restructure_map(V1) end,
|
Fun = fun(_, V1) -> restructure_map(V1) end,
|
||||||
MKMap = fun(Name) -> fun({Key, Val}) -> #{node => Key, Name => Val} end end,
|
MKMap = fun(Name) -> fun({Key, Val}) -> #{node => Key, Name => Val} end end,
|
||||||
HelpFun = fun(M, Name) -> lists:map(MKMap(Name), maps:to_list(M)) end,
|
HelpFun = fun(M, Name) -> lists:map(MKMap(Name), maps:to_list(M)) end,
|
||||||
case AggregateStatus of
|
|
||||||
empty_metrics_and_status ->
|
|
||||||
{400, #{
|
|
||||||
code => <<"BAD_REQUEST">>,
|
|
||||||
message => <<"Resource Not Support Status">>
|
|
||||||
}};
|
|
||||||
_ ->
|
|
||||||
{200, #{
|
{200, #{
|
||||||
node_status => HelpFun(StatusMap, status),
|
node_resource_metrics => HelpFun(maps:map(Fun, ResourceMetricsMap), metrics),
|
||||||
|
resource_metrics =>
|
||||||
|
case maps:size(AggregateResourceMetrics) of
|
||||||
|
0 -> #{};
|
||||||
|
_ -> restructure_map(AggregateResourceMetrics)
|
||||||
|
end,
|
||||||
node_metrics => HelpFun(maps:map(Fun, MetricsMap), metrics),
|
node_metrics => HelpFun(maps:map(Fun, MetricsMap), metrics),
|
||||||
|
metrics => restructure_map(AggregateMetrics),
|
||||||
|
|
||||||
|
node_status => HelpFun(StatusMap, status),
|
||||||
status => AggregateStatus,
|
status => AggregateStatus,
|
||||||
metrics => restructure_map(AggregateMetrics)
|
node_error => HelpFun(maps:map(Fun, ErrorMap), reason)
|
||||||
}}
|
}};
|
||||||
end;
|
|
||||||
{error, ErrL} ->
|
{error, ErrL} ->
|
||||||
{500, #{
|
{400, #{
|
||||||
code => <<"INTERNAL_ERROR">>,
|
code => <<"INTERNAL_ERROR">>,
|
||||||
message => bin_t(io_lib:format("~p", [ErrL]))
|
message => bin_t(io_lib:format("~p", [ErrL]))
|
||||||
}}
|
}}
|
||||||
|
@ -371,7 +366,7 @@ aggregate_status(AllStatus) ->
|
||||||
end.
|
end.
|
||||||
|
|
||||||
aggregate_metrics([]) ->
|
aggregate_metrics([]) ->
|
||||||
empty_metrics_and_status;
|
#{};
|
||||||
aggregate_metrics([HeadMetrics | AllMetrics]) ->
|
aggregate_metrics([HeadMetrics | AllMetrics]) ->
|
||||||
CombinerFun =
|
CombinerFun =
|
||||||
fun ComFun(_Key, Val1, Val2) ->
|
fun ComFun(_Key, Val1, Val2) ->
|
||||||
|
@ -387,20 +382,34 @@ aggregate_metrics([HeadMetrics | AllMetrics]) ->
|
||||||
|
|
||||||
make_result_map(ResList) ->
|
make_result_map(ResList) ->
|
||||||
Fun =
|
Fun =
|
||||||
fun(Elem, {StatusMap, MetricsMap, ErrorMap}) ->
|
fun(Elem, {StatusMap, MetricsMap, ResourceMetricsMap, ErrorMap}) ->
|
||||||
case Elem of
|
case Elem of
|
||||||
{ok, {NodeId, Status, Metrics}} ->
|
{ok, {NodeId, Status, Metrics, ResourceMetrics}} ->
|
||||||
{
|
{
|
||||||
maps:put(NodeId, Status, StatusMap),
|
maps:put(NodeId, Status, StatusMap),
|
||||||
maps:put(NodeId, Metrics, MetricsMap),
|
maps:put(NodeId, Metrics, MetricsMap),
|
||||||
|
maps:put(NodeId, ResourceMetrics, ResourceMetricsMap),
|
||||||
ErrorMap
|
ErrorMap
|
||||||
};
|
};
|
||||||
{error, {NodeId, Reason}} ->
|
{error, {NodeId, Reason}} ->
|
||||||
{StatusMap, MetricsMap, maps:put(NodeId, Reason, ErrorMap)}
|
{StatusMap, MetricsMap, ResourceMetricsMap, maps:put(NodeId, Reason, ErrorMap)}
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
lists:foldl(Fun, {maps:new(), maps:new(), maps:new()}, ResList).
|
lists:foldl(Fun, {maps:new(), maps:new(), maps:new(), maps:new()}, ResList).
|
||||||
|
|
||||||
|
restructure_map(#{
|
||||||
|
counters := #{deny := Failed, matched := Match, allow := Succ, ignore := Ignore},
|
||||||
|
rate := #{matched := #{current := Rate, last5m := Rate5m, max := RateMax}}
|
||||||
|
}) ->
|
||||||
|
#{
|
||||||
|
matched => Match,
|
||||||
|
allow => Succ,
|
||||||
|
deny => Failed,
|
||||||
|
ignore => Ignore,
|
||||||
|
rate => Rate,
|
||||||
|
rate_last5m => Rate5m,
|
||||||
|
rate_max => RateMax
|
||||||
|
};
|
||||||
restructure_map(#{
|
restructure_map(#{
|
||||||
counters := #{failed := Failed, matched := Match, success := Succ},
|
counters := #{failed := Failed, matched := Match, success := Succ},
|
||||||
rate := #{matched := #{current := Rate, last5m := Rate5m, max := RateMax}}
|
rate := #{matched := #{current := Rate, last5m := Rate5m, max := RateMax}}
|
||||||
|
@ -561,7 +570,7 @@ bin(Term) -> erlang:iolist_to_binary(io_lib:format("~p", [Term])).
|
||||||
|
|
||||||
status_metrics_example() ->
|
status_metrics_example() ->
|
||||||
#{
|
#{
|
||||||
metrics => #{
|
resource_metrics => #{
|
||||||
matched => 0,
|
matched => 0,
|
||||||
success => 0,
|
success => 0,
|
||||||
failed => 0,
|
failed => 0,
|
||||||
|
@ -569,7 +578,7 @@ status_metrics_example() ->
|
||||||
rate_last5m => 0.0,
|
rate_last5m => 0.0,
|
||||||
rate_max => 0.0
|
rate_max => 0.0
|
||||||
},
|
},
|
||||||
node_metrics => [
|
node_resource_metrics => [
|
||||||
#{
|
#{
|
||||||
node => node(),
|
node => node(),
|
||||||
metrics => #{
|
metrics => #{
|
||||||
|
@ -582,6 +591,30 @@ status_metrics_example() ->
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
metrics => #{
|
||||||
|
matched => 0,
|
||||||
|
allow => 0,
|
||||||
|
deny => 0,
|
||||||
|
ignore => 0,
|
||||||
|
rate => 0.0,
|
||||||
|
rate_last5m => 0.0,
|
||||||
|
rate_max => 0.0
|
||||||
|
},
|
||||||
|
node_metrics => [
|
||||||
|
#{
|
||||||
|
node => node(),
|
||||||
|
metrics => #{
|
||||||
|
matched => 0,
|
||||||
|
allow => 0,
|
||||||
|
deny => 0,
|
||||||
|
ignore => 0,
|
||||||
|
rate => 0.0,
|
||||||
|
rate_last5m => 0.0,
|
||||||
|
rate_max => 0.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
status => connected,
|
status => connected,
|
||||||
node_status => [
|
node_status => [
|
||||||
#{
|
#{
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
-include("emqx_authz.hrl").
|
-include("emqx_authz.hrl").
|
||||||
-include_lib("hocon/include/hoconsc.hrl").
|
-include_lib("hocon/include/hoconsc.hrl").
|
||||||
-include_lib("emqx_connector/include/emqx_connector.hrl").
|
-include_lib("emqx_connector/include/emqx_connector.hrl").
|
||||||
|
-import(hoconsc, [mk/2, ref/2]).
|
||||||
|
|
||||||
-reflect_type([
|
-reflect_type([
|
||||||
permission/0,
|
permission/0,
|
||||||
|
@ -134,8 +135,85 @@ fields(jwt) ->
|
||||||
default => <<"acl">>,
|
default => <<"acl">>,
|
||||||
desc => ?DESC(acl_claim_name)
|
desc => ?DESC(acl_claim_name)
|
||||||
}}
|
}}
|
||||||
|
];
|
||||||
|
fields("metrics_status_fields") ->
|
||||||
|
[
|
||||||
|
{"resource_metrics", mk(ref(?MODULE, "resource_metrics"), #{desc => ?DESC("metrics")})},
|
||||||
|
{"node_resource_metrics",
|
||||||
|
mk(
|
||||||
|
hoconsc:array(ref(?MODULE, "node_resource_metrics")),
|
||||||
|
#{desc => ?DESC("node_metrics")}
|
||||||
|
)},
|
||||||
|
{"metrics", mk(ref(?MODULE, "metrics"), #{desc => ?DESC("metrics")})},
|
||||||
|
{"node_metrics",
|
||||||
|
mk(
|
||||||
|
hoconsc:array(ref(?MODULE, "node_metrics")),
|
||||||
|
#{desc => ?DESC("node_metrics")}
|
||||||
|
)},
|
||||||
|
{"status", mk(cluster_status(), #{desc => ?DESC("status")})},
|
||||||
|
{"node_status",
|
||||||
|
mk(
|
||||||
|
hoconsc:array(ref(?MODULE, "node_status")),
|
||||||
|
#{desc => ?DESC("node_status")}
|
||||||
|
)},
|
||||||
|
{"node_error",
|
||||||
|
mk(
|
||||||
|
hoconsc:array(ref(?MODULE, "node_error")),
|
||||||
|
#{desc => ?DESC("node_error")}
|
||||||
|
)}
|
||||||
|
];
|
||||||
|
fields("metrics") ->
|
||||||
|
[
|
||||||
|
{"matched", mk(integer(), #{desc => ?DESC("matched")})},
|
||||||
|
{"allow", mk(integer(), #{desc => ?DESC("allow")})},
|
||||||
|
{"deny", mk(integer(), #{desc => ?DESC("deny")})},
|
||||||
|
{"ignore", mk(float(), #{desc => ?DESC("ignore")})},
|
||||||
|
{"rate", mk(float(), #{desc => ?DESC("rate")})},
|
||||||
|
{"rate_max", mk(float(), #{desc => ?DESC("rate_max")})},
|
||||||
|
{"rate_last5m", mk(float(), #{desc => ?DESC("rate_last5m")})}
|
||||||
|
];
|
||||||
|
fields("node_metrics") ->
|
||||||
|
[
|
||||||
|
node_name(),
|
||||||
|
{"metrics", mk(ref(?MODULE, "metrics"), #{desc => ?DESC("metrics")})}
|
||||||
|
];
|
||||||
|
fields("resource_metrics") ->
|
||||||
|
common_field();
|
||||||
|
fields("node_resource_metrics") ->
|
||||||
|
[
|
||||||
|
node_name(),
|
||||||
|
{"metrics", mk(ref(?MODULE, "resource_metrics"), #{desc => ?DESC("metrics")})}
|
||||||
|
];
|
||||||
|
fields("node_status") ->
|
||||||
|
[
|
||||||
|
node_name(),
|
||||||
|
{"status", mk(status(), #{desc => ?DESC("node_status")})}
|
||||||
|
];
|
||||||
|
fields("node_error") ->
|
||||||
|
[
|
||||||
|
node_name(),
|
||||||
|
{"error", mk(string(), #{desc => ?DESC("node_error")})}
|
||||||
].
|
].
|
||||||
|
|
||||||
|
common_field() ->
|
||||||
|
[
|
||||||
|
{"matched", mk(integer(), #{desc => ?DESC("matched")})},
|
||||||
|
{"success", mk(integer(), #{desc => ?DESC("success")})},
|
||||||
|
{"failed", mk(integer(), #{desc => ?DESC("failed")})},
|
||||||
|
{"rate", mk(float(), #{desc => ?DESC("rate")})},
|
||||||
|
{"rate_max", mk(float(), #{desc => ?DESC("rate_max")})},
|
||||||
|
{"rate_last5m", mk(float(), #{desc => ?DESC("rate_last5m")})}
|
||||||
|
].
|
||||||
|
|
||||||
|
status() ->
|
||||||
|
hoconsc:enum([connected, disconnected, connecting]).
|
||||||
|
|
||||||
|
cluster_status() ->
|
||||||
|
hoconsc:enum([connected, disconnected, connecting, inconsistent]).
|
||||||
|
|
||||||
|
node_name() ->
|
||||||
|
{"node", mk(binary(), #{desc => ?DESC("node"), example => "emqx@127.0.0.1"})}.
|
||||||
|
|
||||||
desc(?CONF_NS) ->
|
desc(?CONF_NS) ->
|
||||||
?DESC(?CONF_NS);
|
?DESC(?CONF_NS);
|
||||||
desc(file) ->
|
desc(file) ->
|
||||||
|
|
|
@ -30,7 +30,7 @@
|
||||||
introduced_in() ->
|
introduced_in() ->
|
||||||
"5.0.0".
|
"5.0.0".
|
||||||
|
|
||||||
-spec lookup_from_all_nodes([node()], binary()) ->
|
-spec lookup_from_all_nodes([node()], atom()) ->
|
||||||
emqx_rpc:erpc_multicall().
|
emqx_rpc:erpc_multicall().
|
||||||
lookup_from_all_nodes(Nodes, ResourceId) ->
|
lookup_from_all_nodes(Nodes, Type) ->
|
||||||
erpc:multicall(Nodes, emqx_authz_api_sources, lookup_from_local_node, [ResourceId], ?TIMEOUT).
|
erpc:multicall(Nodes, emqx_authz_api_sources, lookup_from_local_node, [Type], ?TIMEOUT).
|
||||||
|
|
|
@ -188,38 +188,6 @@ t_api(_) ->
|
||||||
],
|
],
|
||||||
{ok, 204, _} = request(post, uri(["authorization", "sources"]), ?SOURCE1),
|
{ok, 204, _} = request(post, uri(["authorization", "sources"]), ?SOURCE1),
|
||||||
|
|
||||||
Snd = fun({_, Val}) -> Val end,
|
|
||||||
LookupVal = fun LookupV(List, RestJson) ->
|
|
||||||
case List of
|
|
||||||
[Name] -> Snd(lists:keyfind(Name, 1, RestJson));
|
|
||||||
[Name | NS] -> LookupV(NS, Snd(lists:keyfind(Name, 1, RestJson)))
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
EqualFun = fun(RList) ->
|
|
||||||
fun({M, V}) ->
|
|
||||||
?assertEqual(
|
|
||||||
V,
|
|
||||||
LookupVal(
|
|
||||||
[<<"metrics">>, M],
|
|
||||||
RList
|
|
||||||
)
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
AssertFun =
|
|
||||||
fun(ResultJson) ->
|
|
||||||
{ok, RList} = emqx_json:safe_decode(ResultJson),
|
|
||||||
MetricsList = [
|
|
||||||
{<<"failed">>, 0},
|
|
||||||
{<<"matched">>, 0},
|
|
||||||
{<<"rate">>, 0.0},
|
|
||||||
{<<"rate_last5m">>, 0.0},
|
|
||||||
{<<"rate_max">>, 0.0},
|
|
||||||
{<<"success">>, 0}
|
|
||||||
],
|
|
||||||
lists:map(EqualFun(RList), MetricsList)
|
|
||||||
end,
|
|
||||||
|
|
||||||
{ok, 200, Result2} = request(get, uri(["authorization", "sources"]), []),
|
{ok, 200, Result2} = request(get, uri(["authorization", "sources"]), []),
|
||||||
Sources = get_sources(Result2),
|
Sources = get_sources(Result2),
|
||||||
?assertMatch(
|
?assertMatch(
|
||||||
|
@ -271,7 +239,14 @@ t_api(_) ->
|
||||||
),
|
),
|
||||||
{ok, 200, Result4} = request(get, uri(["authorization", "sources", "mongodb"]), []),
|
{ok, 200, Result4} = request(get, uri(["authorization", "sources", "mongodb"]), []),
|
||||||
{ok, 200, Status4} = request(get, uri(["authorization", "sources", "mongodb", "status"]), []),
|
{ok, 200, Status4} = request(get, uri(["authorization", "sources", "mongodb", "status"]), []),
|
||||||
AssertFun(Status4),
|
#{
|
||||||
|
<<"metrics">> := #{
|
||||||
|
<<"allow">> := 0,
|
||||||
|
<<"deny">> := 0,
|
||||||
|
<<"matched">> := 0,
|
||||||
|
<<"ignore">> := 0
|
||||||
|
}
|
||||||
|
} = jiffy:decode(Status4, [return_maps]),
|
||||||
?assertMatch(
|
?assertMatch(
|
||||||
#{
|
#{
|
||||||
<<"type">> := <<"mongodb">>,
|
<<"type">> := <<"mongodb">>,
|
||||||
|
@ -304,8 +279,6 @@ t_api(_) ->
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
{ok, 200, Result5} = request(get, uri(["authorization", "sources", "mongodb"]), []),
|
{ok, 200, Result5} = request(get, uri(["authorization", "sources", "mongodb"]), []),
|
||||||
{ok, 200, Status5} = request(get, uri(["authorization", "sources", "mongodb", "status"]), []),
|
|
||||||
AssertFun(Status5),
|
|
||||||
?assertMatch(
|
?assertMatch(
|
||||||
#{
|
#{
|
||||||
<<"type">> := <<"mongodb">>,
|
<<"type">> := <<"mongodb">>,
|
||||||
|
@ -320,6 +293,16 @@ t_api(_) ->
|
||||||
jsx:decode(Result5)
|
jsx:decode(Result5)
|
||||||
),
|
),
|
||||||
|
|
||||||
|
{ok, 200, Status5_1} = request(get, uri(["authorization", "sources", "mongodb", "status"]), []),
|
||||||
|
#{
|
||||||
|
<<"metrics">> := #{
|
||||||
|
<<"allow">> := 0,
|
||||||
|
<<"deny">> := 0,
|
||||||
|
<<"matched">> := 0,
|
||||||
|
<<"ignore">> := 0
|
||||||
|
}
|
||||||
|
} = jiffy:decode(Status5_1, [return_maps]),
|
||||||
|
|
||||||
#{
|
#{
|
||||||
ssl := #{
|
ssl := #{
|
||||||
cacertfile := SavedCacertfile,
|
cacertfile := SavedCacertfile,
|
||||||
|
@ -367,6 +350,77 @@ t_api(_) ->
|
||||||
{ok, 200, Result6} = request(get, uri(["authorization", "sources"]), []),
|
{ok, 200, Result6} = request(get, uri(["authorization", "sources"]), []),
|
||||||
?assertEqual([], get_sources(Result6)),
|
?assertEqual([], get_sources(Result6)),
|
||||||
?assertEqual([], emqx:get_config([authorization, sources])),
|
?assertEqual([], emqx:get_config([authorization, sources])),
|
||||||
|
|
||||||
|
{ok, 204, _} = request(post, uri(["authorization", "sources"]), ?SOURCE6),
|
||||||
|
|
||||||
|
{ok, Client} = emqtt:start_link(
|
||||||
|
[
|
||||||
|
{username, <<"u_event3">>},
|
||||||
|
{clientid, <<"c_event3">>},
|
||||||
|
{proto_ver, v5},
|
||||||
|
{properties, #{'Session-Expiry-Interval' => 60}}
|
||||||
|
]
|
||||||
|
),
|
||||||
|
emqtt:connect(Client),
|
||||||
|
timer:sleep(50),
|
||||||
|
|
||||||
|
emqtt:publish(
|
||||||
|
Client,
|
||||||
|
<<"t1">>,
|
||||||
|
#{'Message-Expiry-Interval' => 60},
|
||||||
|
<<"{\"id\": 1, \"name\": \"ha\"}">>,
|
||||||
|
[{qos, 1}]
|
||||||
|
),
|
||||||
|
|
||||||
|
{ok, 200, Status5} = request(get, uri(["authorization", "sources", "file", "status"]), []),
|
||||||
|
#{
|
||||||
|
<<"metrics">> := #{
|
||||||
|
<<"allow">> := 1,
|
||||||
|
<<"deny">> := 0,
|
||||||
|
<<"matched">> := 1,
|
||||||
|
<<"ignore">> := 0
|
||||||
|
}
|
||||||
|
} = jiffy:decode(Status5, [return_maps]),
|
||||||
|
|
||||||
|
timer:sleep(50),
|
||||||
|
emqtt:publish(
|
||||||
|
Client,
|
||||||
|
<<"t2">>,
|
||||||
|
#{'Message-Expiry-Interval' => 60},
|
||||||
|
<<"{\"id\": 1, \"name\": \"ha\"}">>,
|
||||||
|
[{qos, 1}]
|
||||||
|
),
|
||||||
|
|
||||||
|
{ok, 200, Status6} = request(get, uri(["authorization", "sources", "file", "status"]), []),
|
||||||
|
#{
|
||||||
|
<<"metrics">> := #{
|
||||||
|
<<"allow">> := 2,
|
||||||
|
<<"deny">> := 0,
|
||||||
|
<<"matched">> := 2,
|
||||||
|
<<"ignore">> := 0
|
||||||
|
}
|
||||||
|
} = jiffy:decode(Status6, [return_maps]),
|
||||||
|
|
||||||
|
timer:sleep(50),
|
||||||
|
emqtt:publish(
|
||||||
|
Client,
|
||||||
|
<<"t3">>,
|
||||||
|
#{'Message-Expiry-Interval' => 60},
|
||||||
|
<<"{\"id\": 1, \"name\": \"ha\"}">>,
|
||||||
|
[{qos, 1}]
|
||||||
|
),
|
||||||
|
|
||||||
|
timer:sleep(50),
|
||||||
|
{ok, 200, Status7} = request(get, uri(["authorization", "sources", "file", "status"]), []),
|
||||||
|
#{
|
||||||
|
<<"metrics">> := #{
|
||||||
|
<<"allow">> := 3,
|
||||||
|
<<"deny">> := 0,
|
||||||
|
<<"matched">> := 3,
|
||||||
|
<<"ignore">> := 0
|
||||||
|
}
|
||||||
|
} = jiffy:decode(Status7, [return_maps]),
|
||||||
|
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
t_move_source(_) ->
|
t_move_source(_) ->
|
||||||
|
|
|
@ -72,7 +72,7 @@
|
||||||
rate := #{atom() => rate()}
|
rate := #{atom() => rate()}
|
||||||
}.
|
}.
|
||||||
-type handler_name() :: atom().
|
-type handler_name() :: atom().
|
||||||
-type metric_id() :: binary().
|
-type metric_id() :: binary() | atom().
|
||||||
|
|
||||||
-define(CntrRef(Name), {?MODULE, Name}).
|
-define(CntrRef(Name), {?MODULE, Name}).
|
||||||
-define(SAMPCOUNT_5M, (?SECS_5M div ?SAMPLING)).
|
-define(SAMPCOUNT_5M, (?SECS_5M div ?SAMPLING)).
|
||||||
|
|
Loading…
Reference in New Issue