|
|
@@ -23,33 +23,36 @@
|
|
|
|
|
|
-export([api_spec/0, paths/0, schema/1, fields/1, namespace/0]).
|
|
|
|
|
|
--export([exhooks/2, action_with_name/2, move/2]).
|
|
|
+-export([exhooks/2, action_with_name/2, move/2, server_hooks/2]).
|
|
|
|
|
|
--import(hoconsc, [mk/2, ref/1, enum/1, array/1]).
|
|
|
+-import(hoconsc, [mk/2, ref/1, enum/1, array/1, map/2]).
|
|
|
-import(emqx_dashboard_swagger, [schema_with_example/2, error_codes/2]).
|
|
|
|
|
|
-define(TAGS, [<<"exhooks">>]).
|
|
|
-define(BAD_REQUEST, 'BAD_REQUEST').
|
|
|
-define(BAD_RPC, 'BAD_RPC').
|
|
|
|
|
|
+%%--------------------------------------------------------------------
|
|
|
+%% schema
|
|
|
+%%--------------------------------------------------------------------
|
|
|
namespace() -> "exhook".
|
|
|
|
|
|
api_spec() ->
|
|
|
emqx_dashboard_swagger:spec(?MODULE).
|
|
|
|
|
|
-paths() -> ["/exhooks", "/exhooks/:name", "/exhooks/:name/move"].
|
|
|
+paths() -> ["/exhooks", "/exhooks/:name", "/exhooks/:name/move", "/exhooks/:name/hooks"].
|
|
|
|
|
|
schema(("/exhooks")) ->
|
|
|
#{
|
|
|
'operationId' => exhooks,
|
|
|
get => #{tags => ?TAGS,
|
|
|
description => <<"List all servers">>,
|
|
|
- responses => #{200 => mk(array(ref(detailed_server_info)), #{})}
|
|
|
+ responses => #{200 => mk(array(ref(list_server_info)), #{})}
|
|
|
},
|
|
|
post => #{tags => ?TAGS,
|
|
|
description => <<"Add a servers">>,
|
|
|
'requestBody' => server_conf_schema(),
|
|
|
- responses => #{201 => mk(ref(detailed_server_info), #{}),
|
|
|
+ responses => #{201 => mk(ref(detail_server_info), #{}),
|
|
|
500 => error_codes([?BAD_RPC], <<"Bad RPC">>)
|
|
|
}
|
|
|
}
|
|
|
@@ -60,7 +63,7 @@ schema("/exhooks/:name") ->
|
|
|
get => #{tags => ?TAGS,
|
|
|
description => <<"Get the detail information of server">>,
|
|
|
parameters => params_server_name_in_path(),
|
|
|
- responses => #{200 => mk(ref(detailed_server_info), #{}),
|
|
|
+ responses => #{200 => mk(ref(detail_server_info), #{}),
|
|
|
400 => error_codes([?BAD_REQUEST], <<"Bad Request">>)
|
|
|
}
|
|
|
},
|
|
|
@@ -77,11 +80,21 @@ schema("/exhooks/:name") ->
|
|
|
description => <<"Delete the server">>,
|
|
|
parameters => params_server_name_in_path(),
|
|
|
responses => #{204 => <<>>,
|
|
|
- 500 => error_codes([?BAD_RPC], <<"Bad RPC">>)
|
|
|
- }
|
|
|
+ 500 => error_codes([?BAD_RPC], <<"Bad RPC">>) }
|
|
|
}
|
|
|
};
|
|
|
|
|
|
+schema("/exhooks/:name/hooks") ->
|
|
|
+ #{'operationId' => server_hooks,
|
|
|
+ get => #{tags => ?TAGS,
|
|
|
+ description => <<"Get the hooks information of server">>,
|
|
|
+ parameters => params_server_name_in_path(),
|
|
|
+ responses => #{200 => mk(array(ref(list_hook_info)), #{}),
|
|
|
+ 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
schema("/exhooks/:name/move") ->
|
|
|
#{'operationId' => move,
|
|
|
post => #{tags => ?TAGS,
|
|
|
@@ -96,25 +109,56 @@ schema("/exhooks/:name/move") ->
|
|
|
}.
|
|
|
|
|
|
fields(move_req) ->
|
|
|
- [
|
|
|
- {position, mk(enum([top, bottom, before, 'after']), #{})},
|
|
|
- {related, mk(string(), #{desc => <<"Relative position of movement">>,
|
|
|
- default => <<>>,
|
|
|
- example => <<>>
|
|
|
- })}
|
|
|
+ [ {position, mk(enum([top, bottom, before, 'after']), #{})}
|
|
|
+ , {related, mk(string(), #{desc => <<"Relative position of movement">>,
|
|
|
+ default => <<>>,
|
|
|
+ example => <<>>
|
|
|
+ })}
|
|
|
];
|
|
|
|
|
|
-fields(detailed_server_info) ->
|
|
|
- [ {status, mk(enum([running, waiting, stopped]), #{})}
|
|
|
- , {hooks, mk(array(string()), #{default => []})}
|
|
|
- , {node_status, mk(ref(node_status), #{})}
|
|
|
+fields(list_server_info) ->
|
|
|
+ [ {metrics, mk(ref(metrics), #{})}
|
|
|
+ , {node_metrics, mk(array(ref(node_metrics)), #{})}
|
|
|
+ , {node_status, mk(array(ref(node_status)), #{})}
|
|
|
+ , {hooks, mk(array(ref(hook_info)), #{})}
|
|
|
+ ] ++ emqx_exhook_schema:server_config();
|
|
|
+
|
|
|
+fields(detail_server_info) ->
|
|
|
+ [ {metrics, mk(ref(metrics), #{})}
|
|
|
+ , {node_metrics, mk(array(ref(node_metrics)), #{})}
|
|
|
+ , {node_status, mk(array(ref(node_status)), #{})}
|
|
|
+ , {hooks, mk(array(ref(hook_info)), #{})}
|
|
|
] ++ emqx_exhook_schema:server_config();
|
|
|
|
|
|
+fields(list_hook_info) ->
|
|
|
+ [ {name, mk(binary(), #{})}
|
|
|
+ , {params, mk(map(name, binary()), #{})}
|
|
|
+ , {metrics, mk(ref(metrics), #{})}
|
|
|
+ , {node_metrics, mk(array(ref(node_metrics)), #{})}
|
|
|
+ ];
|
|
|
+
|
|
|
+fields(node_metrics) ->
|
|
|
+ [ {node, mk(string(), #{})}
|
|
|
+ , {metrics, mk(ref(metrics), #{})}
|
|
|
+ ];
|
|
|
+
|
|
|
fields(node_status) ->
|
|
|
[ {node, mk(string(), #{})}
|
|
|
, {status, mk(enum([running, waiting, stopped, not_found, error]), #{})}
|
|
|
];
|
|
|
|
|
|
+fields(hook_info) ->
|
|
|
+ [ {name, mk(binary(), #{})}
|
|
|
+ , {params, mk(map(name, binary()), #{})}
|
|
|
+ ];
|
|
|
+
|
|
|
+fields(metrics) ->
|
|
|
+ [ {succeed, mk(integer(), #{})}
|
|
|
+ , {failed, mk(integer(), #{})}
|
|
|
+ , {rate, mk(integer(), #{})}
|
|
|
+ , {max_rate, mk(integer(), #{})}
|
|
|
+ ];
|
|
|
+
|
|
|
fields(server_config) ->
|
|
|
emqx_exhook_schema:server_config().
|
|
|
|
|
|
@@ -140,16 +184,19 @@ server_conf_schema() ->
|
|
|
}
|
|
|
}).
|
|
|
|
|
|
-
|
|
|
+%%--------------------------------------------------------------------
|
|
|
+%% API
|
|
|
+%%--------------------------------------------------------------------
|
|
|
exhooks(get, _) ->
|
|
|
- ServerL = emqx_exhook_mgr:list(),
|
|
|
- ServerL2 = nodes_all_server_status(ServerL),
|
|
|
- {200, ServerL2};
|
|
|
+ Confs = emqx:get_config([exhook, servers]),
|
|
|
+ Infos = nodes_all_server_info(Confs),
|
|
|
+ {200, Infos};
|
|
|
|
|
|
exhooks(post, #{body := Body}) ->
|
|
|
case emqx_exhook_mgr:update_config([exhook, servers], {add, Body}) of
|
|
|
- {ok, Result} ->
|
|
|
- {201, Result};
|
|
|
+ {ok, _} ->
|
|
|
+ #{<<"name">> := Name} = Body,
|
|
|
+ get_nodes_server_info(Name);
|
|
|
{error, Error} ->
|
|
|
{500, #{code => <<"BAD_RPC">>,
|
|
|
message => Error
|
|
|
@@ -157,16 +204,7 @@ exhooks(post, #{body := Body}) ->
|
|
|
end.
|
|
|
|
|
|
action_with_name(get, #{bindings := #{name := Name}}) ->
|
|
|
- Result = emqx_exhook_mgr:lookup(Name),
|
|
|
- NodeStatus = nodes_server_status(Name),
|
|
|
- case Result of
|
|
|
- not_found ->
|
|
|
- {400, #{code => <<"BAD_REQUEST">>,
|
|
|
- message => <<"Server not found">>
|
|
|
- }};
|
|
|
- ServerInfo ->
|
|
|
- {200, ServerInfo#{node_status => NodeStatus}}
|
|
|
- end;
|
|
|
+ get_nodes_server_info(Name);
|
|
|
|
|
|
action_with_name(put, #{bindings := #{name := Name}, body := Body}) ->
|
|
|
case emqx_exhook_mgr:update_config([exhook, servers],
|
|
|
@@ -177,8 +215,7 @@ action_with_name(put, #{bindings := #{name := Name}, body := Body}) ->
|
|
|
}};
|
|
|
{ok, {error, Reason}} ->
|
|
|
{400, #{code => <<"BAD_REQUEST">>,
|
|
|
- message => unicode:characters_to_binary(
|
|
|
- io_lib:format("Error Reason:~p~n", [Reason]))
|
|
|
+ message => unicode:characters_to_binary(io_lib:format("Error Reason:~p~n", [Reason]))
|
|
|
}};
|
|
|
{ok, _} ->
|
|
|
{200};
|
|
|
@@ -216,59 +253,136 @@ move(post, #{bindings := #{name := Name}, body := Body}) ->
|
|
|
}}
|
|
|
end.
|
|
|
|
|
|
-nodes_server_status(Name) ->
|
|
|
- StatusL = call_cluster(emqx_exhook_mgr, server_status, [Name]),
|
|
|
-
|
|
|
- Handler = fun({Node, {error, _}}) ->
|
|
|
- #{node => Node,
|
|
|
- status => error
|
|
|
- };
|
|
|
- ({Node, Status}) ->
|
|
|
- #{node => Node,
|
|
|
- status => Status
|
|
|
- }
|
|
|
- end,
|
|
|
-
|
|
|
- lists:map(Handler, StatusL).
|
|
|
-
|
|
|
-nodes_all_server_status(ServerL) ->
|
|
|
- AllStatusL = call_cluster(emqx_exhook_mgr, all_servers_status, []),
|
|
|
-
|
|
|
- AggreMap = lists:foldl(fun(#{name := Name}, Acc) ->
|
|
|
- Acc#{Name => []}
|
|
|
- end,
|
|
|
- #{},
|
|
|
- ServerL),
|
|
|
-
|
|
|
- AddToMap = fun(Servers, Node, Status, Map) ->
|
|
|
- lists:foldl(fun(Name, Acc) ->
|
|
|
- StatusL = maps:get(Name, Acc),
|
|
|
- StatusL2 = [#{node => Node,
|
|
|
- status => Status
|
|
|
- } | StatusL],
|
|
|
- Acc#{Name := StatusL2}
|
|
|
- end,
|
|
|
- Map,
|
|
|
- Servers)
|
|
|
- end,
|
|
|
-
|
|
|
- AggreMap2 = lists:foldl(fun({Node, #{running := Running,
|
|
|
- waiting := Waiting,
|
|
|
- stopped := Stopped}},
|
|
|
- Acc) ->
|
|
|
- AddToMap(Stopped, Node, stopped,
|
|
|
- AddToMap(Waiting, Node, waiting,
|
|
|
- AddToMap(Running, Node, running, Acc)))
|
|
|
- end,
|
|
|
- AggreMap,
|
|
|
- AllStatusL),
|
|
|
-
|
|
|
- Handler = fun(#{name := Name} = Server) ->
|
|
|
- Server#{node_status => maps:get(Name, AggreMap2)}
|
|
|
- end,
|
|
|
-
|
|
|
- lists:map(Handler, ServerL).
|
|
|
+server_hooks(get, #{bindings := #{name := Name}}) ->
|
|
|
+ Confs = emqx:get_config([exhook, servers]),
|
|
|
+ case lists:search(fun(#{name := CfgName}) -> CfgName =:= Name end, Confs) of
|
|
|
+ false ->
|
|
|
+ {400, #{code => <<"BAD_REQUEST">>,
|
|
|
+ message => <<"Server not found">>
|
|
|
+ }};
|
|
|
+ _ ->
|
|
|
+ Info = get_nodes_server_hooks_info(Name),
|
|
|
+ {200, Info}
|
|
|
+ end.
|
|
|
+
|
|
|
+get_nodes_server_info(Name) ->
|
|
|
+ Confs = emqx:get_config([exhook, servers]),
|
|
|
+ case lists:search(fun(#{name := CfgName}) -> CfgName =:= Name end, Confs) of
|
|
|
+ false ->
|
|
|
+ {400, #{code => <<"BAD_REQUEST">>,
|
|
|
+ message => <<"Server not found">>
|
|
|
+ }};
|
|
|
+ {value, Conf} ->
|
|
|
+ NodeStatus = nodes_server_info(Name),
|
|
|
+ {200, maps:merge(Conf, NodeStatus)}
|
|
|
+ end.
|
|
|
|
|
|
+%%--------------------------------------------------------------------
|
|
|
+%% GET /exhooks
|
|
|
+%%--------------------------------------------------------------------
|
|
|
+nodes_all_server_info(ConfL) ->
|
|
|
+ AllInfos = call_cluster(emqx_exhook_mgr, all_servers_info, []),
|
|
|
+ Default = emqx_exhook_metrics:new_metrics_info(),
|
|
|
+ node_all_server_info(ConfL, AllInfos, Default, []).
|
|
|
+
|
|
|
+node_all_server_info([#{name := ServerName} = Conf | T], AllInfos, Default, Acc) ->
|
|
|
+ Info = fill_cluster_server_info(AllInfos, [], [], ServerName, Default),
|
|
|
+ AllInfo = maps:merge(Conf, Info),
|
|
|
+ node_all_server_info(T, AllInfos, Default, [AllInfo | Acc]);
|
|
|
+
|
|
|
+node_all_server_info([], _, _, Acc) ->
|
|
|
+ lists:reverse(Acc).
|
|
|
+
|
|
|
+fill_cluster_server_info([{Node, {error, _}} | T], StatusL, MetricsL, ServerName, Default) ->
|
|
|
+ fill_cluster_server_info(T,
|
|
|
+ [#{node => Node, status => error} | StatusL],
|
|
|
+ [#{node => Node, metrics => Default} | MetricsL],
|
|
|
+ ServerName,
|
|
|
+ Default);
|
|
|
+
|
|
|
+fill_cluster_server_info([{Node, Result} | T], StatusL, MetricsL, ServerName, Default) ->
|
|
|
+ #{status := Status, metrics := Metrics} = Result,
|
|
|
+ fill_cluster_server_info(T,
|
|
|
+ [#{node => Node, status => maps:get(ServerName, Status, error)} | StatusL],
|
|
|
+ [#{node => Node, metrics => maps:get(ServerName, Metrics, Default)} | MetricsL],
|
|
|
+ ServerName,
|
|
|
+ Default);
|
|
|
+
|
|
|
+fill_cluster_server_info([], StatusL, MetricsL, ServerName, _) ->
|
|
|
+ Metrics = emqx_exhook_metrics:metrics_aggregate_by_key(metrics, MetricsL),
|
|
|
+ #{metrics => Metrics,
|
|
|
+ node_metrics => MetricsL,
|
|
|
+ node_status => StatusL,
|
|
|
+ hooks => emqx_exhook_mgr:hooks(ServerName)
|
|
|
+ }.
|
|
|
+
|
|
|
+%%--------------------------------------------------------------------
|
|
|
+%% GET /exhooks/{name}
|
|
|
+%%--------------------------------------------------------------------
|
|
|
+nodes_server_info(Name) ->
|
|
|
+ InfoL = call_cluster(emqx_exhook_mgr, server_info, [Name]),
|
|
|
+ Default = emqx_exhook_metrics:new_metrics_info(),
|
|
|
+ nodes_server_info(InfoL, Name, Default, [], []).
|
|
|
+
|
|
|
+nodes_server_info([{Node, {error, _}} | T], Name, Default, StatusL, MetricsL) ->
|
|
|
+ nodes_server_info(T,
|
|
|
+ Name,
|
|
|
+ Default,
|
|
|
+ [#{node => Node, status => error} | StatusL],
|
|
|
+ [#{node => Node, metrics => Default} | MetricsL]
|
|
|
+ );
|
|
|
+
|
|
|
+nodes_server_info([{Node, Result} | T], Name, Default, StatusL, MetricsL) ->
|
|
|
+ #{status := Status, metrics := Metrics} = Result,
|
|
|
+ nodes_server_info(T,
|
|
|
+ Name,
|
|
|
+ Default,
|
|
|
+ [#{node => Node, status => Status} | StatusL],
|
|
|
+ [#{node => Node, metrics => Metrics} | MetricsL]
|
|
|
+ );
|
|
|
+
|
|
|
+nodes_server_info([], Name, _, StatusL, MetricsL) ->
|
|
|
+ #{metrics => emqx_exhook_metrics:metrics_aggregate_by_key(metrics, MetricsL),
|
|
|
+ node_status => StatusL,
|
|
|
+ node_metrics => MetricsL,
|
|
|
+ hooks => emqx_exhook_mgr:hooks(Name)
|
|
|
+ }.
|
|
|
+
|
|
|
+%%--------------------------------------------------------------------
|
|
|
+%% GET /exhooks/{name}/hooks
|
|
|
+%%--------------------------------------------------------------------
|
|
|
+get_nodes_server_hooks_info(Name) ->
|
|
|
+ case emqx_exhook_mgr:hooks(Name) of
|
|
|
+ [] -> [];
|
|
|
+ Hooks ->
|
|
|
+ AllInfos = call_cluster(emqx_exhook_mgr, server_hooks_metrics, [Name]),
|
|
|
+ Default = emqx_exhook_metrics:new_metrics_info(),
|
|
|
+ get_nodes_server_hooks_info(Hooks, AllInfos, Default, [])
|
|
|
+ end.
|
|
|
+
|
|
|
+get_nodes_server_hooks_info([#{name := Name} = Spec | T], AllInfos, Default, Acc) ->
|
|
|
+ Info = fill_server_hooks_info(AllInfos, Name, Default, []),
|
|
|
+ AllInfo = maps:merge(Spec, Info),
|
|
|
+ get_nodes_server_hooks_info(T, AllInfos, Default, [AllInfo | Acc]);
|
|
|
+
|
|
|
+get_nodes_server_hooks_info([], _, _, Acc) ->
|
|
|
+ Acc.
|
|
|
+
|
|
|
+fill_server_hooks_info([{_, {error, _}} | T], Name, Default, MetricsL) ->
|
|
|
+ fill_server_hooks_info(T, Name, Default, MetricsL);
|
|
|
+
|
|
|
+fill_server_hooks_info([{Node, MetricsMap} | T], Name, Default, MetricsL) ->
|
|
|
+ Metrics = maps:get(Name, MetricsMap, Default),
|
|
|
+ NodeMetrics = #{node => Node, metrics => Metrics},
|
|
|
+ fill_server_hooks_info(T, Name, Default, [NodeMetrics | MetricsL]);
|
|
|
+
|
|
|
+fill_server_hooks_info([], _Name, _Default, MetricsL) ->
|
|
|
+ Metrics = emqx_exhook_metrics:metrics_aggregate_by_key(metrics, MetricsL),
|
|
|
+ #{metrics => Metrics, node_metrics => MetricsL}.
|
|
|
+
|
|
|
+%%--------------------------------------------------------------------
|
|
|
+%% cluster call
|
|
|
+%%--------------------------------------------------------------------
|
|
|
call_cluster(Module, Fun, Args) ->
|
|
|
Nodes = mria_mnesia:running_nodes(),
|
|
|
[{Node, rpc_call(Node, Module, Fun, Args)} || Node <- Nodes].
|