From 9e6c63b91b5022931cd08db6dc60cfecda4e1035 Mon Sep 17 00:00:00 2001 From: turtled Date: Thu, 17 Aug 2017 15:51:20 +0800 Subject: [PATCH] REST API add modify_config/config_list --- include/emqttd_internal.hrl | 1 + src/emqttd_cli_config.erl | 75 ++++++++++++++++++++++++++++++++++++- src/emqttd_config.erl | 18 ++++++--- src/emqttd_ctl.erl | 3 ++ src/emqttd_mgmt.erl | 22 +++++++++++ src/emqttd_rest_api.erl | 44 ++++++++++++++++++++++ 6 files changed, 155 insertions(+), 8 deletions(-) diff --git a/include/emqttd_internal.hrl b/include/emqttd_internal.hrl index 68e2b6ba6..06398e31f 100644 --- a/include/emqttd_internal.hrl +++ b/include/emqttd_internal.hrl @@ -72,4 +72,5 @@ -define(ERROR10, 110). %% Plugin has been loaded -define(ERROR11, 111). %% Plugin has been loaded -define(ERROR12, 112). %% Client not online +-define(ERROR13, 113). %% Modify config fail diff --git a/src/emqttd_cli_config.erl b/src/emqttd_cli_config.erl index 2c6ed0e37..d2fcf09c5 100644 --- a/src/emqttd_cli_config.erl +++ b/src/emqttd_cli_config.erl @@ -16,7 +16,13 @@ -module (emqttd_cli_config). --export ([register_config_cli/0, register_config/0, run/1]). +-export ([register_config_cli/0, + register_config/0, + run/1, + set_usage/0, + all_cfgs/0, + get_cfg/2, + get_cfg/3]). -define(APP, emqttd). @@ -46,6 +52,54 @@ register_config_cli() -> register_broker_config(), register_lager_config(). +set_usage() -> + io:format("~-40s# ~-20s# ~-20s ~p~n", ["key", "value", "datatype", "app"]), + io:format("------------------------------------------------------------------------------------------------~n"), + lists:foreach(fun({Key, Val, Datatype, App}) -> + io:format("~-40s# ~-20s# ~-20s ~p~n", [Key, Val, Datatype, App]) + end, all_cfgs()), + io:format("------------------------------------------------------------------------------------------------~n"), + io:format("Usage: set key=value --app=appname~n"). + +all_cfgs() -> + {Mappings, Mappings1} = lists:foldl( + fun({Key, {_, Map, _}}, {Acc, Acc1}) -> + Map1 = lists:map(fun(M) -> {cuttlefish_mapping:variable(M), Key} end, Map), + {Acc ++ Map, Acc1 ++ Map1} + end, {[], []}, ets:tab2list(clique_schema)), + lists:foldl(fun({Key, _}, Acc) -> + case lists:keyfind(cuttlefish_variable:tokenize(Key), 2, Mappings) of + false -> Acc; + Map -> + Datatype = format_datatype(cuttlefish_mapping:datatype(Map)), + App = proplists:get_value(cuttlefish_variable:tokenize(Key), Mappings1), + [{_, [Val0]}] = clique_config:show([Key], [{app, App}]), + Val = any_to_string(proplists:get_value(Key, Val0)), + [{Key, Val, Datatype, App} | Acc] + end + end, [],lists:sort(ets:tab2list(clique_config))). + +get_cfg(App, Key) -> + get_cfg(App, Key, undefined). + +get_cfg(App, Key, Def) -> + [{_, [Val0]}] = clique_config:show([Key], [{app, App}]), + proplists:get_value(Key, Val0, Def). + +format_datatype(Value) -> + format_datatype(Value, ""). + +format_datatype([Head], Acc) when is_tuple(Head) -> + [Head1 | _] = erlang:tuple_to_list(Head), + lists:concat([Acc, Head1]); +format_datatype([Head], Acc) -> + lists:concat([Acc, Head]); +format_datatype([Head | Tail], Acc) when is_tuple(Head)-> + [Head1 | _] = erlang:tuple_to_list(Head), + format_datatype(Tail, Acc ++ lists:concat([Head1, ", "])); +format_datatype([Head | Tail], Acc) -> + format_datatype(Tail, Acc ++ lists:concat([Head, ", "])). + %%-------------------------------------------------------------------- %% Auth/Acl %%-------------------------------------------------------------------- @@ -72,6 +126,8 @@ register_protocol_formatter() -> "keepalive_backoff"], [clique:register_formatter(["mqtt", Key], fun protocol_formatter_callback/2) || Key <- ConfigKeys]. +protocol_formatter_callback([_, "websocket_protocol_header"], Params) -> + Params; protocol_formatter_callback([_, Key], Params) -> proplists:get_value(l2a(Key), Params). @@ -85,7 +141,7 @@ register_protocol_config() -> protocol_config_callback([_AppStr, KeyStr], Value) -> protocol_config_callback(protocol, l2a(KeyStr), Value). -protocol_config_callback(App, websocket_protocol_header, Value) -> +protocol_config_callback(_App, websocket_protocol_header, Value) -> application:set_env(?APP, websocket_protocol_header, Value), " successfully\n"; protocol_config_callback(App, Key, Value) -> @@ -271,3 +327,18 @@ lager_config_callback(_, Value) -> register_config_whitelist(ConfigKeys) -> clique:register_config_whitelist(ConfigKeys, ?APP). +%%-------------------------------------------------------------------- +%% Inner Function +%%-------------------------------------------------------------------- +any_to_string(I) when is_integer(I) -> + integer_to_list(I); +any_to_string(F) when is_float(F)-> + float_to_list(F,[{decimals, 4}]); +any_to_string(A) when is_atom(A) -> + atom_to_list(A); +any_to_string(B) when is_binary(B) -> + binary_to_list(B); +any_to_string(L) when is_list(L) -> + L. + + diff --git a/src/emqttd_config.erl b/src/emqttd_config.erl index 06256bbfd..77a4e8949 100644 --- a/src/emqttd_config.erl +++ b/src/emqttd_config.erl @@ -52,15 +52,21 @@ dump(_App, _Terms) -> %% TODO ok. --spec(set(atom(), atom(), term()) -> ok). +-spec(set(atom(), list(), list()) -> ok). set(App, Par, Val) -> - application:set_env(App, Par, Val). + emqttd_cli_config:run(["config", + "set", + lists:concat([Par, "=", Val]), + lists:concat(["--app=", App])]). --spec(get(atom(), atom()) -> undefined | {ok, term()}). +-spec(get(atom(), list()) -> undefined | {ok, term()}). get(App, Par) -> - application:get_env(App, Par). + case emqttd_cli_config:get_cfg(App, Par) of + undefined -> undefined; + Val -> {ok, Val} + end. --spec(get(atom(), atom(), atom()) -> term()). +-spec(get(atom(), list(), atom()) -> term()). get(App, Par, Def) -> - application:get_env(App, Par, Def). + emqttd_cli_config:get_cfg(App, Par, Def). diff --git a/src/emqttd_ctl.erl b/src/emqttd_ctl.erl index 0cec222fd..77769e3c8 100644 --- a/src/emqttd_ctl.erl +++ b/src/emqttd_ctl.erl @@ -68,6 +68,9 @@ run([]) -> usage(), ok; run(["help"]) -> usage(), ok; +run(["set"] = CmdS) when length(CmdS) =:= 1 -> + emqttd_cli_config:set_usage(), ok; + run(["set" | _] = CmdS) -> emqttd_cli_config:run(["config" | CmdS]), ok; diff --git a/src/emqttd_mgmt.erl b/src/emqttd_mgmt.erl index 0561b86a5..56675bbea 100644 --- a/src/emqttd_mgmt.erl +++ b/src/emqttd_mgmt.erl @@ -43,6 +43,8 @@ -export([kick_client/1, clean_acl_cache/2]). +-export([modify_config/3, modify_config/4, get_configs/0, get_config/1]). + -define(KB, 1024). -define(MB, (1024*1024)). -define(GB, (1024*1024*1024)). @@ -312,6 +314,26 @@ clean_acl_cache(Node, ClientId, Topic) when Node =:= node() -> clean_acl_cache(Node, ClientId, Topic) -> rpc_call(Node, clean_acl_cache, [Node, ClientId, Topic]). +%%-------------------------------------------------------------------- +%% Config ENV +%%-------------------------------------------------------------------- +modify_config(App, Key, Value) -> + Result = [modify_config(Node, App, Key, Value) || Node <- ekka_mnesia:running_nodes()], + lists:any(fun(Item) -> Item =:= ok end, Result). + +modify_config(Node, App, Key, Value) when Node =:= node() -> + emqttd_config:set(App, Key, Value); +modify_config(Node, App, Key, Value) -> + rpc_call(Node, modify_config, [Node, App, Key, Value]). + +get_configs() -> + [{Node, get_config(Node)} || Node <- ekka_mnesia:running_nodes()]. + +get_config(Node) when Node =:= node()-> + emqttd_cli_config:all_cfgs(); +get_config(Node) -> + rpc_call(Node, get_config, [Node]). + %%-------------------------------------------------------------------- %% Internel Functions. %%-------------------------------------------------------------------- diff --git a/src/emqttd_rest_api.erl b/src/emqttd_rest_api.erl index 9129c2c14..91d75c483 100644 --- a/src/emqttd_rest_api.erl +++ b/src/emqttd_rest_api.erl @@ -56,6 +56,10 @@ -http_api({"^nodes/(.+?)/plugins/?$", 'GET', plugin_list, []}). -http_api({"^nodes/(.+?)/plugins/(.+?)/?$", 'PUT', enabled, [{<<"active">>, bool}]}). +-http_api({"^config/(.+?)/?$", 'PUT', modify_config, [{<<"key">>, binary}, {<<"value">>, binary}]}). +-http_api({"^config/?$", 'GET', config_list, []}). +-http_api({"^nodes/(.+?)/config/(.+?)/?$", 'PUT', modify_config, [{<<"key">>, binary}, {<<"value">>, binary}]}). +-http_api({"^nodes/(.+?)/config/?$", 'GET', config_list, []}). -export([alarm_list/3]). -export([client/3, client_list/3, client_list/4, kick_client/3, clean_acl_cache/3]). -export([route/3, route_list/2]). @@ -64,6 +68,7 @@ -export([nodes/2, node/3, brokers/2, broker/3, listeners/2, listener/3, metrics/2, metric/3, stats/2, stat/3]). -export([publish/2, subscribe/2, unsubscribe/2]). -export([plugin_list/3, enabled/4]). +-export([modify_config/3, modify_config/4, config_list/2, config_list/3]). %%-------------------------------------------------------------------------- %% alarm @@ -363,6 +368,44 @@ plugin(#mqtt_plugin{name = Name, version = Ver, descr = Descr, {description, iolist_to_binary(Descr)}, {active, Active}]. +%%-------------------------------------------------------------------------- +%% modify config +%%-------------------------------------------------------------------------- +modify_config('PUT', Params, App) -> + Key = proplists:get_value(<<"key">>, Params, <<"">>), + Value = proplists:get_value(<<"value">>, Params, <<"">>), + case emqttd_mgmt:modify_config(l2a(App), b2l(Key), b2l(Value)) of + true -> {ok, []}; + false -> {error, [{code, ?ERROR13}]} + end. + +modify_config('PUT', Params, Node, App) -> + Key = proplists:get_value(<<"key">>, Params, <<"">>), + Value = proplists:get_value(<<"value">>, Params, <<"">>), + case emqttd_mgmt:modify_config(l2a(Node), l2a(App), b2l(Key), b2l(Value)) of + ok -> {ok, []}; + _ -> {error, [{code, ?ERROR13}]} + end. + +config_list('GET', _Params) -> + Data = emqttd_mgmt:get_configs(), + {ok, [{Node, format_config(Config, [])} || {Node, Config} <- Data]}. + +config_list('GET', _Params, Node) -> + Data = emqttd_mgmt:get_config(l2a(Node)), + {ok, [format_config(Config) || Config <- Data]}. + +format_config([], Acc) -> + Acc; +format_config([{Key, Value, Datatpye, App}| Configs], Acc) -> + format_config(Configs, [format_config({Key, Value, Datatpye, App}) | Acc]). + +format_config({Key, Value, Datatpye, App}) -> + [{<<"key">>, l2b(Key)}, + {<<"value">>, l2b(Value)}, + {<<"datatpye">>, l2b(Datatpye)}, + {<<"app">>, App}]. + %%-------------------------------------------------------------------------- %% Inner function %%-------------------------------------------------------------------------- @@ -399,6 +442,7 @@ bin(undefined) -> <<>>. int(L) -> list_to_integer(L). l2a(L) -> list_to_atom(L). l2b(L) -> list_to_binary(L). +b2l(B) -> binary_to_list(B). page_params(Params) ->