summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTim Watson <tim@rabbitmq.com>2014-04-16 12:28:03 +0100
committerTim Watson <tim@rabbitmq.com>2014-04-16 12:28:03 +0100
commitd7bb98936706a03c5960f31500e0b2b4c0e303fe (patch)
treee6ccd9ff1fe795ea66f960c3e3bf4663b3731e9d
parentc1a0b20ba7771e092612880d0e3f3b42287006a7 (diff)
parent8fc94c42d3cf8b6da16649875efafe54f1272ec3 (diff)
downloadrabbitmq-server-d7bb98936706a03c5960f31500e0b2b4c0e303fe.tar.gz
Merge default into bug24926
-rw-r--r--docs/rabbitmq-plugins.1.xml14
-rwxr-xr-xscripts/rabbitmq-plugins2
-rwxr-xr-xscripts/rabbitmq-plugins.bat6
-rw-r--r--src/app_utils.erl7
-rw-r--r--src/rabbit.erl117
-rw-r--r--src/rabbit_event.erl24
-rw-r--r--src/rabbit_misc.erl25
-rw-r--r--src/rabbit_plugins.erl44
-rw-r--r--src/rabbit_plugins_main.erl194
-rw-r--r--src/rabbit_version.erl9
10 files changed, 318 insertions, 124 deletions
diff --git a/docs/rabbitmq-plugins.1.xml b/docs/rabbitmq-plugins.1.xml
index 8ecb4fc8..3b67d0e6 100644
--- a/docs/rabbitmq-plugins.1.xml
+++ b/docs/rabbitmq-plugins.1.xml
@@ -97,12 +97,14 @@
</variablelist>
<para>
Lists all plugins, their versions, dependencies and
- descriptions. Each plugin is prefixed with a status
- indicator - [ ] to indicate that the plugin is not
- enabled, [E] to indicate that it is explicitly enabled,
- [e] to indicate that it is implicitly enabled, and [!] to
- indicate that it is enabled but missing and thus not
- operational.
+ descriptions. Each plugin is prefixed with two status
+ indicator characters inside [ ]. The first indicator can
+ be " " to indicate that the plugin is not enabled, "E" to
+ indicate that it is explicitly enabled, "e" to indicate
+ that it is implicitly enabled, or "!" to indicate that it
+ is enabled but missing and thus not operational. The
+ second indicator can be " " to show that the plugin is not
+ running, or "*" to show that it is.
</para>
<para>
If the optional pattern is given, only plugins whose
diff --git a/scripts/rabbitmq-plugins b/scripts/rabbitmq-plugins
index 2ec45be0..2213e20b 100755
--- a/scripts/rabbitmq-plugins
+++ b/scripts/rabbitmq-plugins
@@ -21,6 +21,7 @@
##--- Set environment vars RABBITMQ_<var_name> to defaults if not set
+[ "x" = "x$RABBITMQ_NODENAME" ] && RABBITMQ_NODENAME=${NODENAME}
[ "x" = "x$RABBITMQ_ENABLED_PLUGINS_FILE" ] && RABBITMQ_ENABLED_PLUGINS_FILE=${ENABLED_PLUGINS_FILE}
[ "x" = "x$RABBITMQ_PLUGINS_DIR" ] && RABBITMQ_PLUGINS_DIR=${PLUGINS_DIR}
@@ -35,4 +36,5 @@ exec ${ERL_DIR}erl \
-s rabbit_plugins_main \
-enabled_plugins_file "$RABBITMQ_ENABLED_PLUGINS_FILE" \
-plugins_dist_dir "$RABBITMQ_PLUGINS_DIR" \
+ -nodename $RABBITMQ_NODENAME \
-extra "$@"
diff --git a/scripts/rabbitmq-plugins.bat b/scripts/rabbitmq-plugins.bat
index a535ebad..61e39e38 100755
--- a/scripts/rabbitmq-plugins.bat
+++ b/scripts/rabbitmq-plugins.bat
@@ -31,6 +31,10 @@ if "!RABBITMQ_BASE!"=="" (
set RABBITMQ_BASE=!APPDATA!\!RABBITMQ_SERVICENAME!
)
+if "!RABBITMQ_NODENAME!"=="" (
+ set RABBITMQ_NODENAME=rabbit@!COMPUTERNAME!
+)
+
if not exist "!ERLANG_HOME!\bin\erl.exe" (
echo.
echo ******************************
@@ -51,7 +55,7 @@ if "!RABBITMQ_PLUGINS_DIR!"=="" (
set RABBITMQ_PLUGINS_DIR=!TDP0!..\plugins
)
-"!ERLANG_HOME!\bin\erl.exe" -pa "!TDP0!..\ebin" -noinput -hidden -sname rabbitmq-plugins!RANDOM!!TIME:~9! -s rabbit_plugins_main -enabled_plugins_file "!RABBITMQ_ENABLED_PLUGINS_FILE!" -plugins_dist_dir "!RABBITMQ_PLUGINS_DIR:\=/!" -extra !STAR!
+"!ERLANG_HOME!\bin\erl.exe" -pa "!TDP0!..\ebin" -noinput -hidden -sname rabbitmq-plugins!RANDOM!!TIME:~9! -s rabbit_plugins_main -enabled_plugins_file "!RABBITMQ_ENABLED_PLUGINS_FILE!" -plugins_dist_dir "!RABBITMQ_PLUGINS_DIR:\=/!" -nodename !RABBITMQ_NODENAME! -extra !STAR!
endlocal
endlocal
diff --git a/src/app_utils.erl b/src/app_utils.erl
index 0479ce66..e321888d 100644
--- a/src/app_utils.erl
+++ b/src/app_utils.erl
@@ -17,7 +17,7 @@
-export([load_applications/1, start_applications/1, start_applications/2,
stop_applications/1, stop_applications/2, app_dependency_order/2,
- wait_for_applications/1]).
+ wait_for_applications/1, app_dependencies/1]).
-ifdef(use_specs).
@@ -30,6 +30,7 @@
-spec stop_applications([atom()], error_handler()) -> 'ok'.
-spec wait_for_applications([atom()]) -> 'ok'.
-spec app_dependency_order([atom()], boolean()) -> [digraph:vertex()].
+-spec app_dependencies(atom()) -> [atom()].
-endif.
@@ -74,8 +75,8 @@ wait_for_applications(Apps) ->
app_dependency_order(RootApps, StripUnreachable) ->
{ok, G} = rabbit_misc:build_acyclic_graph(
- fun (App, _Deps) -> [{App, App}] end,
- fun (App, Deps) -> [{Dep, App} || Dep <- Deps] end,
+ fun ({App, _Deps}) -> [{App, App}] end,
+ fun ({App, Deps}) -> [{Dep, App} || Dep <- Deps] end,
[{App, app_dependencies(App)} ||
{App, _Desc, _Vsn} <- application:loaded_applications()]),
try
diff --git a/src/rabbit.erl b/src/rabbit.erl
index c2d7e29d..8a682616 100644
--- a/src/rabbit.erl
+++ b/src/rabbit.erl
@@ -22,9 +22,8 @@
stop_and_halt/0, await_startup/0, status/0, is_running/0,
is_running/1, environment/0, rotate_logs/1, force_event_refresh/1,
start_fhc/0]).
-
-export([start/2, stop/1]).
-
+-export([start_apps/1, stop_apps/1]).
-export([log_location/1]). %% for testing
%%---------------------------------------------------------------------------
@@ -211,6 +210,7 @@
%% this really should be an abstract type
-type(log_location() :: 'tty' | 'undefined' | file:filename()).
-type(param() :: atom()).
+-type(app_name() :: atom()).
-spec(start/0 :: () -> 'ok').
-spec(boot/0 :: () -> 'ok').
@@ -242,6 +242,8 @@
-spec(maybe_insert_default_data/0 :: () -> 'ok').
-spec(boot_delegate/0 :: () -> 'ok').
-spec(recover/0 :: () -> 'ok').
+-spec(start_apps/1 :: ([app_name()]) -> 'ok').
+-spec(stop_apps/1 :: ([app_name()]) -> 'ok').
-endif.
@@ -312,9 +314,7 @@ start() ->
ok = ensure_working_log_handlers(),
rabbit_node_monitor:prepare_cluster_status_files(),
rabbit_mnesia:check_cluster_consistency(),
- ok = app_utils:start_applications(
- app_startup_order(), fun handle_app_error/2),
- ok = log_broker_started(rabbit_plugins:active())
+ broker_start()
end).
boot() ->
@@ -329,21 +329,31 @@ boot() ->
%% the upgrade, since if we are a secondary node the
%% primary node will have forgotten us
rabbit_mnesia:check_cluster_consistency(),
- Plugins = rabbit_plugins:setup(),
- ToBeLoaded = Plugins ++ ?APPS,
- ok = app_utils:load_applications(ToBeLoaded),
- StartupApps = app_utils:app_dependency_order(ToBeLoaded,
- false),
- ok = app_utils:start_applications(
- StartupApps, fun handle_app_error/2),
- ok = log_broker_started(Plugins)
+ broker_start()
end).
-handle_app_error(App, {bad_return, {_MFA, {'EXIT', {Reason, _}}}}) ->
- throw({could_not_start, App, Reason});
+broker_start() ->
+ Plugins = rabbit_plugins:setup(),
+ ToBeLoaded = Plugins ++ ?APPS,
+ start_apps(ToBeLoaded),
+ ok = log_broker_started(rabbit_plugins:active()).
+
+handle_app_error(Term) ->
+ fun(App, {bad_return, {_MFA, {'EXIT', {ExitReason, _}}}}) ->
+ throw({Term, App, ExitReason});
+ (App, Reason) ->
+ throw({Term, App, Reason})
+ end.
-handle_app_error(App, Reason) ->
- throw({could_not_start, App, Reason}).
+start_apps(Apps) ->
+ app_utils:load_applications(Apps),
+ StartupApps = app_utils:app_dependency_order(Apps, false),
+ case whereis(rabbit_boot) of
+ undefined -> run_boot_steps(Apps);
+ _ -> ok
+ end,
+ ok = app_utils:start_applications(StartupApps,
+ handle_app_error(could_not_start)).
start_it(StartFun) ->
Marker = spawn_link(fun() -> receive stop -> ok end end),
@@ -369,12 +379,13 @@ start_it(StartFun) ->
end.
stop() ->
+ Apps = app_shutdown_order(),
case whereis(rabbit_boot) of
undefined -> ok;
- _ -> await_startup()
+ _ -> app_utils:wait_for_applications(Apps)
end,
rabbit_log:info("Stopping RabbitMQ~n"),
- ok = app_utils:stop_applications(app_shutdown_order()).
+ ok = app_utils:stop_applications(Apps).
stop_and_halt() ->
try
@@ -385,6 +396,29 @@ stop_and_halt() ->
end,
ok.
+stop_apps(Apps) ->
+ try
+ ok = app_utils:stop_applications(
+ Apps, handle_app_error(error_during_shutdown))
+ after
+ run_cleanup_steps(Apps),
+ [begin
+ {ok, Mods} = application:get_key(App, modules),
+ [begin
+ code:soft_purge(Mod),
+ code:delete(Mod),
+ false = code:is_loaded(Mod)
+ end || Mod <- Mods],
+ application:unload(App)
+ end || App <- Apps]
+ end.
+
+run_cleanup_steps(Apps) ->
+ [run_step(Name, Attributes, cleanup) ||
+ {App, Name, Attributes} <- find_steps(Apps),
+ lists:member(App, Apps)],
+ ok.
+
await_startup() ->
app_utils:wait_for_applications(app_startup_order()).
@@ -468,7 +502,7 @@ start(normal, []) ->
true = register(rabbit, self()),
print_banner(),
log_banner(),
- [ok = run_boot_step(Step) || Step <- boot_steps()],
+ run_boot_steps(),
{ok, SupPid};
Error ->
Error
@@ -496,29 +530,44 @@ app_shutdown_order() ->
%%---------------------------------------------------------------------------
%% boot step logic
-run_boot_step({_StepName, Attributes}) ->
- case [MFA || {mfa, MFA} <- Attributes] of
+run_boot_steps() ->
+ run_boot_steps([App || {App, _, _} <- application:loaded_applications()]).
+
+run_boot_steps(Apps) ->
+ Steps = find_steps(Apps),
+ [ok = run_step(StepName, Attributes, mfa) ||
+ {_, StepName, Attributes} <- Steps],
+ ok.
+
+find_steps(BaseApps) ->
+ Apps = BaseApps -- [App || {App, _, _} <- rabbit_misc:which_applications()],
+ FullBoot = sort_boot_steps(
+ rabbit_misc:all_module_attributes(rabbit_boot_step)),
+ [Step || {App, _, _} = Step <- FullBoot, lists:member(App, Apps)].
+
+run_step(StepName, Attributes, AttributeName) ->
+ case [MFA || {Key, MFA} <- Attributes,
+ Key =:= AttributeName] of
[] ->
ok;
MFAs ->
[try
apply(M,F,A)
of
- ok -> ok;
- {error, Reason} -> boot_error(Reason, not_available)
+ ok -> ok;
+ {error, Reason} -> boot_error({boot_step, StepName, Reason},
+ not_available)
catch
- _:Reason -> boot_error(Reason, erlang:get_stacktrace())
+ _:Reason -> boot_error({boot_step, StepName, Reason},
+ erlang:get_stacktrace())
end || {M,F,A} <- MFAs],
ok
end.
-boot_steps() ->
- sort_boot_steps(rabbit_misc:all_module_attributes(rabbit_boot_step)).
-
-vertices(_Module, Steps) ->
- [{StepName, {StepName, Atts}} || {StepName, Atts} <- Steps].
+vertices({AppName, _Module, Steps}) ->
+ [{StepName, {AppName, StepName, Atts}} || {StepName, Atts} <- Steps].
-edges(_Module, Steps) ->
+edges({_AppName, _Module, Steps}) ->
[case Key of
requires -> {StepName, OtherStep};
enables -> {OtherStep, StepName}
@@ -527,7 +576,7 @@ edges(_Module, Steps) ->
Key =:= requires orelse Key =:= enables].
sort_boot_steps(UnsortedSteps) ->
- case rabbit_misc:build_acyclic_graph(fun vertices/2, fun edges/2,
+ case rabbit_misc:build_acyclic_graph(fun vertices/1, fun edges/1,
UnsortedSteps) of
{ok, G} ->
%% Use topological sort to find a consistent ordering (if
@@ -541,8 +590,8 @@ sort_boot_steps(UnsortedSteps) ->
digraph:delete(G),
%% Check that all mentioned {M,F,A} triples are exported.
case [{StepName, {M,F,A}} ||
- {StepName, Attributes} <- SortedSteps,
- {mfa, {M,F,A}} <- Attributes,
+ {_App, StepName, Attributes} <- SortedSteps,
+ {mfa, {M,F,A}} <- Attributes,
not erlang:function_exported(M, F, length(A))] of
[] -> SortedSteps;
MissingFunctions -> basic_boot_error(
diff --git a/src/rabbit_event.erl b/src/rabbit_event.erl
index b867223b..d3cd1a63 100644
--- a/src/rabbit_event.erl
+++ b/src/rabbit_event.erl
@@ -23,6 +23,7 @@
ensure_stats_timer/3, stop_stats_timer/2, reset_stats_timer/2]).
-export([stats_level/2, if_enabled/3]).
-export([notify/2, notify/3, notify_if/3]).
+-export([sync_notify/2, sync_notify/3]).
%%----------------------------------------------------------------------------
@@ -61,6 +62,9 @@
-spec(notify/2 :: (event_type(), event_props()) -> 'ok').
-spec(notify/3 :: (event_type(), event_props(), reference() | 'none') -> 'ok').
-spec(notify_if/3 :: (boolean(), event_type(), event_props()) -> 'ok').
+-spec(sync_notify/2 :: (event_type(), event_props()) -> 'ok').
+-spec(sync_notify/3 :: (event_type(), event_props(),
+ reference() | 'none') -> 'ok').
-endif.
@@ -145,7 +149,19 @@ notify_if(false, _Type, _Props) -> ok.
notify(Type, Props) -> notify(Type, Props, none).
notify(Type, Props, Ref) ->
- gen_event:notify(?MODULE, #event{type = Type,
- props = Props,
- reference = Ref,
- timestamp = os:timestamp()}).
+ do_notify(notify, #event{type = Type,
+ props = Props,
+ reference = Ref,
+ timestamp = os:timestamp()}).
+
+sync_notify(Type, Props) -> sync_notify(Type, Props, none).
+
+sync_notify(Type, Props, Ref) ->
+ do_notify(sync_notify, #event{type = Type,
+ props = Props,
+ reference = Ref,
+ timestamp = os:timestamp()}).
+
+do_notify(F, Event) ->
+ apply(gen_event, F, [?MODULE, Event]).
+
diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl
index 58e93a3f..2484a31a 100644
--- a/src/rabbit_misc.erl
+++ b/src/rabbit_misc.erl
@@ -209,7 +209,8 @@
[string()])
-> {'ok', {atom(), [{string(), string()}], [string()]}} |
'no_command').
--spec(all_module_attributes/1 :: (atom()) -> [{atom(), [term()]}]).
+-spec(all_module_attributes/1 ::
+ (atom()) -> [{atom(), atom(), [term()]}]).
-spec(build_acyclic_graph/3 ::
(graph_vertex_fun(), graph_edge_fun(), [{atom(), [term()]}])
-> rabbit_types:ok_or_error2(digraph(),
@@ -849,20 +850,20 @@ module_attributes(Module) ->
end.
all_module_attributes(Name) ->
- Modules =
+ Targets =
lists:usort(
lists:append(
- [Modules || {App, _, _} <- application:loaded_applications(),
- {ok, Modules} <- [application:get_key(App, modules)]])),
+ [[{App, Module} || Module <- Modules] ||
+ {App, _, _} <- application:loaded_applications(),
+ {ok, Modules} <- [application:get_key(App, modules)]])),
lists:foldl(
- fun (Module, Acc) ->
+ fun ({App, Module}, Acc) ->
case lists:append([Atts || {N, Atts} <- module_attributes(Module),
N =:= Name]) of
[] -> Acc;
- Atts -> [{Module, Atts} | Acc]
+ Atts -> [{App, Module, Atts} | Acc]
end
- end, [], Modules).
-
+ end, [], Targets).
build_acyclic_graph(VertexFun, EdgeFun, Graph) ->
G = digraph:new([acyclic]),
@@ -870,13 +871,13 @@ build_acyclic_graph(VertexFun, EdgeFun, Graph) ->
[case digraph:vertex(G, Vertex) of
false -> digraph:add_vertex(G, Vertex, Label);
_ -> ok = throw({graph_error, {vertex, duplicate, Vertex}})
- end || {Module, Atts} <- Graph,
- {Vertex, Label} <- VertexFun(Module, Atts)],
+ end || GraphElem <- Graph,
+ {Vertex, Label} <- VertexFun(GraphElem)],
[case digraph:add_edge(G, From, To) of
{error, E} -> throw({graph_error, {edge, E, From, To}});
_ -> ok
- end || {Module, Atts} <- Graph,
- {From, To} <- EdgeFun(Module, Atts)],
+ end || GraphElem <- Graph,
+ {From, To} <- EdgeFun(GraphElem)],
{ok, G}
catch {graph_error, Reason} ->
true = digraph:delete(G),
diff --git a/src/rabbit_plugins.erl b/src/rabbit_plugins.erl
index c0fb05e2..2f10e0a0 100644
--- a/src/rabbit_plugins.erl
+++ b/src/rabbit_plugins.erl
@@ -18,6 +18,7 @@
-include("rabbit.hrl").
-export([setup/0, active/0, read_enabled/1, list/1, dependencies/3]).
+-export([enable/1, disable/1]).
%%----------------------------------------------------------------------------
@@ -31,17 +32,38 @@
-spec(read_enabled/1 :: (file:filename()) -> [plugin_name()]).
-spec(dependencies/3 :: (boolean(), [plugin_name()], [#plugin{}]) ->
[plugin_name()]).
-
+-spec(enable/1 :: ([plugin_name()]) -> 'ok').
+-spec(disable/1 :: ([plugin_name()]) -> 'ok').
-endif.
%%----------------------------------------------------------------------------
+enable(Plugins) ->
+ prepare_plugins(Plugins),
+ rabbit:start_apps(Plugins),
+ ok = rabbit_event:notify(plugins_changed, [{enabled, Plugins}]).
+
+disable(Plugins) ->
+ RunningApps = rabbit_misc:which_applications(),
+ ToDisable = [P || P <- Plugins,
+ proplists:is_defined(P, RunningApps)],
+ ok = rabbit_event:sync_notify(plugins_changed, [{disabled, ToDisable}]),
+ rabbit:stop_apps(ToDisable).
+
%% @doc Prepares the file system and installs all enabled plugins.
setup() ->
- {ok, PluginDir} = application:get_env(rabbit, plugins_dir),
{ok, ExpandDir} = application:get_env(rabbit, plugins_expand_dir),
+
+ %% Eliminate the contents of the destination directory
+ case delete_recursively(ExpandDir) of
+ ok -> ok;
+ {error, E1} -> throw({error, {cannot_delete_plugins_expand_dir,
+ [ExpandDir, E1]}})
+ end,
+
{ok, EnabledFile} = application:get_env(rabbit, enabled_plugins_file),
- prepare_plugins(EnabledFile, PluginDir, ExpandDir).
+ Enabled = read_enabled(EnabledFile),
+ prepare_plugins(Enabled).
%% @doc Lists the plugins which are currently running.
active() ->
@@ -86,8 +108,8 @@ read_enabled(PluginsFile) ->
%% the resulting list, otherwise they're skipped.
dependencies(Reverse, Sources, AllPlugins) ->
{ok, G} = rabbit_misc:build_acyclic_graph(
- fun (App, _Deps) -> [{App, App}] end,
- fun (App, Deps) -> [{App, Dep} || Dep <- Deps] end,
+ fun ({App, _Deps}) -> [{App, App}] end,
+ fun ({App, Deps}) -> [{App, Dep} || Dep <- Deps] end,
lists:ukeysort(
1, [{Name, Deps} ||
#plugin{name = Name,
@@ -104,9 +126,11 @@ dependencies(Reverse, Sources, AllPlugins) ->
%%----------------------------------------------------------------------------
-prepare_plugins(EnabledFile, PluginsDistDir, ExpandDir) ->
+prepare_plugins(Enabled) ->
+ {ok, PluginsDistDir} = application:get_env(rabbit, plugins_dir),
+ {ok, ExpandDir} = application:get_env(rabbit, plugins_expand_dir),
+
AllPlugins = list(PluginsDistDir),
- Enabled = read_enabled(EnabledFile),
ToUnpack = dependencies(false, Enabled, AllPlugins),
ToUnpackPlugins = lookup_plugins(ToUnpack, AllPlugins),
@@ -117,12 +141,6 @@ prepare_plugins(EnabledFile, PluginsDistDir, ExpandDir) ->
[Missing])
end,
- %% Eliminate the contents of the destination directory
- case delete_recursively(ExpandDir) of
- ok -> ok;
- {error, E1} -> throw({error, {cannot_delete_plugins_expand_dir,
- [ExpandDir, E1]}})
- end,
case filelib:ensure_dir(ExpandDir ++ "/") of
ok -> ok;
{error, E2} -> throw({error, {cannot_create_plugins_expand_dir,
diff --git a/src/rabbit_plugins_main.erl b/src/rabbit_plugins_main.erl
index 89e16f14..3b9d30f2 100644
--- a/src/rabbit_plugins_main.erl
+++ b/src/rabbit_plugins_main.erl
@@ -18,23 +18,30 @@
-include("rabbit.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(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(GLOBAL_DEFS, []).
+-define(RPC_TIMEOUT, infinity).
+
+-define(GLOBAL_DEFS(Node), [?NODE_DEF(Node)]).
-define(COMMANDS,
[{list, [?VERBOSE_DEF, ?MINIMAL_DEF, ?ENABLED_DEF, ?ENABLED_ALL_DEF]},
- enable,
- disable]).
+ {enable, [?OFFLINE_DEF]},
+ {disable, [?OFFLINE_DEF]}]).
%%----------------------------------------------------------------------------
@@ -51,11 +58,10 @@
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 rabbit_misc:parse_arguments(?COMMANDS, ?GLOBAL_DEFS,
- init:get_plain_arguments())
- of
+ case parse_arguments(init:get_plain_arguments(), NodeStr) of
{ok, Res} -> Res;
no_command -> print_error("could not recognise command", []),
usage()
@@ -67,7 +73,8 @@ start() ->
[string:join([atom_to_list(Command) | Args], " ")])
end,
- case catch action(Command, Args, Opts, PluginsFile, PluginsDir) of
+ 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, _} | _]}} ->
@@ -92,12 +99,25 @@ stop() ->
%%----------------------------------------------------------------------------
-action(list, [], Opts, PluginsFile, PluginsDir) ->
- action(list, [".*"], Opts, PluginsFile, PluginsDir);
-action(list, [Pat], Opts, PluginsFile, PluginsDir) ->
- format_plugins(Pat, Opts, PluginsFile, PluginsDir);
+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.
+
+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(enable, ToEnable0, _Opts, PluginsFile, PluginsDir) ->
+action(enable, Node, ToEnable0, Opts, PluginsFile, PluginsDir) ->
case ToEnable0 of
[] -> throw({error_string, "Not enough arguments for 'enable'"});
_ -> ok
@@ -108,7 +128,22 @@ action(enable, ToEnable0, _Opts, PluginsFile, PluginsDir) ->
Enabled, AllPlugins),
ToEnable = [list_to_atom(Name) || Name <- ToEnable0],
Missing = ToEnable -- plugin_names(AllPlugins),
- NewEnabled = lists:usort(Enabled ++ ToEnable),
+ ExplicitlyEnabled = lists:usort(Enabled ++ ToEnable),
+ OfflineOnly = proplists:get_bool(?OFFLINE_OPT, Opts),
+ NewEnabled =
+ case OfflineOnly of
+ true -> ToEnable -- Enabled;
+ false ->
+ case rpc:call(Node, rabbit_plugins, active, [], ?RPC_TIMEOUT) of
+ {badrpc, _} -> rpc_failure(Node);
+ [] -> ExplicitlyEnabled;
+ ActiveList ->
+ EnabledSet = sets:from_list(ExplicitlyEnabled),
+ ActiveSet = sets:from_list(ActiveList),
+ Intersect = sets:intersection(EnabledSet, ActiveSet),
+ sets:to_list(sets:subtract(EnabledSet, Intersect))
+ end
+ end,
NewImplicitlyEnabled = rabbit_plugins:dependencies(false,
NewEnabled, AllPlugins),
MissingDeps = (NewImplicitlyEnabled -- plugin_names(AllPlugins)) -- Missing,
@@ -120,15 +155,15 @@ action(enable, ToEnable0, _Opts, PluginsFile, PluginsDir) ->
fmt_missing("plugins", Missing) ++
fmt_missing("dependencies", MissingDeps)})
end,
- write_enabled_plugins(PluginsFile, NewEnabled),
- case NewEnabled -- ImplicitlyEnabled of
+ write_enabled_plugins(PluginsFile, ExplicitlyEnabled),
+ case NewEnabled -- (ImplicitlyEnabled -- ExplicitlyEnabled) of
[] -> io:format("Plugin configuration unchanged.~n");
_ -> print_list("The following plugins have been enabled:",
- NewImplicitlyEnabled -- ImplicitlyEnabled),
- report_change()
+ NewEnabled),
+ action_change(OfflineOnly, Node, enable, NewEnabled)
end;
-action(disable, ToDisable0, _Opts, PluginsFile, PluginsDir) ->
+action(disable, Node, ToDisable0, Opts, PluginsFile, PluginsDir) ->
case ToDisable0 of
[] -> throw({error_string, "Not enough arguments for 'disable'"});
_ -> ok
@@ -143,22 +178,42 @@ action(disable, ToDisable0, _Opts, PluginsFile, PluginsDir) ->
Missing)
end,
ToDisableDeps = rabbit_plugins:dependencies(true, ToDisable, AllPlugins),
+ OfflineOnly = proplists:get_bool(?OFFLINE_OPT, Opts),
+ Active =
+ case OfflineOnly of
+ true -> Enabled;
+ false -> case rpc:call(Node, rabbit_plugins, active,
+ [], ?RPC_TIMEOUT) of
+ {badrpc, _} -> rpc_failure(Node);
+ [] -> Enabled;
+ ActiveList -> ActiveList
+ end
+ end,
NewEnabled = Enabled -- ToDisableDeps,
- case length(Enabled) =:= length(NewEnabled) of
+ case length(Active) =:= length(NewEnabled) of
true -> io:format("Plugin configuration unchanged.~n");
false -> ImplicitlyEnabled =
- rabbit_plugins:dependencies(false, Enabled, AllPlugins),
+ rabbit_plugins:dependencies(false, Active, AllPlugins),
NewImplicitlyEnabled =
rabbit_plugins:dependencies(false,
NewEnabled, AllPlugins),
+ Disabled = ImplicitlyEnabled -- NewImplicitlyEnabled,
print_list("The following plugins have been disabled:",
- ImplicitlyEnabled -- NewImplicitlyEnabled),
+ Disabled),
write_enabled_plugins(PluginsFile, NewEnabled),
- report_change()
+ action_change(OfflineOnly, Node, disable, Disabled)
end.
%%----------------------------------------------------------------------------
+rpc_failure(Node) ->
+ RpcMsg = rabbit_nodes:diagnostics([Node]),
+ Msg = io_lib:format("Unable to contact ~p~n"
+ "To apply these changes anyway, "
+ "try again with --offline~n"
+ "~s", [Node, RpcMsg]),
+ throw({error_string, Msg}).
+
print_error(Format, Args) ->
rabbit_misc:format_stderr("Error: " ++ Format ++ "~n", Args).
@@ -167,7 +222,7 @@ usage() ->
rabbit_misc:quit(1).
%% Pretty print a list of plugins.
-format_plugins(Pattern, Opts, PluginsFile, PluginsDir) ->
+format_plugins(Node, Pattern, Opts, PluginsFile, PluginsDir) ->
Verbose = proplists:get_bool(?VERBOSE_OPT, Opts),
Minimal = proplists:get_bool(?MINIMAL_OPT, Opts),
Format = case {Verbose, Minimal} of
@@ -182,9 +237,14 @@ format_plugins(Pattern, Opts, PluginsFile, PluginsDir) ->
AvailablePlugins = rabbit_plugins:list(PluginsDir),
EnabledExplicitly = rabbit_plugins:read_enabled(PluginsFile),
- EnabledImplicitly =
- rabbit_plugins:dependencies(false, EnabledExplicitly,
- AvailablePlugins) -- EnabledExplicitly,
+ AllEnabled = rabbit_plugins:dependencies(false, EnabledExplicitly,
+ AvailablePlugins),
+ EnabledImplicitly = AllEnabled -- EnabledExplicitly,
+ Running = case rpc:call(Node, rabbit_plugins, active,
+ [], ?RPC_TIMEOUT) of
+ {badrpc, _} -> AllEnabled;
+ Active -> Active
+ end,
Missing = [#plugin{name = Name, dependencies = []} ||
Name <- ((EnabledExplicitly ++ EnabledImplicitly) --
plugin_names(AvailablePlugins))],
@@ -192,31 +252,42 @@ format_plugins(Pattern, Opts, PluginsFile, PluginsDir) ->
Plugins = [ Plugin ||
Plugin = #plugin{name = Name} <- AvailablePlugins ++ Missing,
re:run(atom_to_list(Name), RE, [{capture, none}]) =:= match,
- if OnlyEnabled -> lists:member(Name, EnabledExplicitly);
- OnlyEnabledAll -> (lists:member(Name,
- EnabledExplicitly) or
- lists:member(Name, EnabledImplicitly));
+ if OnlyEnabled -> lists:member(Name, EnabledExplicitly);
+ OnlyEnabledAll -> lists:member(Name, EnabledExplicitly) or
+ lists:member(Name,EnabledImplicitly);
true -> true
end],
Plugins1 = usort_plugins(Plugins),
MaxWidth = lists:max([length(atom_to_list(Name)) ||
#plugin{name = Name} <- Plugins1] ++ [0]),
- [format_plugin(P, EnabledExplicitly, EnabledImplicitly,
+ case Format of
+ minimal -> ok;
+ _ -> io:format(" Configured: E = explicitly enabled; "
+ "e = implicitly enabled; ! = missing~n"
+ " | Status: * = running on ~s~n"
+ " |/~n", [Node])
+ end,
+ [format_plugin(P, EnabledExplicitly, EnabledImplicitly, Running,
plugin_names(Missing), Format, MaxWidth) || P <- Plugins1],
ok.
format_plugin(#plugin{name = Name, version = Version,
description = Description, dependencies = Deps},
- EnabledExplicitly, EnabledImplicitly, Missing,
- Format, MaxWidth) ->
- Glyph = case {lists:member(Name, EnabledExplicitly),
- lists:member(Name, EnabledImplicitly),
- lists:member(Name, Missing)} of
- {true, false, false} -> "[E]";
- {false, true, false} -> "[e]";
- {_, _, true} -> "[!]";
- _ -> "[ ]"
- end,
+ EnabledExplicitly, EnabledImplicitly, Running,
+ Missing, Format, MaxWidth) ->
+ EnabledGlyph = case {lists:member(Name, EnabledExplicitly),
+ lists:member(Name, EnabledImplicitly),
+ lists:member(Name, Missing)} of
+ {true, false, false} -> "E";
+ {false, true, false} -> "e";
+ {_, _, true} -> "!";
+ _ -> " "
+ end,
+ RunningGlyph = case lists:member(Name, Running) of
+ true -> "*";
+ false -> " "
+ end,
+ Glyph = rabbit_misc:format("[~s~s]", [EnabledGlyph, RunningGlyph]),
Opt = fun (_F, A, A) -> ok;
( F, A, _) -> io:format(F, [A])
end,
@@ -227,9 +298,9 @@ format_plugin(#plugin{name = Name, version = Version,
Opt("~s", Version, undefined),
io:format("~n");
verbose -> io:format("~s ~w~n", [Glyph, Name]),
- Opt(" Version: \t~s~n", Version, undefined),
- Opt(" Dependencies:\t~p~n", Deps, []),
- Opt(" Description: \t~s~n", Description, undefined),
+ Opt(" Version: \t~s~n", Version, undefined),
+ Opt(" Dependencies:\t~p~n", Deps, []),
+ Opt(" Description: \t~s~n", Description, undefined),
io:format("~n")
end.
@@ -262,6 +333,35 @@ write_enabled_plugins(PluginsFile, Plugins) ->
PluginsFile, Reason}})
end.
-report_change() ->
- io:format("Plugin configuration has changed. "
- "Restart RabbitMQ for changes to take effect.~n").
+action_change(true, _Node, Action, _Targets) ->
+ io:format("Offline Mode: No plugins were ~p.~n"
+ "Please (re)start the broker to apply your changes.~n",
+ [case Action of
+ enable -> started;
+ disable -> stopped
+ end]);
+action_change(false, Node, Action, Targets) ->
+ rpc_call(Node, rabbit_plugins, Action, [Targets]).
+
+rpc_call(Node, Mod, Action, Args) ->
+ io:format("Changing plugin configuration on ~p.", [Node]),
+ AsyncKey = rpc:async_call(Node, Mod, Action, Args),
+ rpc_progress(AsyncKey, Node, Action).
+
+rpc_progress(Key, Node, Action) ->
+ case rpc:nb_yield(Key, 1000) of
+ timeout -> io:format("."),
+ rpc_progress(Key, Node, Action);
+ {value, {badrpc, nodedown}} ->
+ io:format(". error.~nUnable to contact ~p.~n ", [Node]),
+ io:format("Please start the broker to apply "
+ "your changes.~n");
+ {value, ok} ->
+ io:format(". done.~n", []);
+ {value, Error} ->
+ io:format(". error.~nUnable to ~p plugin(s).~n"
+ "Please restart the broker to apply your changes.~n"
+ "Error: ~p~n",
+ [Action, Error])
+ end.
+
diff --git a/src/rabbit_version.erl b/src/rabbit_version.erl
index d943b599..ef480ccb 100644
--- a/src/rabbit_version.erl
+++ b/src/rabbit_version.erl
@@ -113,10 +113,11 @@ upgrades_required(Scope) ->
%% -------------------------------------------------------------------
with_upgrade_graph(Fun, Scope) ->
+ Attrs = rabbit_misc:all_module_attributes(rabbit_upgrade),
case rabbit_misc:build_acyclic_graph(
- fun (Module, Steps) -> vertices(Module, Steps, Scope) end,
- fun (Module, Steps) -> edges(Module, Steps, Scope) end,
- rabbit_misc:all_module_attributes(rabbit_upgrade)) of
+ fun ({Module, Steps}) -> vertices(Module, Steps, Scope) end,
+ fun ({Module, Steps}) -> edges(Module, Steps, Scope) end,
+ [{Mod, Steps} || {_, Mod, Steps} <- Attrs]) of
{ok, G} -> try
Fun(G)
after
@@ -161,7 +162,7 @@ heads(G) ->
categorise_by_scope(Version) when is_list(Version) ->
Categorised =
- [{Scope, Name} || {_Module, Attributes} <-
+ [{Scope, Name} || {_App, _Module, Attributes} <-
rabbit_misc:all_module_attributes(rabbit_upgrade),
{Name, Scope, _Requires} <- Attributes,
lists:member(Name, Version)],