diff options
author | Simon MacMullen <simon@rabbitmq.com> | 2014-09-16 17:52:01 +0100 |
---|---|---|
committer | Simon MacMullen <simon@rabbitmq.com> | 2014-09-16 17:52:01 +0100 |
commit | 06a30fdb757b93f976989d62d047e5b6a6553e42 (patch) | |
tree | f4bbf4799869c2f2d6eb36a3302965febdc7ce97 | |
parent | d790b58cb380d2b1b6825e6bf1f3dd004da3a07b (diff) | |
parent | 5763fadffc8328903641ed77ff2c7a28cb2067e9 (diff) | |
download | rabbitmq-server-06a30fdb757b93f976989d62d047e5b6a6553e42.tar.gz |
Merge bug25955
-rw-r--r-- | include/rabbit_cli.hrl | 48 | ||||
-rw-r--r-- | src/rabbit_cli.erl | 184 | ||||
-rw-r--r-- | src/rabbit_control_main.erl | 130 | ||||
-rw-r--r-- | src/rabbit_misc.erl | 66 | ||||
-rw-r--r-- | src/rabbit_plugins_main.erl | 233 | ||||
-rw-r--r-- | src/rabbit_tests.erl | 2 |
6 files changed, 327 insertions, 336 deletions
diff --git a/include/rabbit_cli.hrl b/include/rabbit_cli.hrl new file mode 100644 index 00000000..ddb4f4d0 --- /dev/null +++ b/include/rabbit_cli.hrl @@ -0,0 +1,48 @@ +%% The contents of this file are subject to the Mozilla Public License +%% Version 1.1 (the "License"); you may not use this file except in +%% compliance with the License. You may obtain a copy of the License +%% at http://www.mozilla.org/MPL/ +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and +%% limitations under the License. +%% +%% The Original Code is RabbitMQ. +%% +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% + +-define(NODE_OPT, "-n"). +-define(QUIET_OPT, "-q"). +-define(VHOST_OPT, "-p"). + +-define(VERBOSE_OPT, "-v"). +-define(MINIMAL_OPT, "-m"). +-define(ENABLED_OPT, "-E"). +-define(ENABLED_ALL_OPT, "-e"). + +-define(PRIORITY_OPT, "--priority"). +-define(APPLY_TO_OPT, "--apply-to"). +-define(RAM_OPT, "--ram"). +-define(OFFLINE_OPT, "--offline"). +-define(ONLINE_OPT, "--online"). + + +-define(NODE_DEF(Node), {?NODE_OPT, {option, Node}}). +-define(QUIET_DEF, {?QUIET_OPT, flag}). +-define(VHOST_DEF, {?VHOST_OPT, {option, "/"}}). + +-define(VERBOSE_DEF, {?VERBOSE_OPT, flag}). +-define(MINIMAL_DEF, {?MINIMAL_OPT, flag}). +-define(ENABLED_DEF, {?ENABLED_OPT, flag}). +-define(ENABLED_ALL_DEF, {?ENABLED_ALL_OPT, flag}). + +-define(PRIORITY_DEF, {?PRIORITY_OPT, {option, "0"}}). +-define(APPLY_TO_DEF, {?APPLY_TO_OPT, {option, "all"}}). +-define(RAM_DEF, {?RAM_OPT, flag}). +-define(OFFLINE_DEF, {?OFFLINE_OPT, flag}). +-define(ONLINE_DEF, {?ONLINE_OPT, flag}). + +-define(RPC_TIMEOUT, infinity). diff --git a/src/rabbit_cli.erl b/src/rabbit_cli.erl new file mode 100644 index 00000000..0c19c727 --- /dev/null +++ b/src/rabbit_cli.erl @@ -0,0 +1,184 @@ +%% The contents of this file are subject to the Mozilla Public License +%% Version 1.1 (the "License"); you may not use this file except in +%% compliance with the License. You may obtain a copy of the License +%% at http://www.mozilla.org/MPL/ +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and +%% limitations under the License. +%% +%% The Original Code is RabbitMQ. +%% +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% + +-module(rabbit_cli). +-include("rabbit_cli.hrl"). + +-export([main/3, parse_arguments/4]). + +%%---------------------------------------------------------------------------- + +-ifdef(use_specs). + +-type(optdef() :: flag | {option, string()}). + +-spec(parse_arguments/4 :: + ([{atom(), [{string(), optdef()}]} | atom()], + [{string(), optdef()}], + string(), + [string()]) + -> {'ok', {atom(), [{string(), string()}], [string()]}} | + 'no_command'). + +-endif. + +%%---------------------------------------------------------------------------- + +main(ParseFun, DoFun, UsageMod) -> + {ok, [[NodeStr|_]|_]} = init:get_argument(nodename), + {Command, Opts, Args} = + case ParseFun(init:get_plain_arguments(), NodeStr) of + {ok, Res} -> Res; + no_command -> print_error("could not recognise command", []), + usage(UsageMod) + end, + Node = proplists:get_value(?NODE_OPT, Opts), + PrintInvalidCommandError = + fun () -> + print_error("invalid command '~s'", + [string:join([atom_to_list(Command) | Args], " ")]) + end, + + %% The reason we don't use a try/catch here is that rpc:call turns + %% thrown errors into normal return values + case catch DoFun(Command, Node, Args, Opts) of + ok -> + rabbit_misc:quit(0); + {'EXIT', {function_clause, [{?MODULE, action, _} | _]}} -> %% < R15 + PrintInvalidCommandError(), + usage(UsageMod); + {'EXIT', {function_clause, [{?MODULE, action, _, _} | _]}} -> %% >= R15 + PrintInvalidCommandError(), + usage(UsageMod); + {error, {missing_dependencies, Missing, Blame}} -> + print_error("dependent plugins ~p not found; used by ~p.", + [Missing, Blame]), + rabbit_misc:quit(2); + {'EXIT', {badarg, _}} -> + print_error("invalid parameter: ~p", [Args]), + usage(UsageMod); + {error, {Problem, Reason}} when is_atom(Problem), is_binary(Reason) -> + %% We handle this common case specially to avoid ~p since + %% that has i18n issues + print_error("~s: ~s", [Problem, Reason]), + rabbit_misc:quit(2); + {error, Reason} -> + print_error("~p", [Reason]), + rabbit_misc:quit(2); + {error_string, Reason} -> + print_error("~s", [Reason]), + rabbit_misc:quit(2); + {badrpc, {'EXIT', Reason}} -> + print_error("~p", [Reason]), + rabbit_misc:quit(2); + {badrpc, Reason} -> + print_error("unable to connect to node ~w: ~w", [Node, Reason]), + print_badrpc_diagnostics([Node]), + rabbit_misc:quit(2); + {badrpc_multi, Reason, Nodes} -> + print_error("unable to connect to nodes ~p: ~w", [Nodes, Reason]), + print_badrpc_diagnostics(Nodes), + rabbit_misc:quit(2); + Other -> + print_error("~p", [Other]), + rabbit_misc:quit(2) + end. + +usage(Mod) -> + io:format("~s", [Mod:usage()]), + rabbit_misc:quit(1). + +%%---------------------------------------------------------------------------- + +parse_arguments(Commands, GlobalDefs, NodeOpt, CmdLine) -> + case parse_arguments(Commands, GlobalDefs, CmdLine) of + {ok, {Cmd, Opts0, Args}} -> + Opts = [case K of + NodeOpt -> {NodeOpt, rabbit_nodes:make(V)}; + _ -> {K, V} + end || {K, V} <- Opts0], + {ok, {Cmd, Opts, Args}}; + E -> + E + end. + +%% Takes: +%% * A list of [{atom(), [{string(), optdef()]} | atom()], where the atom()s +%% are the accepted commands and the optional [string()] is the list of +%% accepted options for that command +%% * A list [{string(), optdef()}] of options valid for all commands +%% * The list of arguments given by the user +%% +%% Returns either {ok, {atom(), [{string(), string()}], [string()]} which are +%% respectively the command, the key-value pairs of the options and the leftover +%% arguments; or no_command if no command could be parsed. +parse_arguments(Commands, GlobalDefs, As) -> + lists:foldl(maybe_process_opts(GlobalDefs, As), no_command, Commands). + +maybe_process_opts(GDefs, As) -> + fun({C, Os}, no_command) -> + process_opts(atom_to_list(C), dict:from_list(GDefs ++ Os), As); + (C, no_command) -> + (maybe_process_opts(GDefs, As))({C, []}, no_command); + (_, {ok, Res}) -> + {ok, Res} + end. + +process_opts(C, Defs, As0) -> + KVs0 = dict:map(fun (_, flag) -> false; + (_, {option, V}) -> V + end, Defs), + process_opts(Defs, C, As0, not_found, KVs0, []). + +%% Consume flags/options until you find the correct command. If there are no +%% arguments or the first argument is not the command we're expecting, fail. +%% Arguments to this are: definitions, cmd we're looking for, args we +%% haven't parsed, whether we have found the cmd, options we've found, +%% plain args we've found. +process_opts(_Defs, C, [], found, KVs, Outs) -> + {ok, {list_to_atom(C), dict:to_list(KVs), lists:reverse(Outs)}}; +process_opts(_Defs, _C, [], not_found, _, _) -> + no_command; +process_opts(Defs, C, [A | As], Found, KVs, Outs) -> + OptType = case dict:find(A, Defs) of + error -> none; + {ok, flag} -> flag; + {ok, {option, _}} -> option + end, + case {OptType, C, Found} of + {flag, _, _} -> process_opts( + Defs, C, As, Found, dict:store(A, true, KVs), + Outs); + {option, _, _} -> case As of + [] -> no_command; + [V | As1] -> process_opts( + Defs, C, As1, Found, + dict:store(A, V, KVs), Outs) + end; + {none, A, _} -> process_opts(Defs, C, As, found, KVs, Outs); + {none, _, found} -> process_opts(Defs, C, As, found, KVs, [A | Outs]); + {none, _, _} -> no_command + end. + +%%---------------------------------------------------------------------------- + +fmt_stderr(Format, Args) -> rabbit_misc:format_stderr(Format ++ "~n", Args). + +print_error(Format, Args) -> fmt_stderr("Error: " ++ Format, Args). + +print_badrpc_diagnostics(Nodes) -> + fmt_stderr(rabbit_nodes:diagnostics(Nodes), []). + diff --git a/src/rabbit_control_main.erl b/src/rabbit_control_main.erl index 70fe10e6..afc2caa0 100644 --- a/src/rabbit_control_main.erl +++ b/src/rabbit_control_main.erl @@ -16,29 +16,13 @@ -module(rabbit_control_main). -include("rabbit.hrl"). +-include("rabbit_cli.hrl"). -export([start/0, stop/0, parse_arguments/2, action/5, sync_queue/1, cancel_sync_queue/1]). --define(RPC_TIMEOUT, infinity). -define(EXTERNAL_CHECK_INTERVAL, 1000). --define(QUIET_OPT, "-q"). --define(NODE_OPT, "-n"). --define(VHOST_OPT, "-p"). --define(PRIORITY_OPT, "--priority"). --define(APPLY_TO_OPT, "--apply-to"). --define(RAM_OPT, "--ram"). --define(OFFLINE_OPT, "--offline"). - --define(QUIET_DEF, {?QUIET_OPT, flag}). --define(NODE_DEF(Node), {?NODE_OPT, {option, Node}}). --define(VHOST_DEF, {?VHOST_OPT, {option, "/"}}). --define(PRIORITY_DEF, {?PRIORITY_OPT, {option, "0"}}). --define(APPLY_TO_DEF, {?APPLY_TO_OPT, {option, "all"}}). --define(RAM_DEF, {?RAM_OPT, flag}). --define(OFFLINE_DEF, {?OFFLINE_OPT, flag}). - -define(GLOBAL_DEFS(Node), [?QUIET_DEF, ?NODE_DEF(Node)]). -define(COMMANDS, @@ -131,7 +115,6 @@ (atom(), node(), [string()], [{string(), any()}], fun ((string(), [any()]) -> 'ok')) -> 'ok'). --spec(usage/0 :: () -> no_return()). -endif. @@ -139,79 +122,24 @@ start() -> start_distribution(), - {ok, [[NodeStr|_]|_]} = init:get_argument(nodename), - {Command, Opts, Args} = - case parse_arguments(init:get_plain_arguments(), NodeStr) of - {ok, Res} -> Res; - no_command -> print_error("could not recognise command", []), - usage() - end, - Quiet = proplists:get_bool(?QUIET_OPT, Opts), - Node = proplists:get_value(?NODE_OPT, Opts), - Inform = case Quiet of - true -> fun (_Format, _Args1) -> ok end; - false -> fun (Format, Args1) -> - io:format(Format ++ " ...~n", Args1) - end - end, - PrintInvalidCommandError = - fun () -> - print_error("invalid command '~s'", - [string:join([atom_to_list(Command) | Args], " ")]) - end, - - %% The reason we don't use a try/catch here is that rpc:call turns - %% thrown errors into normal return values - case catch do_action(Command, Node, Args, Opts, Inform) of - ok -> - case Quiet of - true -> ok; - false -> io:format("...done.~n") - end, - rabbit_misc:quit(0); - {ok, Info} -> - case Quiet of - true -> ok; - false -> io:format("...done (~p).~n", [Info]) - end, - rabbit_misc:quit(0); - {'EXIT', {function_clause, [{?MODULE, action, _} | _]}} -> %% < R15 - PrintInvalidCommandError(), - usage(); - {'EXIT', {function_clause, [{?MODULE, action, _, _} | _]}} -> %% >= R15 - PrintInvalidCommandError(), - usage(); - {'EXIT', {badarg, _}} -> - print_error("invalid parameter: ~p", [Args]), - usage(); - {error, {Problem, Reason}} when is_atom(Problem), is_binary(Reason) -> - %% We handle this common case specially to avoid ~p since - %% that has i18n issues - print_error("~s: ~s", [Problem, Reason]), - rabbit_misc:quit(2); - {error, Reason} -> - print_error("~p", [Reason]), - rabbit_misc:quit(2); - {error_string, Reason} -> - print_error("~s", [Reason]), - rabbit_misc:quit(2); - {badrpc, {'EXIT', Reason}} -> - print_error("~p", [Reason]), - rabbit_misc:quit(2); - {badrpc, Reason} -> - print_error("unable to connect to node ~w: ~w", [Node, Reason]), - print_badrpc_diagnostics([Node]), - rabbit_misc:quit(2); - {badrpc_multi, Reason, Nodes} -> - print_error("unable to connect to nodes ~p: ~w", [Nodes, Reason]), - print_badrpc_diagnostics(Nodes), - rabbit_misc:quit(2); - Other -> - print_error("~p", [Other]), - rabbit_misc:quit(2) - end. + rabbit_cli:main( + fun (Args, NodeStr) -> + parse_arguments(Args, NodeStr) + end, + fun (Command, Node, Args, Opts) -> + Quiet = proplists:get_bool(?QUIET_OPT, Opts), + Inform = case Quiet of + true -> fun (_Format, _Args1) -> ok end; + false -> fun (Format, Args1) -> + io:format(Format ++ " ...~n", Args1) + end + end, + do_action(Command, Node, Args, Opts, Inform) + end, rabbit_ctl_usage). -fmt_stderr(Format, Args) -> rabbit_misc:format_stderr(Format ++ "~n", Args). +parse_arguments(CmdLine, NodeStr) -> + rabbit_cli:parse_arguments( + ?COMMANDS, ?GLOBAL_DEFS(NodeStr), ?NODE_OPT, CmdLine). print_report(Node, {Descr, Module, InfoFun, KeysFun}) -> io:format("~s:~n", [Descr]), @@ -230,31 +158,9 @@ print_report0(Node, {Module, InfoFun, KeysFun}, VHostArg) -> end, io:nl(). -print_error(Format, Args) -> fmt_stderr("Error: " ++ Format, Args). - -print_badrpc_diagnostics(Nodes) -> - fmt_stderr(rabbit_nodes:diagnostics(Nodes), []). - stop() -> ok. -usage() -> - io:format("~s", [rabbit_ctl_usage:usage()]), - rabbit_misc:quit(1). - -parse_arguments(CmdLine, NodeStr) -> - case rabbit_misc:parse_arguments( - ?COMMANDS, ?GLOBAL_DEFS(NodeStr), CmdLine) of - {ok, {Cmd, Opts0, Args}} -> - Opts = [case K of - ?NODE_OPT -> {?NODE_OPT, rabbit_nodes:make(V)}; - _ -> {K, V} - end || {K, V} <- Opts0], - {ok, {Cmd, Opts, Args}}; - E -> - E - end. - %%---------------------------------------------------------------------------- do_action(Command, Node, Args, Opts, Inform) -> diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index c5b566de..3696698c 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -49,7 +49,6 @@ -export([version_minor_equivalent/2]). -export([dict_cons/3, orddict_cons/3, gb_trees_cons/3]). -export([gb_trees_fold/3, gb_trees_foreach/2]). --export([parse_arguments/3]). -export([all_module_attributes/1, build_acyclic_graph/3]). -export([now_ms/0]). -export([const/1]). @@ -86,7 +85,6 @@ -type(ok_or_error() :: rabbit_types:ok_or_error(any())). -type(thunk(T) :: fun(() -> T)). -type(resource_name() :: binary()). --type(optdef() :: flag | {option, string()}). -type(channel_or_connection_exit() :: rabbit_types:channel_exit() | rabbit_types:connection_exit()). -type(digraph_label() :: term()). @@ -207,12 +205,6 @@ -spec(gb_trees_fold/3 :: (fun ((any(), any(), A) -> A), A, gb_tree()) -> A). -spec(gb_trees_foreach/2 :: (fun ((any(), any()) -> any()), gb_tree()) -> 'ok'). --spec(parse_arguments/3 :: - ([{atom(), [{string(), optdef()}]} | atom()], - [{string(), optdef()}], - [string()]) - -> {'ok', {atom(), [{string(), string()}], [string()]}} | - 'no_command'). -spec(all_module_attributes/1 :: (atom()) -> [{atom(), atom(), [term()]}]). -spec(build_acyclic_graph/3 :: @@ -786,64 +778,6 @@ gb_trees_fold1(Fun, Acc, {Key, Val, It}) -> gb_trees_foreach(Fun, Tree) -> gb_trees_fold(fun (Key, Val, Acc) -> Fun(Key, Val), Acc end, ok, Tree). -%% Takes: -%% * A list of [{atom(), [{string(), optdef()]} | atom()], where the atom()s -%% are the accepted commands and the optional [string()] is the list of -%% accepted options for that command -%% * A list [{string(), optdef()}] of options valid for all commands -%% * The list of arguments given by the user -%% -%% Returns either {ok, {atom(), [{string(), string()}], [string()]} which are -%% respectively the command, the key-value pairs of the options and the leftover -%% arguments; or no_command if no command could be parsed. -parse_arguments(Commands, GlobalDefs, As) -> - lists:foldl(maybe_process_opts(GlobalDefs, As), no_command, Commands). - -maybe_process_opts(GDefs, As) -> - fun({C, Os}, no_command) -> - process_opts(atom_to_list(C), dict:from_list(GDefs ++ Os), As); - (C, no_command) -> - (maybe_process_opts(GDefs, As))({C, []}, no_command); - (_, {ok, Res}) -> - {ok, Res} - end. - -process_opts(C, Defs, As0) -> - KVs0 = dict:map(fun (_, flag) -> false; - (_, {option, V}) -> V - end, Defs), - process_opts(Defs, C, As0, not_found, KVs0, []). - -%% Consume flags/options until you find the correct command. If there are no -%% arguments or the first argument is not the command we're expecting, fail. -%% Arguments to this are: definitions, cmd we're looking for, args we -%% haven't parsed, whether we have found the cmd, options we've found, -%% plain args we've found. -process_opts(_Defs, C, [], found, KVs, Outs) -> - {ok, {list_to_atom(C), dict:to_list(KVs), lists:reverse(Outs)}}; -process_opts(_Defs, _C, [], not_found, _, _) -> - no_command; -process_opts(Defs, C, [A | As], Found, KVs, Outs) -> - OptType = case dict:find(A, Defs) of - error -> none; - {ok, flag} -> flag; - {ok, {option, _}} -> option - end, - case {OptType, C, Found} of - {flag, _, _} -> process_opts( - Defs, C, As, Found, dict:store(A, true, KVs), - Outs); - {option, _, _} -> case As of - [] -> no_command; - [V | As1] -> process_opts( - Defs, C, As1, Found, - dict:store(A, V, KVs), Outs) - end; - {none, A, _} -> process_opts(Defs, C, As, found, KVs, Outs); - {none, _, found} -> process_opts(Defs, C, As, found, KVs, [A | Outs]); - {none, _, _} -> no_command - end. - now_ms() -> timer:now_diff(now(), {0,0,0}) div 1000. diff --git a/src/rabbit_plugins_main.erl b/src/rabbit_plugins_main.erl index 278fcf98..57a98f1f 100644 --- a/src/rabbit_plugins_main.erl +++ b/src/rabbit_plugins_main.erl @@ -16,27 +16,9 @@ -module(rabbit_plugins_main). -include("rabbit.hrl"). +-include("rabbit_cli.hrl"). -export([start/0, stop/0]). --export([action/6]). - --define(NODE_OPT, "-n"). --define(VERBOSE_OPT, "-v"). --define(MINIMAL_OPT, "-m"). --define(ENABLED_OPT, "-E"). --define(ENABLED_ALL_OPT, "-e"). --define(OFFLINE_OPT, "--offline"). --define(ONLINE_OPT, "--online"). - --define(NODE_DEF(Node), {?NODE_OPT, {option, Node}}). --define(VERBOSE_DEF, {?VERBOSE_OPT, flag}). --define(MINIMAL_DEF, {?MINIMAL_OPT, flag}). --define(ENABLED_DEF, {?ENABLED_OPT, flag}). --define(ENABLED_ALL_DEF, {?ENABLED_ALL_OPT, flag}). --define(OFFLINE_DEF, {?OFFLINE_OPT, flag}). --define(ONLINE_DEF, {?ONLINE_OPT, flag}). - --define(RPC_TIMEOUT, infinity). -define(GLOBAL_DEFS(Node), [?NODE_DEF(Node)]). @@ -53,61 +35,32 @@ -spec(start/0 :: () -> no_return()). -spec(stop/0 :: () -> 'ok'). --spec(usage/0 :: () -> no_return()). -endif. %%---------------------------------------------------------------------------- -start() -> - {ok, [[PluginsFile|_]|_]} = - init:get_argument(enabled_plugins_file), - {ok, [[NodeStr|_]|_]} = init:get_argument(nodename), - {ok, [[PluginsDir|_]|_]} = init:get_argument(plugins_dist_dir), - {Command, Opts, Args} = - case parse_arguments(init:get_plain_arguments(), NodeStr) of - {ok, Res} -> Res; - no_command -> print_error("could not recognise command", []), - usage() - end, +-record(cli, {file, dir, all, enabled, implicit}). - PrintInvalidCommandError = - fun () -> - print_error("invalid command '~s'", - [string:join([atom_to_list(Command) | Args], " ")]) - end, - Node = proplists:get_value(?NODE_OPT, Opts), - case catch action(Command, Node, Args, Opts, PluginsFile, PluginsDir) of - ok -> - rabbit_misc:quit(0); - {'EXIT', {function_clause, [{?MODULE, action, _} | _]}} -> - PrintInvalidCommandError(), - usage(); - {'EXIT', {function_clause, [{?MODULE, action, _, _} | _]}} -> - PrintInvalidCommandError(), - usage(); - {error, {missing_dependencies, Missing, Blame}} -> - print_error("dependent plugins ~p not found; used by ~p.", - [Missing, Blame]), - rabbit_misc:quit(2); - {error, Reason} -> - print_error("~p", [Reason]), - rabbit_misc:quit(2); - {error_string, Reason} -> - print_error("~s", [Reason]), - rabbit_misc:quit(2); - {badrpc, {'EXIT', Reason}} -> - print_error("~p", [Reason]), - rabbit_misc:quit(2); - {badrpc, Reason} -> - print_error("unable to connect to node ~w: ~w", [Node, Reason]), - print_badrpc_diagnostics([Node]), - rabbit_misc:quit(2); - Other -> - print_error("~p", [Other]), - rabbit_misc:quit(2) - end. +start() -> + {ok, [[PluginsFile|_]|_]} = init:get_argument(enabled_plugins_file), + {ok, [[PluginsDir |_]|_]} = init:get_argument(plugins_dist_dir), + rabbit_cli:main( + fun (Args, NodeStr) -> + parse_arguments(Args, NodeStr) + end, + fun (Command, Node, Args, Opts) -> + All = rabbit_plugins:list(PluginsDir), + Enabled = rabbit_plugins:read_enabled(PluginsFile), + Implicit = rabbit_plugins:dependencies(false, Enabled, All), + State = #cli{file = PluginsFile, + dir = PluginsDir, + all = All, + enabled = Enabled, + implicit = Implicit}, + action(Command, Node, Args, Opts, State) + end, rabbit_plugins_usage). stop() -> ok. @@ -115,116 +68,85 @@ stop() -> %%---------------------------------------------------------------------------- parse_arguments(CmdLine, NodeStr) -> - case rabbit_misc:parse_arguments( - ?COMMANDS, ?GLOBAL_DEFS(NodeStr), CmdLine) of - {ok, {Cmd, Opts0, Args}} -> - Opts = [case K of - ?NODE_OPT -> {?NODE_OPT, rabbit_nodes:make(V)}; - _ -> {K, V} - end || {K, V} <- Opts0], - {ok, {Cmd, Opts, Args}}; - E -> - E - end. + rabbit_cli:parse_arguments( + ?COMMANDS, ?GLOBAL_DEFS(NodeStr), ?NODE_OPT, CmdLine). -action(list, Node, [], Opts, PluginsFile, PluginsDir) -> - action(list, Node, [".*"], Opts, PluginsFile, PluginsDir); -action(list, Node, [Pat], Opts, PluginsFile, PluginsDir) -> - format_plugins(Node, Pat, Opts, PluginsFile, PluginsDir); +action(list, Node, [], Opts, State) -> + action(list, Node, [".*"], Opts, State); +action(list, Node, [Pat], Opts, State) -> + format_plugins(Node, Pat, Opts, State); -action(enable, Node, ToEnable0, Opts, PluginsFile, PluginsDir) -> +action(enable, Node, ToEnable0, Opts, State = #cli{all = All, + implicit = Implicit, + enabled = Enabled}) -> case ToEnable0 of [] -> throw({error_string, "Not enough arguments for 'enable'"}); _ -> ok end, - AllPlugins = rabbit_plugins:list(PluginsDir), - Enabled = rabbit_plugins:read_enabled(PluginsFile), - ImplicitlyEnabled = rabbit_plugins:dependencies(false, Enabled, AllPlugins), ToEnable = [list_to_atom(Name) || Name <- ToEnable0], - Missing = ToEnable -- plugin_names(AllPlugins), + Missing = ToEnable -- plugin_names(All), case Missing of [] -> ok; _ -> throw({error_string, fmt_missing(Missing)}) end, NewEnabled = lists:usort(Enabled ++ ToEnable), - NewImplicitlyEnabled = rabbit_plugins:dependencies(false, - NewEnabled, AllPlugins), - write_enabled_plugins(PluginsFile, NewEnabled), - case NewEnabled -- ImplicitlyEnabled of + NewImplicit = write_enabled_plugins(NewEnabled, State), + case NewEnabled -- Implicit of [] -> io:format("Plugin configuration unchanged.~n"); _ -> print_list("The following plugins have been enabled:", - NewImplicitlyEnabled -- ImplicitlyEnabled) + NewImplicit -- Implicit) end, - action_change( - Opts, Node, ImplicitlyEnabled, NewImplicitlyEnabled, PluginsFile); - -action(set, Node, ToSet0, Opts, PluginsFile, PluginsDir) -> - ToSet = [list_to_atom(Name) || Name <- ToSet0], - AllPlugins = rabbit_plugins:list(PluginsDir), - Enabled = rabbit_plugins:read_enabled(PluginsFile), - ImplicitlyEnabled = rabbit_plugins:dependencies(false, Enabled, AllPlugins), - Missing = ToSet -- plugin_names(AllPlugins), + action_change(Opts, Node, Implicit, NewImplicit, State); + +action(set, Node, NewEnabled0, Opts, State = #cli{all = All, + implicit = Implicit}) -> + NewEnabled = [list_to_atom(Name) || Name <- NewEnabled0], + Missing = NewEnabled -- plugin_names(All), case Missing of [] -> ok; _ -> throw({error_string, fmt_missing(Missing)}) end, - NewImplicitlyEnabled = rabbit_plugins:dependencies(false, - ToSet, AllPlugins), - write_enabled_plugins(PluginsFile, ToSet), - case NewImplicitlyEnabled of + NewImplicit = write_enabled_plugins(NewEnabled, State), + case NewImplicit of [] -> io:format("All plugins are now disabled.~n"); _ -> print_list("The following plugins are now enabled:", - NewImplicitlyEnabled) + NewImplicit) end, - action_change( - Opts, Node, ImplicitlyEnabled, NewImplicitlyEnabled, PluginsFile); + action_change(Opts, Node, Implicit, NewImplicit, State); -action(disable, Node, ToDisable0, Opts, PluginsFile, PluginsDir) -> +action(disable, Node, ToDisable0, Opts, State = #cli{all = All, + implicit = Implicit, + enabled = Enabled}) -> case ToDisable0 of [] -> throw({error_string, "Not enough arguments for 'disable'"}); _ -> ok end, - AllPlugins = rabbit_plugins:list(PluginsDir), - Enabled = rabbit_plugins:read_enabled(PluginsFile), - ImplicitlyEnabled = rabbit_plugins:dependencies(false, Enabled, AllPlugins), ToDisable = [list_to_atom(Name) || Name <- ToDisable0], - Missing = ToDisable -- plugin_names(AllPlugins), + Missing = ToDisable -- plugin_names(All), case Missing of [] -> ok; _ -> print_list("Warning: the following plugins could not be found:", Missing) end, - ToDisableDeps = rabbit_plugins:dependencies(true, ToDisable, AllPlugins), + ToDisableDeps = rabbit_plugins:dependencies(true, ToDisable, All), NewEnabled = Enabled -- ToDisableDeps, - NewImplicitlyEnabled = rabbit_plugins:dependencies(false, - NewEnabled, AllPlugins), + NewImplicit = write_enabled_plugins(NewEnabled, State), case length(Enabled) =:= length(NewEnabled) of true -> io:format("Plugin configuration unchanged.~n"); false -> print_list("The following plugins have been disabled:", - ImplicitlyEnabled -- NewImplicitlyEnabled), - write_enabled_plugins(PluginsFile, NewEnabled) + Implicit -- NewImplicit) end, - action_change( - Opts, Node, ImplicitlyEnabled, NewImplicitlyEnabled, PluginsFile); + action_change(Opts, Node, Implicit, NewImplicit, State); -action(sync, Node, [], _Opts, PluginsFile, _PluginsDir) -> - sync(Node, true, PluginsFile). +action(sync, Node, [], _Opts, State) -> + sync(Node, true, State). %%---------------------------------------------------------------------------- -fmt_stderr(Format, Args) -> rabbit_misc:format_stderr(Format ++ "~n", Args). - -print_error(Format, Args) -> fmt_stderr("Error: " ++ Format, Args). - -print_badrpc_diagnostics(Nodes) -> - fmt_stderr(rabbit_nodes:diagnostics(Nodes), []). - -usage() -> - io:format("~s", [rabbit_plugins_usage:usage()]), - rabbit_misc:quit(1). - %% Pretty print a list of plugins. -format_plugins(Node, Pattern, Opts, PluginsFile, PluginsDir) -> +format_plugins(Node, Pattern, Opts, #cli{all = All, + enabled = Enabled, + implicit = Implicit}) -> Verbose = proplists:get_bool(?VERBOSE_OPT, Opts), Minimal = proplists:get_bool(?MINIMAL_OPT, Opts), Format = case {Verbose, Minimal} of @@ -237,11 +159,7 @@ format_plugins(Node, Pattern, Opts, PluginsFile, PluginsDir) -> OnlyEnabled = proplists:get_bool(?ENABLED_OPT, Opts), OnlyEnabledAll = proplists:get_bool(?ENABLED_ALL_OPT, Opts), - AvailablePlugins = rabbit_plugins:list(PluginsDir), - EnabledExplicitly = rabbit_plugins:read_enabled(PluginsFile), - AllEnabled = rabbit_plugins:dependencies(false, EnabledExplicitly, - AvailablePlugins), - EnabledImplicitly = AllEnabled -- EnabledExplicitly, + EnabledImplicitly = Implicit -- Enabled, {StatusMsg, Running} = case rpc:call(Node, rabbit_plugins, active, [], ?RPC_TIMEOUT) of {badrpc, _} -> {"[failed to contact ~s - status not shown]", []}; @@ -249,10 +167,10 @@ format_plugins(Node, Pattern, Opts, PluginsFile, PluginsDir) -> end, {ok, RE} = re:compile(Pattern), Plugins = [ Plugin || - Plugin = #plugin{name = Name} <- AvailablePlugins, + Plugin = #plugin{name = Name} <- All, re:run(atom_to_list(Name), RE, [{capture, none}]) =:= match, - if OnlyEnabled -> lists:member(Name, EnabledExplicitly); - OnlyEnabledAll -> lists:member(Name, EnabledExplicitly) or + if OnlyEnabled -> lists:member(Name, Enabled); + OnlyEnabledAll -> lists:member(Name, Enabled) or lists:member(Name,EnabledImplicitly); true -> true end], @@ -266,15 +184,15 @@ format_plugins(Node, Pattern, Opts, PluginsFile, PluginsDir) -> " | Status: ~s~n" " |/~n", [rabbit_misc:format(StatusMsg, [Node])]) end, - [format_plugin(P, EnabledExplicitly, EnabledImplicitly, Running, + [format_plugin(P, Enabled, EnabledImplicitly, Running, Format, MaxWidth) || P <- Plugins1], ok. format_plugin(#plugin{name = Name, version = Version, description = Description, dependencies = Deps}, - EnabledExplicitly, EnabledImplicitly, Running, Format, + Enabled, EnabledImplicitly, Running, Format, MaxWidth) -> - EnabledGlyph = case {lists:member(Name, EnabledExplicitly), + EnabledGlyph = case {lists:member(Name, Enabled), lists:member(Name, EnabledImplicitly)} of {true, false} -> "E"; {false, true} -> "e"; @@ -323,28 +241,29 @@ plugin_names(Plugins) -> [Name || #plugin{name = Name} <- Plugins]. %% Write the enabled plugin names on disk. -write_enabled_plugins(PluginsFile, Plugins) -> - case rabbit_file:write_term_file(PluginsFile, [Plugins]) of - ok -> ok; +write_enabled_plugins(Plugins, #cli{file = File, + all = All}) -> + case rabbit_file:write_term_file(File, [Plugins]) of + ok -> rabbit_plugins:dependencies(false, Plugins, All); {error, Reason} -> throw({error, {cannot_write_enabled_plugins_file, - PluginsFile, Reason}}) + File, Reason}}) end. -action_change(Opts, Node, Old, New, PluginsFile) -> +action_change(Opts, Node, Old, New, State) -> action_change0(proplists:get_bool(?OFFLINE_OPT, Opts), proplists:get_bool(?ONLINE_OPT, Opts), - Node, Old, New, PluginsFile). + Node, Old, New, State). -action_change0(true, _Online, _Node, Same, Same, _PluginsFile) -> +action_change0(true, _Online, _Node, Same, Same, _State) -> %% Definitely nothing to do ok; -action_change0(true, _Online, _Node, _Old, _New, _PluginsFile) -> +action_change0(true, _Online, _Node, _Old, _New, _State) -> io:format("Offline change; changes will take effect at broker restart.~n"); -action_change0(false, Online, Node, _Old, _New, PluginsFile) -> - sync(Node, Online, PluginsFile). +action_change0(false, Online, Node, _Old, _New, State) -> + sync(Node, Online, State). -sync(Node, ForceOnline, PluginsFile) -> - rpc_call(Node, ForceOnline, rabbit_plugins, ensure, [PluginsFile]). +sync(Node, ForceOnline, #cli{file = File}) -> + rpc_call(Node, ForceOnline, rabbit_plugins, ensure, [File]). rpc_call(Node, Online, Mod, Fun, Args) -> io:format("~nApplying plugin configuration to ~s...", [Node]), diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index ee052c32..9cef22c1 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -858,7 +858,7 @@ test_arguments_parser() -> GetOptions = fun (Args) -> - rabbit_misc:parse_arguments(Commands1, GlobalOpts1, Args) + rabbit_cli:parse_arguments(Commands1, GlobalOpts1, "-n", Args) end, check_parse_arguments(no_command, GetOptions, []), |