summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile3
-rw-r--r--docs/rabbitmqctl.1.xml134
-rw-r--r--ebin/rabbit_app.in5
-rw-r--r--include/rabbit.hrl5
-rw-r--r--include/rabbit_auth_backend_spec.hrl3
-rw-r--r--packaging/RPMS/Fedora/rabbitmq-server.spec3
-rw-r--r--packaging/debs/Debian/debian/changelog6
-rw-r--r--src/rabbit.erl29
-rw-r--r--src/rabbit_access_control.erl27
-rw-r--r--src/rabbit_amqqueue.erl13
-rw-r--r--src/rabbit_amqqueue_process.erl4
-rw-r--r--src/rabbit_auth_backend.erl8
-rw-r--r--src/rabbit_auth_backend_internal.erl111
-rw-r--r--src/rabbit_binding.erl35
-rw-r--r--src/rabbit_control.erl113
-rw-r--r--src/rabbit_event.erl12
-rw-r--r--src/rabbit_exchange.erl21
-rw-r--r--src/rabbit_router.erl35
-rw-r--r--src/rabbit_tests.erl20
-rw-r--r--src/rabbit_types.erl4
-rw-r--r--src/rabbit_upgrade_functions.erl12
-rw-r--r--src/rabbit_vhost.erl6
22 files changed, 379 insertions, 230 deletions
diff --git a/Makefile b/Makefile
index a347689b..d8ef058e 100644
--- a/Makefile
+++ b/Makefile
@@ -162,7 +162,8 @@ run-node: all
./scripts/rabbitmq-server
run-tests: all
- echo "rabbit_tests:all_tests()." | $(ERL_CALL)
+ OUT=$$(echo "rabbit_tests:all_tests()." | $(ERL_CALL)) ; \
+ echo $$OUT ; echo $$OUT | grep '^{ok, passed}$$' > /dev/null
start-background-node:
$(BASIC_SCRIPT_ENVIRONMENT_SETTINGS) \
diff --git a/docs/rabbitmqctl.1.xml b/docs/rabbitmqctl.1.xml
index b825a1d0..cc3d4d2a 100644
--- a/docs/rabbitmqctl.1.xml
+++ b/docs/rabbitmqctl.1.xml
@@ -59,6 +59,11 @@
RabbitMQ broker. It performs all actions by connecting to one of the
broker's nodes.
</para>
+ <para>
+ Diagnostic information is displayed if the broker was not
+ running, could not be reached, or rejected the connection due to
+ mismatching Erlang cookies.
+ </para>
</refsect1>
<refsect1>
@@ -179,24 +184,6 @@
</listitem>
</varlistentry>
- <varlistentry>
- <term><cmdsynopsis><command>status</command></cmdsynopsis></term>
- <listitem>
- <para>
- Displays various information about the RabbitMQ broker,
- such as whether the RabbitMQ application on the current
- node, its version number, what nodes are part of the
- broker, which of these are running.
- </para>
- <para role="example-prefix">For example:</para>
- <screen role="example">rabbitmqctl status</screen>
- <para role="example">
- This command displays information about the RabbitMQ
- broker.
- </para>
- </listitem>
- </varlistentry>
-
<varlistentry id="reset">
<term><cmdsynopsis><command>reset</command></cmdsynopsis></term>
<listitem>
@@ -377,6 +364,20 @@
</para>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><cmdsynopsis><command>cluster_status</command></cmdsynopsis></term>
+ <listitem>
+ <para>
+ Displays all the nodes in the cluster grouped by node type,
+ together with the currently running nodes.
+ </para>
+ <para role="example-prefix">For example:</para>
+ <screen role="example">rabbitmqctl cluster_status</screen>
+ <para role="example">
+ This command displays the nodes in the cluster.
+ </para>
+ </listitem>
+ </varlistentry>
</variablelist>
</refsect2>
@@ -512,17 +513,22 @@
</varlistentry>
<varlistentry>
- <term><cmdsynopsis><command>set_admin</command> <arg choice="req"><replaceable>username</replaceable></arg></cmdsynopsis></term>
+ <term><cmdsynopsis><command>set_user_tags</command> <arg choice="req"><replaceable>username</replaceable></arg> <arg choice="req"><replaceable>tag</replaceable> ...</arg></cmdsynopsis></term>
<listitem>
<variablelist>
<varlistentry>
<term>username</term>
- <listitem><para>The name of the user whose administrative
- status is to be set.</para></listitem>
+ <listitem><para>The name of the user whose tags are to
+ be set.</para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>tag</term>
+ <listitem><para>Zero, one or more tags to set. Any
+ existing tags will be removed.</para></listitem>
</varlistentry>
</variablelist>
<para role="example-prefix">For example:</para>
- <screen role="example">rabbitmqctl set_admin tonyg</screen>
+ <screen role="example">rabbitmqctl set_user_tags tonyg administrator</screen>
<para role="example">
This command instructs the RabbitMQ broker to ensure the user
named <command>tonyg</command> is an administrator. This has no
@@ -531,24 +537,10 @@
user logs in via some other means (for example with the
management plugin).
</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term><cmdsynopsis><command>clear_admin</command> <arg choice="req"><replaceable>username</replaceable></arg></cmdsynopsis></term>
- <listitem>
- <variablelist>
- <varlistentry>
- <term>username</term>
- <listitem><para>The name of the user whose administrative
- status is to be cleared.</para></listitem>
- </varlistentry>
- </variablelist>
- <para role="example-prefix">For example:</para>
- <screen role="example">rabbitmqctl clear_admin tonyg</screen>
+ <screen role="example">rabbitmqctl set_user_tags tonyg</screen>
<para role="example">
- This command instructs the RabbitMQ broker to ensure the user
- named <command>tonyg</command> is not an administrator.
+ This command instructs the RabbitMQ broker to remove any
+ tags from the user named <command>tonyg</command>.
</para>
</listitem>
</varlistentry>
@@ -647,6 +639,10 @@
<listitem><para>Whether tracing is enabled for this virtual host.</para></listitem>
</varlistentry>
</variablelist>
+ <para>
+ If no <command>vhostinfoitem</command>s are specified
+ then the vhost name is displayed.
+ </para>
<para role="example-prefix">For example:</para>
<screen role="example">rabbitmqctl list_vhosts name tracing</screen>
<para role="example">
@@ -1259,7 +1255,7 @@
</varlistentry>
<varlistentry>
- <term><cmdsynopsis><command>list_consumers</command></cmdsynopsis></term>
+ <term><cmdsynopsis><command>list_consumers</command> <arg choice="opt">-p <replaceable>vhostpath</replaceable></arg></cmdsynopsis></term>
<listitem>
<para>
List consumers, i.e. subscriptions to a queue's message
@@ -1271,11 +1267,59 @@
indicating whether acknowledgements are expected for
messages delivered to this consumer.
</para>
- <para role="usage">
- The output format for "list_consumers" is a list of rows containing,
- in order, the queue name, channel process id, consumer tag, and a
- boolean indicating whether acknowledgements are expected from the
- consumer.
+ <para>
+ The output is a list of rows containing, in order, the queue name,
+ channel process id, consumer tag, and a boolean indicating whether
+ acknowledgements are expected from the consumer.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><cmdsynopsis><command>status</command></cmdsynopsis></term>
+ <listitem>
+ <para>
+ Displays broker status information such as the running
+ applications on the current Erlang node, RabbitMQ and
+ Erlang versions and OS name. (See
+ the <command>cluster_status</command> command to find
+ out which nodes are clustered and running.)
+ </para>
+ <para role="example-prefix">For example:</para>
+ <screen role="example">rabbitmqctl status</screen>
+ <para role="example">
+ This command displays information about the RabbitMQ
+ broker.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><cmdsynopsis><command>environment</command></cmdsynopsis></term>
+ <listitem>
+ <para>
+ Display the name and value of each variable in the
+ application environment.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><cmdsynopsis><command>report</command></cmdsynopsis></term>
+ <listitem>
+ <para>
+ Generate a server status report containing a
+ concatenation of all server status information for
+ support purposes. The output should be redirected to a
+ file when accompanying a support request.
+ </para>
+ <para role="example-prefix">
+ For example:
+ </para>
+ <screen role="example">rabbitmqctl report > server_report.txt</screen>
+ <para role="example">
+ This command creates a server report which may be
+ attached to a support request email.
</para>
</listitem>
</varlistentry>
diff --git a/ebin/rabbit_app.in b/ebin/rabbit_app.in
index 7dabb8c3..65a3269a 100644
--- a/ebin/rabbit_app.in
+++ b/ebin/rabbit_app.in
@@ -21,18 +21,17 @@
{msg_store_index_module, rabbit_msg_store_ets_index},
{backing_queue_module, rabbit_variable_queue},
{frame_max, 131072},
- {persister_max_wrap_entries, 500},
- {persister_hibernate_after, 10000},
{msg_store_file_size_limit, 16777216},
{queue_index_max_journal_entries, 262144},
{default_user, <<"guest">>},
{default_pass, <<"guest">>},
- {default_user_is_admin, true},
+ {default_user_tags, [administrator]},
{default_vhost, <<"/">>},
{default_permissions, [<<".*">>, <<".*">>, <<".*">>]},
{cluster_nodes, []},
{server_properties, []},
{collect_statistics, none},
+ {collect_statistics_interval, 5000},
{auth_mechanisms, ['PLAIN', 'AMQPLAIN']},
{auth_backends, [rabbit_auth_backend_internal]},
{delegate_count, 16},
diff --git a/include/rabbit.hrl b/include/rabbit.hrl
index 1388f3c4..0a202c5e 100644
--- a/include/rabbit.hrl
+++ b/include/rabbit.hrl
@@ -15,12 +15,12 @@
%%
-record(user, {username,
- is_admin,
+ tags,
auth_backend, %% Module this user came from
impl %% Scratch space for that module
}).
--record(internal_user, {username, password_hash, is_admin}).
+-record(internal_user, {username, password_hash, tags}).
-record(permission, {configure, write, read}).
-record(user_vhost, {username, virtual_host}).
-record(user_permission, {user_vhost, permission}).
@@ -86,7 +86,6 @@
-define(HIBERNATE_AFTER_MIN, 1000).
-define(DESIRED_HIBERNATE, 10000).
--define(STATS_INTERVAL, 5000).
-define(ROUTING_HEADERS, [<<"CC">>, <<"BCC">>]).
-define(DELETED_HEADER, <<"BCC">>).
diff --git a/include/rabbit_auth_backend_spec.hrl b/include/rabbit_auth_backend_spec.hrl
index e26d44ea..803bb75c 100644
--- a/include/rabbit_auth_backend_spec.hrl
+++ b/include/rabbit_auth_backend_spec.hrl
@@ -22,8 +22,7 @@
{'ok', rabbit_types:user()} |
{'refused', string(), [any()]} |
{'error', any()}).
--spec(check_vhost_access/3 :: (rabbit_types:user(), rabbit_types:vhost(),
- rabbit_access_control:vhost_permission_atom()) ->
+-spec(check_vhost_access/2 :: (rabbit_types:user(), rabbit_types:vhost()) ->
boolean() | {'error', any()}).
-spec(check_resource_access/3 :: (rabbit_types:user(),
rabbit_types:r(atom()),
diff --git a/packaging/RPMS/Fedora/rabbitmq-server.spec b/packaging/RPMS/Fedora/rabbitmq-server.spec
index f9e9df8b..ffc826eb 100644
--- a/packaging/RPMS/Fedora/rabbitmq-server.spec
+++ b/packaging/RPMS/Fedora/rabbitmq-server.spec
@@ -120,6 +120,9 @@ done
rm -rf %{buildroot}
%changelog
+* Thu Jun 9 2011 jerryk@vmware.com 2.5.0-1
+- New Upstream Release
+
* Thu Apr 7 2011 Alexandru Scvortov <alexandru@rabbitmq.com> 2.4.1-1
- New Upstream Release
diff --git a/packaging/debs/Debian/debian/changelog b/packaging/debs/Debian/debian/changelog
index 0383b955..1cab4235 100644
--- a/packaging/debs/Debian/debian/changelog
+++ b/packaging/debs/Debian/debian/changelog
@@ -1,3 +1,9 @@
+rabbitmq-server (2.5.0-1) lucid; urgency=low
+
+ * New Upstream Release
+
+ -- <jerryk@vmware.com> Thu, 09 Jun 2011 07:20:29 -0700
+
rabbitmq-server (2.4.1-1) lucid; urgency=low
* New Upstream Release
diff --git a/src/rabbit.erl b/src/rabbit.erl
index e6e80b4a..100cacb0 100644
--- a/src/rabbit.erl
+++ b/src/rabbit.erl
@@ -18,7 +18,7 @@
-behaviour(application).
--export([prepare/0, start/0, stop/0, stop_and_halt/0, status/0,
+-export([prepare/0, start/0, stop/0, stop_and_halt/0, status/0, environment/0,
rotate_logs/1]).
-export([start/2, stop/1]).
@@ -178,9 +178,12 @@
-spec(stop_and_halt/0 :: () -> 'ok').
-spec(rotate_logs/1 :: (file_suffix()) -> rabbit_types:ok_or_error(any())).
-spec(status/0 ::
- () -> [{running_applications, [{atom(), string(), string()}]} |
- {nodes, [{rabbit_mnesia:node_type(), [node()]}]} |
- {running_nodes, [node()]}]).
+ () -> [{pid, integer()} |
+ {running_applications, [{atom(), string(), string()}]} |
+ {os, {atom(), atom()}} |
+ {erlang_version, string()} |
+ {memory, any()}]).
+-spec(environment/0 :: () -> [{atom() | term()}]).
-spec(log_location/1 :: ('sasl' | 'kernel') -> log_location()).
-spec(maybe_insert_default_data/0 :: () -> 'ok').
@@ -217,8 +220,15 @@ stop_and_halt() ->
status() ->
[{pid, list_to_integer(os:getpid())},
- {running_applications, application:which_applications()}] ++
- rabbit_mnesia:status().
+ {running_applications, application:which_applications()},
+ {os, os:type()},
+ {erlang_version, erlang:system_info(system_version)},
+ {memory, erlang:memory()}].
+
+environment() ->
+ lists:keysort(
+ 1, [P || P = {K, _} <- application:get_all_env(rabbit),
+ K =/= default_pass]).
rotate_logs(BinarySuffix) ->
Suffix = binary_to_list(BinarySuffix),
@@ -476,16 +486,13 @@ maybe_insert_default_data() ->
insert_default_data() ->
{ok, DefaultUser} = application:get_env(default_user),
{ok, DefaultPass} = application:get_env(default_pass),
- {ok, DefaultAdmin} = application:get_env(default_user_is_admin),
+ {ok, DefaultTags} = application:get_env(default_user_tags),
{ok, DefaultVHost} = application:get_env(default_vhost),
{ok, [DefaultConfigurePerm, DefaultWritePerm, DefaultReadPerm]} =
application:get_env(default_permissions),
ok = rabbit_vhost:add(DefaultVHost),
ok = rabbit_auth_backend_internal:add_user(DefaultUser, DefaultPass),
- case DefaultAdmin of
- true -> rabbit_auth_backend_internal:set_admin(DefaultUser);
- _ -> ok
- end,
+ ok = rabbit_auth_backend_internal:set_tags(DefaultUser, DefaultTags),
ok = rabbit_auth_backend_internal:set_permissions(DefaultUser, DefaultVHost,
DefaultConfigurePerm,
DefaultWritePerm,
diff --git a/src/rabbit_access_control.erl b/src/rabbit_access_control.erl
index 59c00848..c0ae18c0 100644
--- a/src/rabbit_access_control.erl
+++ b/src/rabbit_access_control.erl
@@ -19,16 +19,15 @@
-include("rabbit.hrl").
-export([check_user_pass_login/2, check_user_login/2,
- check_vhost_access/2, check_resource_access/3, list_vhosts/2]).
+ check_vhost_access/2, check_resource_access/3]).
%%----------------------------------------------------------------------------
-ifdef(use_specs).
--export_type([permission_atom/0, vhost_permission_atom/0]).
+-export_type([permission_atom/0]).
-type(permission_atom() :: 'configure' | 'read' | 'write').
--type(vhost_permission_atom() :: 'read' | 'write').
-spec(check_user_pass_login/2 ::
(rabbit_types:username(), rabbit_types:password())
@@ -39,8 +38,6 @@
-spec(check_resource_access/3 ::
(rabbit_types:user(), rabbit_types:r(atom()), permission_atom())
-> 'ok' | rabbit_types:channel_exit()).
--spec(list_vhosts/2 :: (rabbit_types:user(), vhost_permission_atom())
- -> [rabbit_types:vhost()]).
-endif.
@@ -70,7 +67,7 @@ check_vhost_access(User = #user{ username = Username,
check_access(
fun() ->
rabbit_vhost:exists(VHostPath) andalso
- Module:check_vhost_access(User, VHostPath, write)
+ Module:check_vhost_access(User, VHostPath)
end,
"~s failed checking vhost access to ~s for ~s: ~p~n",
[Module, VHostPath, Username],
@@ -104,21 +101,3 @@ check_access(Fun, ErrStr, ErrArgs, RefStr, RefArgs) ->
false ->
rabbit_misc:protocol_error(access_refused, RefStr, RefArgs)
end.
-
-%% Permission = write -> log in
-%% Permission = read -> learn of the existence of (only relevant for
-%% management plugin)
-list_vhosts(User = #user{username = Username, auth_backend = Module},
- Permission) ->
- lists:filter(
- fun(VHost) ->
- case Module:check_vhost_access(User, VHost, Permission) of
- {error, _} = E ->
- rabbit_log:warning("~w failed checking vhost access "
- "to ~s for ~s: ~p~n",
- [Module, VHost, Username, E]),
- false;
- Else ->
- Else
- end
- end, rabbit_vhost:list()).
diff --git a/src/rabbit_amqqueue.erl b/src/rabbit_amqqueue.erl
index 7f46b870..e5c53620 100644
--- a/src/rabbit_amqqueue.erl
+++ b/src/rabbit_amqqueue.erl
@@ -22,7 +22,7 @@
check_exclusive_access/2, with_exclusive_access_or_die/3,
stat/1, deliver/2, requeue/3, ack/4, reject/4]).
-export([list/1, info_keys/0, info/1, info/2, info_all/1, info_all/2]).
--export([consumers/1, consumers_all/1]).
+-export([consumers/1, consumers_all/1, consumer_info_keys/0]).
-export([basic_get/3, basic_consume/7, basic_cancel/4]).
-export([notify_sent/2, unblock/2, flush_all/2]).
-export([commit_all/3, rollback_all/3, notify_down_all/2, limit_all/3]).
@@ -94,6 +94,7 @@
-spec(consumers/1 ::
(rabbit_types:amqqueue())
-> [{pid(), rabbit_types:ctag(), boolean()}]).
+-spec(consumer_info_keys/0 :: () -> rabbit_types:info_keys()).
-spec(consumers_all/1 ::
(rabbit_types:vhost())
-> [{name(), pid(), rabbit_types:ctag(), boolean()}]).
@@ -161,6 +162,9 @@
%%----------------------------------------------------------------------------
+-define(CONSUMER_INFO_KEYS,
+ [queue_name, channel_pid, consumer_tag, ack_required]).
+
start() ->
DurableQueues = find_durable_queues(),
{ok, BQ} = application:get_env(rabbit, backing_queue_module),
@@ -363,10 +367,15 @@ info_all(VHostPath, Items) -> map(VHostPath, fun (Q) -> info(Q, Items) end).
consumers(#amqqueue{ pid = QPid }) ->
delegate_call(QPid, consumers).
+consumer_info_keys() -> ?CONSUMER_INFO_KEYS.
+
consumers_all(VHostPath) ->
+ ConsumerInfoKeys=consumer_info_keys(),
lists:append(
map(VHostPath,
- fun (Q) -> [{Q#amqqueue.name, ChPid, ConsumerTag, AckRequired} ||
+ fun (Q) ->
+ [lists:zip(ConsumerInfoKeys,
+ [Q#amqqueue.name, ChPid, ConsumerTag, AckRequired]) ||
{ChPid, ConsumerTag, AckRequired} <- consumers(Q)]
end)).
diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl
index 17c35e90..e7cb67a2 100644
--- a/src/rabbit_amqqueue_process.erl
+++ b/src/rabbit_amqqueue_process.erl
@@ -465,9 +465,7 @@ confirm_messages(MsgIds, State = #q{msg_id_to_channel = MTC}) ->
{CMs, MTC0}
end
end, {gb_trees:empty(), MTC}, MsgIds),
- gb_trees_foreach(fun(ChPid, MsgSeqNos) ->
- rabbit_channel:confirm(ChPid, MsgSeqNos)
- end, CMs),
+ gb_trees_foreach(fun rabbit_channel:confirm/2, CMs),
State#q{msg_id_to_channel = MTC1}.
gb_trees_foreach(_, none) ->
diff --git a/src/rabbit_auth_backend.erl b/src/rabbit_auth_backend.erl
index 09820c5b..ade158bb 100644
--- a/src/rabbit_auth_backend.erl
+++ b/src/rabbit_auth_backend.erl
@@ -36,17 +36,13 @@ behaviour_info(callbacks) ->
%% Client failed authentication. Log and die.
{check_user_login, 2},
- %% Given #user, vhost path and permission, can a user access a vhost?
- %% Permission is read - learn of the existence of (only relevant for
- %% management plugin)
- %% or write - log in
- %%
+ %% Given #user and vhost, can a user log in to a vhost?
%% Possible responses:
%% true
%% false
%% {error, Error}
%% Something went wrong. Log and die.
- {check_vhost_access, 3},
+ {check_vhost_access, 2},
%% Given #user, resource and permission, can a user access a resource?
%%
diff --git a/src/rabbit_auth_backend_internal.erl b/src/rabbit_auth_backend_internal.erl
index f70813d1..6a018bd1 100644
--- a/src/rabbit_auth_backend_internal.erl
+++ b/src/rabbit_auth_backend_internal.erl
@@ -20,15 +20,17 @@
-behaviour(rabbit_auth_backend).
-export([description/0]).
--export([check_user_login/2, check_vhost_access/3, check_resource_access/3]).
+-export([check_user_login/2, check_vhost_access/2, check_resource_access/3]).
--export([add_user/2, delete_user/1, change_password/2, set_admin/1,
- clear_admin/1, list_users/0, lookup_user/1, clear_password/1]).
+-export([add_user/2, delete_user/1, change_password/2, set_tags/2,
+ list_users/0, user_info_keys/0, lookup_user/1, clear_password/1]).
-export([make_salt/0, check_password/2, change_password_hash/2,
hash_password/1]).
-export([set_permissions/5, clear_permissions/2,
list_permissions/0, list_vhost_permissions/1, list_user_permissions/1,
- list_user_vhost_permissions/2]).
+ list_user_vhost_permissions/2, perms_info_keys/0,
+ vhost_perms_info_keys/0, user_perms_info_keys/0,
+ user_vhost_perms_info_keys/0]).
-include("rabbit_auth_backend_spec.hrl").
@@ -48,9 +50,9 @@
rabbit_types:password_hash()) -> 'ok').
-spec(hash_password/1 :: (rabbit_types:password())
-> rabbit_types:password_hash()).
--spec(set_admin/1 :: (rabbit_types:username()) -> 'ok').
--spec(clear_admin/1 :: (rabbit_types:username()) -> 'ok').
--spec(list_users/0 :: () -> [{rabbit_types:username(), boolean()}]).
+-spec(set_tags/2 :: (rabbit_types:username(), [atom()]) -> 'ok').
+-spec(list_users/0 :: () -> rabbit_types:infos()).
+-spec(user_info_keys/0 :: () -> rabbit_types:info_keys()).
-spec(lookup_user/1 :: (rabbit_types:username())
-> rabbit_types:ok(rabbit_types:internal_user())
| rabbit_types:error('not_found')).
@@ -58,23 +60,25 @@
regexp(), regexp(), regexp()) -> 'ok').
-spec(clear_permissions/2 :: (rabbit_types:username(), rabbit_types:vhost())
-> 'ok').
--spec(list_permissions/0 ::
- () -> [{rabbit_types:username(), rabbit_types:vhost(),
- regexp(), regexp(), regexp()}]).
+-spec(list_permissions/0 :: () -> rabbit_types:infos()).
-spec(list_vhost_permissions/1 ::
- (rabbit_types:vhost()) -> [{rabbit_types:username(),
- regexp(), regexp(), regexp()}]).
+ (rabbit_types:vhost()) -> rabbit_types:infos()).
-spec(list_user_permissions/1 ::
- (rabbit_types:username()) -> [{rabbit_types:vhost(),
- regexp(), regexp(), regexp()}]).
+ (rabbit_types:username()) -> rabbit_types:infos()).
-spec(list_user_vhost_permissions/2 ::
(rabbit_types:username(), rabbit_types:vhost())
- -> [{regexp(), regexp(), regexp()}]).
-
+ -> rabbit_types:infos()).
+-spec(perms_info_keys/0 :: () -> rabbit_types:info_keys()).
+-spec(vhost_perms_info_keys/0 :: () -> rabbit_types:info_keys()).
+-spec(user_perms_info_keys/0 :: () -> rabbit_types:info_keys()).
+-spec(user_vhost_perms_info_keys/0 :: () -> rabbit_types:info_keys()).
-endif.
%%----------------------------------------------------------------------------
+-define(PERMS_INFO_KEYS, [configure, write, read]).
+-define(USER_INFO_KEYS, [user, tags]).
+
%% Implementation of rabbit_auth_backend
description() ->
@@ -94,10 +98,10 @@ check_user_login(Username, AuthProps) ->
internal_check_user_login(Username, Fun) ->
Refused = {refused, "user '~s' - invalid credentials", [Username]},
case lookup_user(Username) of
- {ok, User = #internal_user{is_admin = IsAdmin}} ->
+ {ok, User = #internal_user{tags = Tags}} ->
case Fun(User) of
true -> {ok, #user{username = Username,
- is_admin = IsAdmin,
+ tags = Tags,
auth_backend = ?MODULE,
impl = User}};
_ -> Refused
@@ -106,16 +110,13 @@ internal_check_user_login(Username, Fun) ->
Refused
end.
-check_vhost_access(#user{is_admin = true}, _VHostPath, read) ->
- true;
-
-check_vhost_access(#user{username = Username}, VHostPath, _) ->
+check_vhost_access(#user{username = Username}, VHost) ->
%% TODO: use dirty ops instead
rabbit_misc:execute_mnesia_transaction(
fun () ->
case mnesia:read({rabbit_user_permission,
#user_vhost{username = Username,
- virtual_host = VHostPath}}) of
+ virtual_host = VHost}}) of
[] -> false;
[_R] -> true
end
@@ -158,7 +159,7 @@ add_user(Username, Password) ->
#internal_user{username = Username,
password_hash =
hash_password(Password),
- is_admin = false},
+ tags = []},
write);
_ ->
mnesia:abort({user_already_exists, Username})
@@ -219,16 +220,12 @@ salted_md5(Salt, Cleartext) ->
Salted = <<Salt/binary, Cleartext/binary>>,
erlang:md5(Salted).
-set_admin(Username) -> set_admin(Username, true).
-
-clear_admin(Username) -> set_admin(Username, false).
-
-set_admin(Username, IsAdmin) ->
+set_tags(Username, Tags) ->
R = update_user(Username, fun(User) ->
- User#internal_user{is_admin = IsAdmin}
+ User#internal_user{tags = Tags}
end),
- rabbit_log:info("Set user admin flag for user ~p to ~p~n",
- [Username, IsAdmin]),
+ rabbit_log:info("Set user tags for user ~p to ~p~n",
+ [Username, Tags]),
R.
update_user(Username, Fun) ->
@@ -241,10 +238,12 @@ update_user(Username, Fun) ->
end)).
list_users() ->
- [{Username, IsAdmin} ||
- #internal_user{username = Username, is_admin = IsAdmin} <-
+ [[{user, Username}, {tags, Tags}] ||
+ #internal_user{username = Username, tags = Tags} <-
mnesia:dirty_match_object(rabbit_user, #internal_user{_ = '_'})].
+user_info_keys() -> ?USER_INFO_KEYS.
+
lookup_user(Username) ->
rabbit_misc:dirty_read({rabbit_user, Username}).
@@ -283,32 +282,38 @@ clear_permissions(Username, VHostPath) ->
virtual_host = VHostPath}})
end)).
+perms_info_keys() -> [user, vhost | ?PERMS_INFO_KEYS].
+vhost_perms_info_keys() -> [user | ?PERMS_INFO_KEYS].
+user_perms_info_keys() -> [vhost | ?PERMS_INFO_KEYS].
+user_vhost_perms_info_keys() -> ?PERMS_INFO_KEYS.
+
list_permissions() ->
- [{Username, VHostPath, ConfigurePerm, WritePerm, ReadPerm} ||
- {Username, VHostPath, ConfigurePerm, WritePerm, ReadPerm} <-
- list_permissions(match_user_vhost('_', '_'))].
+ list_permissions(perms_info_keys(), match_user_vhost('_', '_')).
list_vhost_permissions(VHostPath) ->
- [{Username, ConfigurePerm, WritePerm, ReadPerm} ||
- {Username, _, ConfigurePerm, WritePerm, ReadPerm} <-
- list_permissions(rabbit_vhost:with(
- VHostPath, match_user_vhost('_', VHostPath)))].
+ list_permissions(
+ vhost_perms_info_keys(),
+ rabbit_vhost:with(VHostPath, match_user_vhost('_', VHostPath))).
list_user_permissions(Username) ->
- [{VHostPath, ConfigurePerm, WritePerm, ReadPerm} ||
- {_, VHostPath, ConfigurePerm, WritePerm, ReadPerm} <-
- list_permissions(rabbit_misc:with_user(
- Username, match_user_vhost(Username, '_')))].
+ list_permissions(
+ user_perms_info_keys(),
+ rabbit_misc:with_user(Username, match_user_vhost(Username, '_'))).
list_user_vhost_permissions(Username, VHostPath) ->
- [{ConfigurePerm, WritePerm, ReadPerm} ||
- {_, _, ConfigurePerm, WritePerm, ReadPerm} <-
- list_permissions(rabbit_misc:with_user_and_vhost(
- Username, VHostPath,
- match_user_vhost(Username, VHostPath)))].
-
-list_permissions(QueryThunk) ->
- [{Username, VHostPath, ConfigurePerm, WritePerm, ReadPerm} ||
+ list_permissions(
+ user_vhost_perms_info_keys(),
+ rabbit_misc:with_user_and_vhost(
+ Username, VHostPath, match_user_vhost(Username, VHostPath))).
+
+filter_props(Keys, Props) -> [T || T = {K, _} <- Props, lists:member(K, Keys)].
+
+list_permissions(Keys, QueryThunk) ->
+ [filter_props(Keys, [{user, Username},
+ {vhost, VHostPath},
+ {configure, ConfigurePerm},
+ {write, WritePerm},
+ {read, ReadPerm}]) ||
#user_permission{user_vhost = #user_vhost{username = Username,
virtual_host = VHostPath},
permission = #permission{ configure = ConfigurePerm,
diff --git a/src/rabbit_binding.erl b/src/rabbit_binding.erl
index 2f71bfab..5873537c 100644
--- a/src/rabbit_binding.erl
+++ b/src/rabbit_binding.erl
@@ -198,22 +198,33 @@ list(VHostPath) ->
Route)].
list_for_source(SrcName) ->
- Route = #route{binding = #binding{source = SrcName, _ = '_'}},
- [B || #route{binding = B} <- mnesia:dirty_match_object(rabbit_route,
- Route)].
+ mnesia:async_dirty(
+ fun() ->
+ Route = #route{binding = #binding{source = SrcName, _ = '_'}},
+ [B || #route{binding = B}
+ <- mnesia:match_object(rabbit_route, Route, read)]
+ end).
list_for_destination(DstName) ->
- Route = #route{binding = #binding{destination = DstName, _ = '_'}},
- [reverse_binding(B) || #reverse_route{reverse_binding = B} <-
- mnesia:dirty_match_object(rabbit_reverse_route,
- reverse_route(Route))].
+ mnesia:async_dirty(
+ fun() ->
+ Route = #route{binding = #binding{destination = DstName,
+ _ = '_'}},
+ [reverse_binding(B) ||
+ #reverse_route{reverse_binding = B} <-
+ mnesia:match_object(rabbit_reverse_route,
+ reverse_route(Route), read)]
+ end).
list_for_source_and_destination(SrcName, DstName) ->
- Route = #route{binding = #binding{source = SrcName,
- destination = DstName,
- _ = '_'}},
- [B || #route{binding = B} <- mnesia:dirty_match_object(rabbit_route,
- Route)].
+ mnesia:async_dirty(
+ fun() ->
+ Route = #route{binding = #binding{source = SrcName,
+ destination = DstName,
+ _ = '_'}},
+ [B || #route{binding = B} <- mnesia:match_object(rabbit_route,
+ Route, read)]
+ end).
info_keys() -> ?INFO_KEYS.
diff --git a/src/rabbit_control.erl b/src/rabbit_control.erl
index 117496f5..9194a45b 100644
--- a/src/rabbit_control.erl
+++ b/src/rabbit_control.erl
@@ -26,6 +26,19 @@
-define(NODE_OPT, "-n").
-define(VHOST_OPT, "-p").
+-define(GLOBAL_QUERIES,
+ [{"Connections", rabbit_networking, connection_info_all,
+ connection_info_keys},
+ {"Channels", rabbit_channel, info_all, info_keys}]).
+
+-define(VHOST_QUERIES,
+ [{"Queues", rabbit_amqqueue, info_all, info_keys},
+ {"Exchanges", rabbit_exchange, info_all, info_keys},
+ {"Bindings", rabbit_binding, info_all, info_keys},
+ {"Consumers", rabbit_amqqueue, consumers_all, consumer_info_keys},
+ {"Permissions", rabbit_auth_backend_internal, list_vhost_permissions,
+ vhost_perms_info_keys}]).
+
%%----------------------------------------------------------------------------
-ifdef(use_specs).
@@ -96,6 +109,23 @@ start() ->
fmt_stderr(Format, Args) -> rabbit_misc:format_stderr(Format ++ "~n", Args).
+print_report(Node, {Descr, Module, InfoFun, KeysFun}) ->
+ io:format("~s:~n", [Descr]),
+ print_report0(Node, {Module, InfoFun, KeysFun}, []).
+
+print_report(Node, {Descr, Module, InfoFun, KeysFun}, VHostArg) ->
+ io:format("~s on ~s:~n", [Descr, VHostArg]),
+ print_report0(Node, {Module, InfoFun, KeysFun}, VHostArg).
+
+print_report0(Node, {Module, InfoFun, KeysFun}, VHostArg) ->
+ case Results = rpc_call(Node, Module, InfoFun, VHostArg) of
+ [_|_] -> InfoItems = rpc_call(Node, Module, KeysFun, []),
+ display_row([atom_to_list(I) || I <- InfoItems]),
+ display_info_list(Results, InfoItems);
+ _ -> ok
+ end,
+ io:nl().
+
print_error(Format, Args) -> fmt_stderr("Error: " ++ Format, Args).
print_badrpc_diagnostics(Node) ->
@@ -167,11 +197,15 @@ action(wait, Node, [], _Opts, Inform) ->
action(status, Node, [], _Opts, Inform) ->
Inform("Status of node ~p", [Node]),
- case call(Node, {rabbit, status, []}) of
- {badrpc, _} = Res -> Res;
- Res -> io:format("~p~n", [Res]),
- ok
- end;
+ display_call_result(Node, {rabbit, status, []});
+
+action(cluster_status, Node, [], _Opts, Inform) ->
+ Inform("Cluster status of node ~p", [Node]),
+ display_call_result(Node, {rabbit_mnesia, status, []});
+
+action(environment, Node, _App, _Opts, Inform) ->
+ Inform("Application environment of node ~p", [Node]),
+ display_call_result(Node, {rabbit, environment, []});
action(rotate_logs, Node, [], _Opts, Inform) ->
Inform("Reopening logs for node ~p", [Node]),
@@ -201,17 +235,17 @@ action(clear_password, Node, Args = [Username], _Opts, Inform) ->
Inform("Clearing password for user ~p", [Username]),
call(Node, {rabbit_auth_backend_internal, clear_password, Args});
-action(set_admin, Node, [Username], _Opts, Inform) ->
- Inform("Setting administrative status for user ~p", [Username]),
- call(Node, {rabbit_auth_backend_internal, set_admin, [Username]});
-
-action(clear_admin, Node, [Username], _Opts, Inform) ->
- Inform("Clearing administrative status for user ~p", [Username]),
- call(Node, {rabbit_auth_backend_internal, clear_admin, [Username]});
+action(set_user_tags, Node, [Username | TagsStr], _Opts, Inform) ->
+ Tags = [list_to_atom(T) || T <- TagsStr],
+ Inform("Setting tags for user ~p to ~p", [Username, Tags]),
+ rpc_call(Node, rabbit_auth_backend_internal, set_tags,
+ [list_to_binary(Username), Tags]);
action(list_users, Node, [], _Opts, Inform) ->
Inform("Listing users", []),
- display_list(call(Node, {rabbit_auth_backend_internal, list_users, []}));
+ display_info_list(
+ call(Node, {rabbit_auth_backend_internal, list_users, []}),
+ rabbit_auth_backend_internal:user_info_keys());
action(add_vhost, Node, Args = [_VHostPath], _Opts, Inform) ->
Inform("Creating vhost ~p", Args),
@@ -228,8 +262,9 @@ action(list_vhosts, Node, Args, _Opts, Inform) ->
action(list_user_permissions, Node, Args = [_Username], _Opts, Inform) ->
Inform("Listing permissions for user ~p", Args),
- display_list(call(Node, {rabbit_auth_backend_internal,
- list_user_permissions, Args}));
+ display_info_list(call(Node, {rabbit_auth_backend_internal,
+ list_user_permissions, Args}),
+ rabbit_auth_backend_internal:user_perms_info_keys());
action(list_queues, Node, Args, Opts, Inform) ->
Inform("Listing queues", []),
@@ -286,14 +321,8 @@ action(list_channels, Node, Args, _Opts, Inform) ->
action(list_consumers, Node, _Args, Opts, Inform) ->
Inform("Listing consumers", []),
VHostArg = list_to_binary(proplists:get_value(?VHOST_OPT, Opts)),
- InfoKeys = [queue_name, channel_pid, consumer_tag, ack_required],
- case rpc_call(Node, rabbit_amqqueue, consumers_all, [VHostArg]) of
- L when is_list(L) -> display_info_list(
- [lists:zip(InfoKeys, tuple_to_list(X)) ||
- X <- L],
- InfoKeys);
- Other -> Other
- end;
+ display_info_list(rpc_call(Node, rabbit_amqqueue, consumers_all, [VHostArg]),
+ rabbit_amqqueue:consumer_info_keys());
action(trace_on, Node, [], Opts, Inform) ->
VHost = proplists:get_value(?VHOST_OPT, Opts),
@@ -320,8 +349,20 @@ action(clear_permissions, Node, [Username], Opts, Inform) ->
action(list_permissions, Node, [], Opts, Inform) ->
VHost = proplists:get_value(?VHOST_OPT, Opts),
Inform("Listing permissions in vhost ~p", [VHost]),
- display_list(call(Node, {rabbit_auth_backend_internal,
- list_vhost_permissions, [VHost]})).
+ display_info_list(call(Node, {rabbit_auth_backend_internal,
+ list_vhost_permissions, [VHost]}),
+ rabbit_auth_backend_internal:vhost_perms_info_keys());
+
+action(report, Node, _Args, _Opts, Inform) ->
+ io:format("Reporting server status on ~p~n~n", [erlang:universaltime()]),
+ [begin ok = action(Action, N, [], [], Inform), io:nl() end ||
+ N <- unsafe_rpc(Node, rabbit_mnesia, running_clustered_nodes, []),
+ Action <- [status, cluster_status, environment]],
+ VHosts = unsafe_rpc(Node, rabbit_vhost, list, []),
+ [print_report(Node, Q) || Q <- ?GLOBAL_QUERIES],
+ [print_report(Node, Q, [V]) || Q <- ?VHOST_QUERIES, V <- VHosts],
+ io:format("End of server status report~n"),
+ ok.
%%----------------------------------------------------------------------------
@@ -393,16 +434,18 @@ format_info_item([T | _] = Value)
format_info_item(Value) ->
io_lib:format("~w", [Value]).
-display_list(L) when is_list(L) ->
- lists:foreach(fun (I) when is_binary(I) ->
- io:format("~s~n", [escape(I)]);
- (I) when is_tuple(I) ->
- display_row([escape(V)
- || V <- tuple_to_list(I)])
- end,
- lists:sort(L)),
- ok;
-display_list(Other) -> Other.
+display_call_result(Node, MFA) ->
+ case call(Node, MFA) of
+ {badrpc, _} = Res -> throw(Res);
+ Res -> io:format("~p~n", [Res]),
+ ok
+ end.
+
+unsafe_rpc(Node, Mod, Fun, Args) ->
+ case rpc_call(Node, Mod, Fun, Args) of
+ {badrpc, _} = Res -> throw(Res);
+ Normal -> Normal
+ end.
call(Node, {Mod, Fun, Args}) ->
rpc_call(Node, Mod, Fun, lists:map(fun list_to_binary/1, Args)).
diff --git a/src/rabbit_event.erl b/src/rabbit_event.erl
index 9ed532db..468f9293 100644
--- a/src/rabbit_event.erl
+++ b/src/rabbit_event.erl
@@ -26,7 +26,7 @@
%%----------------------------------------------------------------------------
--record(state, {level, timer}).
+-record(state, {level, interval, timer}).
%%----------------------------------------------------------------------------
@@ -49,6 +49,7 @@
-opaque(state() :: #state {
level :: level(),
+ interval :: integer(),
timer :: atom()
}).
@@ -95,13 +96,14 @@ start_link() ->
init_stats_timer() ->
{ok, StatsLevel} = application:get_env(rabbit, collect_statistics),
- #state{level = StatsLevel, timer = undefined}.
+ {ok, Interval} = application:get_env(rabbit, collect_statistics_interval),
+ #state{level = StatsLevel, interval = Interval, timer = undefined}.
ensure_stats_timer(State = #state{level = none}, _Fun) ->
State;
-ensure_stats_timer(State = #state{timer = undefined}, Fun) ->
- {ok, TRef} = timer:apply_after(?STATS_INTERVAL,
- erlang, apply, [Fun, []]),
+ensure_stats_timer(State = #state{interval = Interval,
+ timer = undefined}, Fun) ->
+ {ok, TRef} = timer:apply_after(Interval, erlang, apply, [Fun, []]),
State#state{timer = TRef};
ensure_stats_timer(State, _Fun) ->
State.
diff --git a/src/rabbit_exchange.erl b/src/rabbit_exchange.erl
index 84a44cd2..cab1b99f 100644
--- a/src/rabbit_exchange.erl
+++ b/src/rabbit_exchange.erl
@@ -24,7 +24,7 @@
info_keys/0, info/1, info/2, info_all/1, info_all/2,
publish/2, delete/2]).
%% these must be run inside a mnesia tx
--export([maybe_auto_delete/1, serial/1]).
+-export([maybe_auto_delete/1, serial/1, peek_serial/1]).
%%----------------------------------------------------------------------------
@@ -75,7 +75,8 @@
-spec(maybe_auto_delete/1::
(rabbit_types:exchange())
-> 'not_deleted' | {'deleted', rabbit_binding:deletions()}).
--spec(serial/1:: (rabbit_types:exchange()) -> 'none' | pos_integer()).
+-spec(serial/1 :: (rabbit_types:exchange()) -> 'none' | pos_integer()).
+-spec(peek_serial/1 :: (name()) -> pos_integer() | 'undefined').
-endif.
@@ -93,7 +94,7 @@ recover() ->
true -> store(X);
false -> ok
end,
- rabbit_exchange:callback(X, create, [Tx, X])
+ rabbit_exchange:callback(X, create, [map_create_tx(Tx), X])
end,
rabbit_durable_exchange),
[XName || #exchange{name = XName} <- Xs].
@@ -127,10 +128,7 @@ declare(XName, Type, Durable, AutoDelete, Internal, Args) ->
end
end,
fun ({new, Exchange}, Tx) ->
- ok = XT:create(case Tx of
- true -> transaction;
- false -> none
- end, Exchange),
+ ok = XT:create(map_create_tx(Tx), Exchange),
rabbit_event:notify_if(not Tx, exchange_created, info(Exchange)),
Exchange;
({existing, Exchange}, _Tx) ->
@@ -139,6 +137,9 @@ declare(XName, Type, Durable, AutoDelete, Internal, Args) ->
Err
end).
+map_create_tx(true) -> transaction;
+map_create_tx(false) -> none.
+
store(X = #exchange{name = Name, type = Type}) ->
ok = mnesia:write(rabbit_exchange, X, write),
case (type_to_module(Type)):serialise_events() of
@@ -330,6 +331,12 @@ next_serial(XName) ->
#exchange_serial{name = XName, next = Serial + 1}, write),
Serial.
+peek_serial(XName) ->
+ case mnesia:read({rabbit_exchange_serial, XName}) of
+ [#exchange_serial{next = Serial}] -> Serial;
+ _ -> undefined
+ end.
+
%% Used with atoms from records; e.g., the type is expected to exist.
type_to_module(T) ->
{ok, Module} = rabbit_registry:lookup_module(exchange, T),
diff --git a/src/rabbit_router.erl b/src/rabbit_router.erl
index 4f683564..b1d940d2 100644
--- a/src/rabbit_router.erl
+++ b/src/rabbit_router.erl
@@ -84,21 +84,18 @@ match_bindings(SrcName, Match) ->
mnesia:async_dirty(fun qlc:e/1, [Query]).
match_routing_key(SrcName, [RoutingKey]) ->
- MatchHead = #route{binding = #binding{source = SrcName,
+ find_routes(#route{binding = #binding{source = SrcName,
destination = '$1',
key = RoutingKey,
_ = '_'}},
- mnesia:dirty_select(rabbit_route, [{MatchHead, [], ['$1']}]);
+ []);
match_routing_key(SrcName, [_|_] = RoutingKeys) ->
- Condition = list_to_tuple(['orelse' | [{'=:=', '$2', RKey} ||
- RKey <- RoutingKeys]]),
- MatchHead = #route{binding = #binding{source = SrcName,
+ find_routes(#route{binding = #binding{source = SrcName,
destination = '$1',
key = '$2',
_ = '_'}},
- mnesia:dirty_select(rabbit_route, [{MatchHead, [Condition], ['$1']}]).
-
-
+ [list_to_tuple(['orelse' | [{'=:=', '$2', RKey} ||
+ RKey <- RoutingKeys]])]).
%%--------------------------------------------------------------------
@@ -119,3 +116,25 @@ lookup_qpids(QNames) ->
QPids
end
end, [], QNames).
+
+%% Normally we'd call mnesia:dirty_select/2 here, but that is quite
+%% expensive due to
+%%
+%% 1) general mnesia overheads (figuring out table types and
+%% locations, etc). We get away with bypassing these because we know
+%% that the table
+%% - is not the schema table
+%% - has a local ram copy
+%% - does not have any indices
+%%
+%% 2) 'fixing' of the table with ets:safe_fixtable/2, which is wholly
+%% unnecessary. According to the ets docs (and the code in erl_db.c),
+%% 'select' is safe anyway ("Functions that internally traverse over a
+%% table, like select and match, will give the same guarantee as
+%% safe_fixtable.") and, furthermore, even the lower level iterators
+%% ('first' and 'next') are safe on ordered_set tables ("Note that for
+%% tables of the ordered_set type, safe_fixtable/2 is not necessary as
+%% calls to first/1 and next/2 will always succeed."), which
+%% rabbit_route is.
+find_routes(MatchHead, Conditions) ->
+ ets:select(rabbit_route, [{MatchHead, Conditions, ['$1']}]).
diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl
index 3f4aa54e..f5492cdc 100644
--- a/src/rabbit_tests.erl
+++ b/src/rabbit_tests.erl
@@ -1072,15 +1072,25 @@ test_user_management() ->
control_action(list_permissions, [], [{"-p", "/testhost"}]),
{error, {invalid_regexp, _, _}} =
control_action(set_permissions, ["guest", "+foo", ".*", ".*"]),
+ {error, {no_such_user, _}} =
+ control_action(set_user_tags, ["foo", "bar"]),
%% user creation
ok = control_action(add_user, ["foo", "bar"]),
{error, {user_already_exists, _}} =
control_action(add_user, ["foo", "bar"]),
ok = control_action(change_password, ["foo", "baz"]),
- ok = control_action(set_admin, ["foo"]),
- ok = control_action(clear_admin, ["foo"]),
- ok = control_action(list_users, []),
+
+ TestTags = fun (Tags) ->
+ Args = ["foo" | [atom_to_list(T) || T <- Tags]],
+ ok = control_action(set_user_tags, Args),
+ {ok, #internal_user{tags = Tags}} =
+ rabbit_auth_backend_internal:lookup_user(<<"foo">>),
+ ok = control_action(list_users, [])
+ end,
+ TestTags([foo, bar, baz]),
+ TestTags([administrator]),
+ TestTags([]),
%% vhost creation
ok = control_action(add_vhost, ["/testhost"]),
@@ -1203,10 +1213,10 @@ test_spawn() ->
user(Username) ->
#user{username = Username,
- is_admin = true,
+ tags = [administrator],
auth_backend = rabbit_auth_backend_internal,
impl = #internal_user{username = Username,
- is_admin = true}}.
+ tags = [administrator]}}.
test_statistics_event_receiver(Pid) ->
receive
diff --git a/src/rabbit_types.erl b/src/rabbit_types.erl
index aa174e96..22204100 100644
--- a/src/rabbit_types.erl
+++ b/src/rabbit_types.erl
@@ -140,14 +140,14 @@
-type(user() ::
#user{username :: username(),
- is_admin :: boolean(),
+ tags :: [atom()],
auth_backend :: atom(),
impl :: any()}).
-type(internal_user() ::
#internal_user{username :: username(),
password_hash :: password_hash(),
- is_admin :: boolean()}).
+ tags :: [atom()]}).
-type(username() :: binary()).
-type(password() :: binary()).
diff --git a/src/rabbit_upgrade_functions.erl b/src/rabbit_upgrade_functions.erl
index 4b205597..b4ac3328 100644
--- a/src/rabbit_upgrade_functions.erl
+++ b/src/rabbit_upgrade_functions.erl
@@ -29,6 +29,7 @@
-rabbit_upgrade({semi_durable_route, mnesia, []}).
-rabbit_upgrade({exchange_event_serial, mnesia, []}).
-rabbit_upgrade({trace_exchanges, mnesia, []}).
+-rabbit_upgrade({user_admin_to_tags, mnesia, [user_to_internal_user]}).
-rabbit_upgrade({mirror_pids, mnesia, []}).
-rabbit_upgrade({gm, mnesia, []}).
@@ -45,6 +46,7 @@
-spec(semi_durable_route/0 :: () -> 'ok').
-spec(exchange_event_serial/0 :: () -> 'ok').
-spec(trace_exchanges/0 :: () -> 'ok').
+-spec(user_admin_to_tags/0 :: () -> 'ok').
-spec(mirror_pids/0 :: () -> 'ok').
-spec(gm/0 :: () -> 'ok').
@@ -125,6 +127,16 @@ trace_exchanges() ->
VHost <- rabbit_vhost:list()],
ok.
+user_admin_to_tags() ->
+ transform(
+ rabbit_user,
+ fun({internal_user, Username, PasswordHash, true}) ->
+ {internal_user, Username, PasswordHash, [administrator]};
+ ({internal_user, Username, PasswordHash, false}) ->
+ {internal_user, Username, PasswordHash, [management]}
+ end,
+ [username, password_hash, tags], internal_user).
+
mirror_pids() ->
Tables = [rabbit_queue, rabbit_durable_queue],
AddMirrorPidsFun =
diff --git a/src/rabbit_vhost.erl b/src/rabbit_vhost.erl
index 5270d80b..08d6c99a 100644
--- a/src/rabbit_vhost.erl
+++ b/src/rabbit_vhost.erl
@@ -91,9 +91,9 @@ delete(VHostPath) ->
internal_delete(VHostPath) ->
lists:foreach(
- fun ({Username, _, _, _}) ->
- ok = rabbit_auth_backend_internal:clear_permissions(Username,
- VHostPath)
+ fun (Info) ->
+ ok = rabbit_auth_backend_internal:clear_permissions(
+ proplists:get_value(user, Info), VHostPath)
end,
rabbit_auth_backend_internal:list_vhost_permissions(VHostPath)),
ok = mnesia:delete({rabbit_vhost, VHostPath}),