summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon MacMullen <simon@rabbitmq.com>2014-09-16 17:52:01 +0100
committerSimon MacMullen <simon@rabbitmq.com>2014-09-16 17:52:01 +0100
commit06a30fdb757b93f976989d62d047e5b6a6553e42 (patch)
treef4bbf4799869c2f2d6eb36a3302965febdc7ce97
parentd790b58cb380d2b1b6825e6bf1f3dd004da3a07b (diff)
parent5763fadffc8328903641ed77ff2c7a28cb2067e9 (diff)
downloadrabbitmq-server-06a30fdb757b93f976989d62d047e5b6a6553e42.tar.gz
Merge bug25955
-rw-r--r--include/rabbit_cli.hrl48
-rw-r--r--src/rabbit_cli.erl184
-rw-r--r--src/rabbit_control_main.erl130
-rw-r--r--src/rabbit_misc.erl66
-rw-r--r--src/rabbit_plugins_main.erl233
-rw-r--r--src/rabbit_tests.erl2
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, []),