diff --git a/deps/rabbit/src/rabbit_nodes.erl b/deps/rabbit/src/rabbit_nodes.erl index 956239c6a1..ff7c1759da 100644 --- a/deps/rabbit/src/rabbit_nodes.erl +++ b/deps/rabbit/src/rabbit_nodes.erl @@ -23,6 +23,7 @@ filter_running/1, filter_not_running/1, is_serving/1, list_serving/0, list_not_serving/0, filter_serving/1, filter_not_serving/1, + list_serving_with_plugin/1, filter_with_plugin/2, name_type/0, running_count/0, total_count/0, await_running_count/2, is_single_node_cluster/0, boot/0]). @@ -457,10 +458,63 @@ filter_not_serving(Nodes) -> Serving = do_filter_serving(Members), Members -- Serving. +%% @doc Combined list_serving/0 and rabbit_plugins:is_enabled_on_node/2 +%% to return nodes that are running, not under maintenance mode +%% and have a specific plugin enabled. +%% @see list_serving/0 +%% @see rabbit_plugins:is_enabled_on_node/2 +-spec list_serving_with_plugin(PluginName :: rabbit_plugins:plugin_name()) -> [node()]. +list_serving_with_plugin(PluginName) -> + Members = list_serving(), + filter_with_plugin(PluginName, Members). + +%% @doc Filters the given list of nodes to only select those belonging to the +%% cluster and having a specific plugin enabled. +%% +%% The cluster being considered is the one which the node running this +%% function belongs to. +%% +%% @see filter_serving/1. +-spec filter_with_plugin(rabbit_plugins:plugin_name(), [node()]) -> [node()]. +filter_with_plugin(PluginName, Nodes) -> + Members = filter_members(Nodes), + do_filter_with_plugin(PluginName, Members). + + +%% @doc Filters the given list of cluster members to only select those who +%% accept clients. +%% +%% The given list of nodes must have been verified to only contain cluster +%% members. +%% +%% @private + +do_filter_with_plugin(PluginName, Members) -> + %% All clustered members having a specific plugin enabled + Rets = erpc:multicall( + Members, rabbit_plugins, is_enabled, [PluginName], ?FILTER_RPC_TIMEOUT), + RetPerMember = lists:zip(Members, Rets), + lists:filtermap( + fun + ({Member, {ok, true}}) -> + {true, Member}; + ({_, {ok, false}}) -> + false; + ({_, {error, {erpc, Reason}}}) + when Reason =:= noconnection orelse Reason =:= timeout -> + false; + ({Member, Error}) -> + ?LOG_ERROR( + "~s:~s: Failed to query node ~ts: ~p", + [?MODULE, ?FUNCTION_NAME, Member, Error], + #{domain => ?RMQLOG_DOMAIN_GLOBAL}), + false + end, RetPerMember). + -spec do_filter_serving(Members) -> Members when Members :: [node()]. %% @doc Filters the given list of cluster members to only select those who -%% accept clients. +%% accept client connections. %% %% The given list of nodes must have been verified to only contain cluster %% members. diff --git a/deps/rabbit/src/rabbit_plugins.erl b/deps/rabbit/src/rabbit_plugins.erl index 086adae509..d4d0ccd4c5 100644 --- a/deps/rabbit/src/rabbit_plugins.erl +++ b/deps/rabbit/src/rabbit_plugins.erl @@ -13,11 +13,16 @@ -export([validate_plugins/1, format_invalid_plugins/1]). -export([is_strictly_plugin/1, strictly_plugins/2, strictly_plugins/1]). -export([plugins_dir/0, plugin_names/1, plugins_expand_dir/0, enabled_plugins_file/0]). +-export([is_enabled/1, is_enabled_on_node/2]). % Export for testing purpose. -export([is_version_supported/2, validate_plugins/2]). %%---------------------------------------------------------------------------- +-export_type([ + plugin_name/0 +]). + -type plugin_name() :: atom(). %%---------------------------------------------------------------------------- @@ -129,6 +134,29 @@ active() -> [App || {App, _, _} <- rabbit_misc:which_applications(), lists:member(App, InstalledPlugins)]. +%% @doc Returns true if the plugin is enabled on the current node. + +-spec is_enabled(Name :: plugin_name()) -> boolean(). + +is_enabled(Name) -> + EnabledPlugins = active(), + lists:member(Name, EnabledPlugins). + +%% @doc Returns true if the plugin is enabled on the given node. + +-spec is_enabled_on_node(Name :: plugin_name(), Node :: node()) -> boolean(). + +is_enabled_on_node(Name, Node) -> + try + case erpc:call(Node, ?MODULE, is_enabled, [Name], 5000) of + true -> true; + _ -> false + end + catch + error:{erpc, _} -> false; + _Class:_Reason:_Stacktrace -> false + end. + %% @doc Get the list of plugins which are ready to be enabled. -spec list(string()) -> [#plugin{}]. diff --git a/deps/rabbit/test/unit_plugin_directories_SUITE.erl b/deps/rabbit/test/unit_plugin_directories_SUITE.erl index 8cf751d8dc..3cc5bd714a 100644 --- a/deps/rabbit/test/unit_plugin_directories_SUITE.erl +++ b/deps/rabbit/test/unit_plugin_directories_SUITE.erl @@ -61,6 +61,10 @@ listing_plugins_from_multiple_directories(Config) -> end, Path = FirstDir ++ PathSep ++ SecondDir, Got = lists:sort([{Name, Vsn} || #plugin{name = Name, version = Vsn} <- rabbit_plugins:list(Path)]), + PluginsMap = maps:from_list(Got), + ?assert(maps:is_key(plugin_first_dir, PluginsMap)), + ?assert(maps:is_key(plugin_second_dir, PluginsMap)), + ?assert(maps:is_key(plugin_both, PluginsMap)), %% `rabbit` was loaded automatically by `rabbit_plugins:list/1`. %% We want to unload it now so it does not interfere with other %% testcases.