Merged bug23358 into default

This commit is contained in:
Simon MacMullen 2010-12-20 13:02:54 +00:00
commit 4d48777127
19 changed files with 384 additions and 221 deletions

View File

@ -4,7 +4,7 @@ DEPS=rabbitmq-mochiweb rabbitmq-server rabbitmq-erlang-client rabbitmq-managemen
INTERNAL_DEPS=webmachine
RUNTIME_DEPS=webmachine
TEST_APPS=crypto inets mochiweb rabbit_mochiweb webmachine rabbit_management_agent rabbit_management amqp_client
TEST_APPS=crypto inets mochiweb rabbit_mochiweb webmachine rabbit_management_agent amqp_client rabbit_management
TEST_ARGS=-rabbit_mochiweb port 55672
START_RABBIT_IN_TESTS=true
TEST_COMMANDS=rabbit_mgmt_test_all:all_tests()

View File

@ -6,4 +6,4 @@
{mod, {rabbit_mgmt_app, []}},
{env, [{http_log_dir, none}]},
{applications, [kernel, stdlib, rabbit, rabbit_mochiweb, webmachine,
rabbit_management_agent]}]}.
amqp_client, rabbit_management_agent]}]}.

View File

@ -90,7 +90,7 @@ table.form { margin-bottom: 0.5em; }
table.form th { text-align: right; vertical-align: top; }
table.form input[type=text] { width: 200px; }
table.form select { width: 200px; }
table.form select.narrow { width: 80px; }
table.form select.narrow { width: 110px; }
table.form .multifield { margin: 0; padding: 0; }
table.form .multifield p { margin: 0; padding: 0; }
@ -110,6 +110,8 @@ table.bindings { margin-bottom: 2em; }
td.binding-endpoint span.object { border: 2px solid #bbb; padding: 10px; border-radius: 10px; -moz-border-radius: 10px; }
td.binding-endpoint span.arrow { font-size: 200%; }
#no-password { display: none; }
#scratch { display: none; }
tr.alt1 td {

View File

@ -352,6 +352,15 @@ function postprocess() {
$('.multifield input').live('blur', function() {
update_multifields();
});
$('#has-password').change(function() {
if ($(this).val() == 'true') {
$('#password').slideDown(100);
$('#no-password').slideUp(100);
} else {
$('#password').slideUp(100);
$('#no-password').slideDown(100);
}
});
if (! user_administrator) {
$('.administrator-only').remove();
}
@ -497,7 +506,7 @@ function sync_post(sammy, path_template) {
}
function sync_req(type, params0, path_template) {
var params = collapse_multifields(params0);
var params = params_magic(params0);
var path;
try {
path = fill_path_template(path_template, params);
@ -556,6 +565,11 @@ function fill_path_template(template, params) {
// Better suggestions appreciated
var INTEGER_ARGUMENTS = map(['x-expires']);
function params_magic(params) {
return maybe_remove_password(
collapse_multifields(params));
}
function collapse_multifields(params0) {
var params = {};
for (key in params0) {
@ -586,6 +600,14 @@ function collapse_multifields(params0) {
return params;
}
function maybe_remove_password(params) {
if (params['has-password'] == 'false') {
delete params['password'];
}
return params;
}
function debug(str) {
$('<p>' + str + '</p>').appendTo('#debug');
}

View File

@ -59,6 +59,13 @@
</div>
</div>
<div class="section">
<h2>Consumers</h2>
<div class="hider updatable">
<%= format('consumers', {'mode': 'channel', 'consumers': channel.consumer_details}) %>
</div>
</div>
<% if (statistics_level == 'fine') { %>
<div class="section">
<h2>Message Rates</h2>

View File

@ -48,6 +48,10 @@
<th>SSL</th>
<td><%= fmt_boolean(connection.ssl) %></td>
</tr>
<tr>
<th>Authentication</th>
<td><%= connection.auth_mechanism %></td>
</tr>
</table>
<table class="facts">

View File

@ -0,0 +1,35 @@
<% if (consumers.length > 0) { %>
<table class="list">
<thead>
<tr>
<% if (mode == 'queue') { %>
<th>Channel</th>
<th>Consumer Tag</th>
<% } else { %>
<th>Consumer Tag</th>
<th>Queue</th>
<% } %>
<th>Ack Required</th>
<th>Exclusive</th>
</tr>
</thead>
<%
for (var i = 0; i < consumers.length; i++) {
var consumer = consumers[i];
%>
<tr<%= alt_rows(i) %>>
<% if (mode == 'queue') { %>
<td><%= link_channel(consumer.channel_details.name) %></td>
<td><%= consumer.consumer_tag %></td>
<% } else { %>
<td><%= consumer.consumer_tag %></td>
<td><%= link_queue(consumer.queue_details.vhost, consumer.queue_details.name) %></td>
<% } %>
<td class="c"><%= fmt_boolean(consumer.ack_required) %></td>
<td class="c"><%= fmt_boolean(consumer.exclusive) %></td>
</tr>
<% } %>
</table>
<% } else { %>
<p>... no consumers ...</p>
<% } %>

View File

@ -88,7 +88,7 @@
</div>
<div class="section">
<div class="section-hidden">
<h2>Applications</h2>
<div class="hider updatable">
<% if (node.running) { %>
@ -115,3 +115,13 @@
<% } %>
</div>
</div>
<div class="section-hidden">
<h2>Registry</h2>
<div class="hider updatable">
<h3>Exchange Types</h3>
<%= format('registry', {'list': node.exchange_types, 'node': node, 'show_enabled': false} ) %>
<h3>Authentication Mechanisms</h3>
<%= format('registry', {'list': node.auth_mechanisms, 'node': node, 'show_enabled': true} ) %>
</div>
</div>

View File

@ -69,6 +69,13 @@
</div>
</div>
<div class="section-hidden">
<h2>Consumers</h2>
<div class="hider updatable">
<%= format('consumers', {'mode': 'queue', 'consumers': queue.consumer_details}) %>
</div>
</div>
<% if (statistics_level == 'fine') { %>
<div class="section-hidden">
<h2>Message Rates</h2>

View File

@ -0,0 +1,25 @@
<% if (node.running) { %>
<table class="list">
<tr>
<th>Name</th>
<th>Description</th>
<% if (show_enabled) { %>
<th>Enabled</th>
<% } %>
</tr>
<%
for (var i = 0; i < list.length; i++) {
var item = list[i];
%>
<tr<%= alt_rows(i)%>>
<td><%= item.name %></td>
<td><%= item.description %></td>
<% if (show_enabled) { %>
<td class="c"><%= fmt_boolean(item.enabled) %></td>
<% } %>
</tr>
<% } %>
</table>
<% } else {%>
<p>...node not running...</p>
<% } %>

View File

@ -1,6 +1,21 @@
<h1>User: <b><%= user.name %></b></h1>
<p>Administrator: <%= fmt_boolean(user.administrator) %></p>
<div class="section">
<h2>Overview</h2>
<div class="hider">
<table class="facts">
<tr>
<th>Administrator</th>
<td><%= fmt_boolean(user.administrator) %></td>
</tr>
<tr>
<th>Can log in with password</th>
<td><%= fmt_boolean(user.password_hash.length > 0) %></td>
</tr>
</table>
<span class="br"></span>
</div>
</div>
<%= format('permissions', {'mode': 'user', 'permissions': permissions, 'vhosts': vhosts, 'parent': user}) %>

View File

@ -31,8 +31,22 @@
<td><input type="text" name="username"/></td>
</tr>
<tr>
<th><label>Password:</label></th>
<td><input type="text" name="password"/></td>
<th>
<label>
<select name="has-password" id="has-password" class="narrow">
<option value="true">Password:</option>
<option value="false">No password</option>
</select>
</label>
</th>
<td>
<div id="password">
<input type="text" name="password" />
</div>
<div id="no-password">
User cannot log in using password.
</div>
</td>
</tr>
<tr>
<th><label>Administrator:</label></th>

View File

@ -33,7 +33,7 @@
-record(state, {tables}).
-define(FINE_STATS_TYPES, [channel_queue_stats, channel_exchange_stats,
channel_queue_exchange_stats]).
-define(TABLES, [queue_stats, connection_stats, channel_stats] ++
-define(TABLES, [queue_stats, connection_stats, channel_stats, consumers] ++
?FINE_STATS_TYPES).
-define(DELIVER_GET, [deliver, deliver_no_ack, get, get_no_ack]).
@ -91,56 +91,32 @@ start_link() ->
Else
end.
get_queues(Qs) ->
safe_call({get_queues, Qs, list}, Qs).
get_queues(Qs) -> safe_call({get_queues, Qs, list}, Qs).
get_queue(Q) -> safe_call({get_queues, [Q], detail}, [Q]).
get_exchanges(Xs) -> safe_call({get_exchanges, Xs, list}, Xs).
get_exchange(X) -> safe_call({get_exchanges, [X], detail}, [X]).
get_connections() -> safe_call(get_connections).
get_connection(Name) -> safe_call({get_connection, Name}).
get_channels() -> safe_call(get_channels).
get_channel(Name) -> safe_call({get_channel, Name}).
get_overview(Username) -> safe_call({get_overview, Username}).
get_overview() -> safe_call({get_overview, all}).
get_queue(Q) ->
safe_call({get_queues, [Q], detail}, [Q]).
get_exchanges(Xs) ->
safe_call({get_exchanges, Xs, list}, Xs).
get_exchange(X) ->
safe_call({get_exchanges, [X], detail}, [X]).
get_connections() ->
safe_call(get_connections).
get_connection(Name) ->
safe_call({get_connection, Name}).
get_channels() ->
safe_call(get_channels).
get_channel(Name) ->
safe_call({get_channel, Name}).
get_overview(Username) ->
safe_call({get_overview, Username}).
get_overview() ->
safe_call({get_overview, all}).
safe_call(Term) ->
safe_call(Term, []).
safe_call(Term) -> safe_call(Term, []).
safe_call(Term, Item) ->
try
gen_server:call({global, ?MODULE}, Term, infinity)
catch exit:{noproc, _} ->
Item
catch exit:{noproc, _} -> Item
end.
%%----------------------------------------------------------------------------
pget(Key, List) ->
pget(Key, List, unknown).
pget(Key, List) -> pget(Key, List, unknown).
pget(Key, List, Default) ->
proplists:get_value(Key, List, Default).
pget(Key, List, Default) -> proplists:get_value(Key, List, Default).
pset(Key, Value, List) ->
[{Key, Value} | proplists:delete(Key, List)].
pset(Key, Value, List) -> [{Key, Value} | proplists:delete(Key, List)].
id(Pid) when is_pid(Pid) -> rabbit_mgmt_format:pid(Pid);
id(List) -> rabbit_mgmt_format:pid(pget(pid, List)).
@ -149,8 +125,7 @@ add(unknown, _) -> unknown;
add(_, unknown) -> unknown;
add(A, B) -> A + B.
lookup_element(Table, Key) ->
lookup_element(Table, Key, 2).
lookup_element(Table, Key) -> lookup_element(Table, Key, 2).
lookup_element(Table, Key, Pos) ->
try ets:lookup_element(Table, Key, Pos)
@ -223,101 +198,45 @@ if_unknown(Val, _Def) -> Val.
%%----------------------------------------------------------------------------
augment(Items, Funs, Tables) ->
Augmented = [augment(K, Items, Fun, Tables) || {K, Fun} <- Funs] ++ Items,
[{K, V} || {K, V} <- Augmented, V =/= unknown].
augment(K, Items, Fun, Tables) ->
Key = details_key(K),
case pget(K, Items) of
none -> {Key, unknown};
unknown -> {Key, unknown};
Id -> {Key, Fun(Id, Tables)}
end.
augment_channel_pid(Pid, Tables) ->
Ch = lookup_element(
orddict:fetch(channel_stats, Tables),
{Pid, create}),
Conn = lookup_element(
orddict:fetch(connection_stats, Tables),
{pget(connection, Ch), create}),
[{number, pget(number, Ch)},
{name, pget(name, Ch)},
{connection_name, pget(name, Conn)},
{peer_address, pget(peer_address, Conn)},
{peer_port, pget(peer_port, Conn)}].
augment_connection_pid(Pid, Tables) ->
Conn = lookup_element(orddict:fetch(connection_stats, Tables),
{Pid, create}),
[{peer_address, pget(peer_address, Conn)},
{peer_port, pget(peer_port, Conn)},
{name, pget(name, Conn)}].
augment_queue_pid(Pid, _Tables) ->
%% TODO This should be in rabbit_amqqueue?
[Q] = mnesia:dirty_match_object(
rabbit_queue,
#amqqueue{pid = rabbit_misc:string_to_pid(Pid), _ = '_'}),
Name = Q#amqqueue.name,
[{name, Name#resource.name},
{vhost, Name#resource.virtual_host}].
augment_msg_stats(Stats, Tables) ->
[augment_msg_stats_items(Props, Tables) || Props <- Stats].
augment_msg_stats_items(Props, Tables) ->
augment(Props, [{connection, fun augment_connection_pid/2},
{channel, fun augment_channel_pid/2},
{queue, fun augment_queue_pid/2}], Tables).
%%----------------------------------------------------------------------------
init([]) ->
{ok, #state{tables = orddict:from_list(
[{Key, ets:new(anon, [private])} ||
Key <- ?TABLES])}}.
handle_call({get_queues, Qs0, Mode}, _From, State = #state{tables = Tables}) ->
FineStats = case Mode of
FineSpecs = case Mode of
list -> ?FINE_STATS_QUEUE_LIST;
detail -> ?FINE_STATS_QUEUE_DETAIL
end,
Qs1 = merge_stats(Qs0, FineStats, queue_stats, Tables),
Qs1 = queue_stats(Qs0, FineSpecs, Tables),
Qs2 = [[{messages, add(pget(messages_ready, Q),
pget(messages_unacknowledged, Q))} | Q] || Q <- Qs1],
Qs3 = [augment(Q, [{owner_pid, fun augment_connection_pid/2}], Tables) ||
Q <- Qs2],
{reply, Qs3, State};
{reply, Qs2, State};
handle_call({get_exchanges, Xs, Mode}, _From,
State = #state{tables = Tables}) ->
FineStats = case Mode of
FineSpecs = case Mode of
list -> ?FINE_STATS_EXCHANGE_LIST;
detail -> ?FINE_STATS_EXCHANGE_DETAIL
end,
{reply, merge_stats(Xs, FineStats, none, Tables),
State};
{reply, exchange_stats(Xs, FineSpecs, Tables), State};
handle_call(get_connections, _From, State = #state{tables = Tables}) ->
Conns = created_events(connection_stats, Tables),
{reply, merge_stats(Conns, ?FINE_STATS_NONE, connection_stats, Tables),
State};
{reply, connection_stats(Conns, Tables), State};
handle_call({get_connection, Name}, _From, State = #state{tables = Tables}) ->
Conns = created_event(Name, connection_stats, Tables),
[Res] = merge_stats(Conns, ?FINE_STATS_NONE, connection_stats, Tables),
[Res] = connection_stats(Conns, Tables),
{reply, result_or_error(Res), State};
handle_call(get_channels, _From, State = #state{tables = Tables}) ->
Chs = created_events(channel_stats, Tables),
Res = merge_stats(Chs, ?FINE_STATS_CHANNEL_LIST, channel_stats, Tables),
{reply, Res, State};
{reply, channel_stats(Chs, ?FINE_STATS_CHANNEL_LIST, Tables), State};
handle_call({get_channel, Name}, _From, State = #state{tables = Tables}) ->
Chs = created_event(Name, channel_stats, Tables),
[Res] = merge_stats(Chs, ?FINE_STATS_CHANNEL_DETAIL, channel_stats, Tables),
[Res] = channel_stats(Chs, ?FINE_STATS_CHANNEL_DETAIL, Tables),
{reply, result_or_error(Res), State};
handle_call({get_overview, Username}, _From, State = #state{tables = Tables}) ->
@ -325,10 +244,9 @@ handle_call({get_overview, Username}, _From, State = #state{tables = Tables}) ->
all -> rabbit_access_control:list_vhosts();
_ -> rabbit_mgmt_util:vhosts(Username)
end,
Qs0 = lists:append(
[[rabbit_mgmt_format:queue(Q) || Q <- rabbit_amqqueue:list(V)]
|| V <- VHosts]),
Qs1 = merge_stats(Qs0, ?FINE_STATS_NONE, queue_stats, Tables),
Qs0 = [rabbit_mgmt_format:queue(Q) || V <- VHosts,
Q <- rabbit_amqqueue:list(V)],
Qs1 = basic_queue_stats(Qs0, Tables),
Totals0 = sum(Qs1, [messages_ready, messages_unacknowledged]),
Totals = [{messages, add(pget(messages_ready, Totals0),
pget(messages_unacknowledged, Totals0))}|Totals0],
@ -339,13 +257,13 @@ handle_call({get_overview, Username}, _From, State = #state{tables = Tables}) ->
get_fine_stats(
[], [R || R = {Id, _, _}
<- ets:tab2list(orddict:fetch(Type, Tables)),
Filter(augment_msg_stats_items(
format_id(Id), Tables), Name)])
Filter(augment_msg_stats(format_id(Id), Tables),
Name)])
end,
Publish = F(channel_exchange_stats, exchange),
Consume = F(channel_queue_stats, queue_details),
{reply, [{message_stats, Publish ++ Consume}, {queue_totals, Totals}],
State};
{reply, [{message_stats, Publish ++ Consume},
{queue_totals, Totals}], State};
handle_call(_Request, _From, State) ->
{reply, not_understood, State}.
@ -428,6 +346,14 @@ handle_event(Event = #event{type = channel_closed,
Type <- ?FINE_STATS_TYPES],
{ok, State};
handle_event(#event{type = consumer_created, props = Props}, State) ->
handle_consumer(fun(Table, Id, P) -> ets:insert(Table, {Id, P}) end,
Props, State);
handle_event(#event{type = consumer_deleted, props = Props}, State) ->
handle_consumer(fun(Table, Id, _P) -> ets:delete(Table, Id) end,
Props, State);
handle_event(_Event, State) ->
{ok, State}.
@ -461,6 +387,14 @@ handle_deleted(TName, #event{props = [{pid, Pid}]},
ets:delete(Table, {id(Pid), stats}),
{ok, State}.
handle_consumer(Fun, Props,
State = #state{tables = Tables}) ->
P = rabbit_mgmt_format:format(
Props, [{fun rabbit_mgmt_format:pid/1, [queue, channel]}]),
Table = orddict:fetch(consumers, Tables),
Fun(Table, {pget(queue, P), pget(channel, P)}, P),
{ok, State}.
handle_fine_stats(Type, Props, Timestamp, State = #state{tables = Tables}) ->
case pget(Type, Props) of
unknown ->
@ -530,31 +464,30 @@ format_id({ChPid, QPid, #resource{name=XName, virtual_host=XVhost}}) ->
[{channel, ChPid}, {queue, QPid},
{exchange, [{name, XName}, {vhost, XVhost}]}].
merge_stats(Objs, FineSpecs, Type, Tables) ->
WithCoarse =
case Type of
none ->
Objs;
_ ->
Table = orddict:fetch(Type, Tables),
[Obj ++ zero_old_rates(
lookup_element(Table, {pget(pid, Obj), stats}))
|| Obj <- Objs]
end,
%%----------------------------------------------------------------------------
merge_stats(Objs, Funs) ->
[lists:foldl(fun (Fun, Props) -> Fun(Props) ++ Props end, Obj, Funs)
|| Obj <- Objs].
basic_stats_fun(Type, Tables) ->
Table = orddict:fetch(Type, Tables),
fun (Props) ->
zero_old_rates(lookup_element(Table, {pget(pid, Props), stats}))
end.
fine_stats_fun(FineSpecs, Tables) ->
FineStats = [{AttachName, AttachBy,
get_fine_stats(FineStatsType, GroupBy, Tables)}
|| {FineStatsType, GroupBy, AttachName, AttachBy}
<- FineSpecs],
augment_msg_stats(merge_fine_stats(WithCoarse, FineStats, Tables), Tables).
fun (Props) ->
lists:foldl(fun (FineStat, StatProps) ->
fine_stat(Props, StatProps, FineStat, Tables)
end, [], FineStats)
end.
merge_fine_stats(Stats, [], _Tables) ->
Stats;
merge_fine_stats(Stats, [{AttachName, AttachBy, Dict} | Rest], Tables) ->
merge_fine_stats([merge_fine_stats0(AttachName, AttachBy,
Props, Dict, Tables)
|| Props <- Stats], Rest, Tables).
merge_fine_stats0(AttachName, AttachBy, Props, Dict, Tables) ->
fine_stat(Props, StatProps, {AttachName, AttachBy, Dict}, Tables) ->
Id = case AttachBy of
exchange ->
[{name, pget(name, Props)}, {vhost, pget(vhost, Props)}];
@ -562,19 +495,29 @@ merge_fine_stats0(AttachName, AttachBy, Props, Dict, Tables) ->
pget(pid, Props)
end,
case dict:find({AttachBy, Id}, Dict) of
{ok, Stats} -> [{AttachName, pget(AttachName, Props, []) ++
{ok, Stats} -> [{AttachName, pget(AttachName, StatProps, []) ++
augment_fine_stats(Stats, Tables)} |
proplists:delete(AttachName, Props)];
error -> Props
proplists:delete(AttachName, StatProps)];
error -> StatProps
end.
augment_fine_stats(Dict, Tables) when element(1, Dict) == dict ->
[[{stats, augment_fine_stats(Stats, Tables)} |
augment_msg_stats_items([IdTuple], Tables)]
augment_msg_stats([IdTuple], Tables)]
|| {IdTuple, Stats} <- dict:to_list(Dict)];
augment_fine_stats(Stats, _Tables) ->
Stats.
consumer_details_fun(PatternFun, Tables) ->
Table = orddict:fetch(consumers, Tables),
fun ([]) -> [];
(Props) -> Pattern = PatternFun(Props),
[{consumer_details,
[augment_msg_stats(Obj, Tables)
|| Obj <- lists:append(
ets:match(Table, {Pattern, '$1'}))]}]
end.
zero_old_rates(Stats) -> [maybe_zero_rate(S) || S <- Stats].
maybe_zero_rate({Key, Val}) ->
@ -587,8 +530,87 @@ maybe_zero_rate({Key, Val}) ->
false -> {Key, Val}
end.
is_details(Key) ->
lists:suffix("_details", atom_to_list(Key)).
is_details(Key) -> lists:suffix("_details", atom_to_list(Key)).
details_key(Key) ->
list_to_atom(atom_to_list(Key) ++ "_details").
details_key(Key) -> list_to_atom(atom_to_list(Key) ++ "_details").
%%----------------------------------------------------------------------------
augment_msg_stats(Props, Tables) ->
(augment_msg_stats_fun(Tables))(Props) ++ Props.
augment_msg_stats_fun(Tables) ->
Funs = [{connection, fun augment_connection_pid/2},
{channel, fun augment_channel_pid/2},
{queue, fun augment_queue_pid/2},
{owner_pid, fun augment_connection_pid/2}],
fun (Props) -> augment(Props, Funs, Tables) end.
augment(Items, Funs, Tables) ->
Augmented = [augment(K, Items, Fun, Tables) || {K, Fun} <- Funs],
[{K, V} || {K, V} <- Augmented, V =/= unknown].
augment(K, Items, Fun, Tables) ->
Key = details_key(K),
case pget(K, Items) of
none -> {Key, unknown};
unknown -> {Key, unknown};
Id -> {Key, Fun(Id, Tables)}
end.
augment_channel_pid(Pid, Tables) ->
Ch = lookup_element(orddict:fetch(channel_stats, Tables),
{Pid, create}),
Conn = lookup_element(orddict:fetch(connection_stats, Tables),
{pget(connection, Ch), create}),
[{name, pget(name, Ch)},
{number, pget(number, Ch)},
{connection_name, pget(name, Conn)},
{peer_address, pget(peer_address, Conn)},
{peer_port, pget(peer_port, Conn)}].
augment_connection_pid(Pid, Tables) ->
Conn = lookup_element(orddict:fetch(connection_stats, Tables),
{Pid, create}),
[{name, pget(name, Conn)},
{peer_address, pget(peer_address, Conn)},
{peer_port, pget(peer_port, Conn)}].
augment_queue_pid(Pid, _Tables) ->
%% TODO This should be in rabbit_amqqueue?
case mnesia:dirty_match_object(
rabbit_queue,
#amqqueue{pid = rabbit_misc:string_to_pid(Pid), _ = '_'}) of
[Q] -> Name = Q#amqqueue.name,
[{name, Name#resource.name},
{vhost, Name#resource.virtual_host}];
[] -> [] %% Queue went away before we could get its details.
end.
%%----------------------------------------------------------------------------
basic_queue_stats(Objs, Tables) ->
merge_stats(Objs, [basic_stats_fun(queue_stats, Tables),
augment_msg_stats_fun(Tables)]).
queue_stats(Objs, FineSpecs, Tables) ->
merge_stats(Objs, [basic_stats_fun(queue_stats, Tables),
consumer_details_fun(
fun (Props) -> {pget(pid, Props), '_'} end, Tables),
fine_stats_fun(FineSpecs, Tables),
augment_msg_stats_fun(Tables)]).
exchange_stats(Objs, FineSpecs, Tables) ->
merge_stats(Objs, [fine_stats_fun(FineSpecs, Tables),
augment_msg_stats_fun(Tables)]).
connection_stats(Objs, Tables) ->
merge_stats(Objs, [basic_stats_fun(connection_stats, Tables),
augment_msg_stats_fun(Tables)]).
channel_stats(Objs, FineSpecs, Tables) ->
merge_stats(Objs, [basic_stats_fun(channel_stats, Tables),
consumer_details_fun(
fun (Props) -> {'_', pget(pid, Props)} end, Tables),
fine_stats_fun(FineSpecs, Tables),
augment_msg_stats_fun(Tables)]).

View File

@ -19,7 +19,7 @@
-export([is_authorized/2, is_authorized_admin/2, vhost/1]).
-export([is_authorized_vhost/2, is_authorized/3, is_authorized_user/3]).
-export([bad_request/3, id/2, parse_bool/1]).
-export([with_decode/4, not_found/3, amqp_request/4]).
-export([with_decode/4, with_decode_opts/4, not_found/3, amqp_request/4]).
-export([all_or_one_vhost/2, with_decode_vhost/4, reply/3, filter_vhost/3]).
-export([filter_user/3, with_decode/5, redirect/2, args/1, vhosts/1]).
-export([reply_list/3, reply_list/4, sort_list/4, destination_type/1]).
@ -72,7 +72,7 @@ is_authorized(ReqData, Context, Fun) ->
is_admin = IsAdmin}};
false -> Unauthorized
end;
refused ->
{refused, _, _} ->
Unauthorized
end;
_ ->
@ -176,6 +176,21 @@ id0(Key, ReqData) ->
error -> none
end.
%% TODO unify this with the function below after bug23384 is merged
with_decode_opts(Keys, ReqData, Context, Fun) ->
Body = wrq:req_body(ReqData),
case decode(Keys, Body) of
{error, Reason} -> bad_request(Reason, ReqData, Context);
_Values -> try
{ok, Obj0} = decode(Body),
Obj = [{list_to_atom(binary_to_list(K)), V} ||
{K, V} <- Obj0],
Fun(Obj)
catch {error, Error} ->
bad_request(Error, ReqData, Context)
end
end.
with_decode(Keys, ReqData, Context, Fun) ->
with_decode(Keys, wrq:req_body(ReqData), ReqData, Context, Fun).
@ -190,20 +205,22 @@ with_decode(Keys, Body, ReqData, Context, Fun) ->
end.
decode(Keys, Body) ->
{Res, Json} = try
{struct, J} = mochijson2:decode(Body),
{ok, J}
catch error:_ -> {error, not_json}
end,
case Res of
ok -> Results =
[get_or_missing(list_to_binary(atom_to_list(K)), Json) ||
K <- Keys],
case [E || E = {key_missing, _} <- Results] of
[] -> Results;
Errors -> {error, Errors}
end;
_ -> {Res, Json}
case decode(Body) of
{ok, J} -> Results =
[get_or_missing(list_to_binary(atom_to_list(K)), J) ||
K <- Keys],
case [E || E = {key_missing, _} <- Results] of
[] -> Results;
Errors -> {error, Errors}
end;
Else -> Else
end.
decode(Body) ->
try
{struct, J} = mochijson2:decode(Body),
{ok, J}
catch error:_ -> {error, not_json}
end.
with_decode_vhost(Keys, ReqData, Context, Fun) ->

View File

@ -179,18 +179,7 @@ clean_value(A) -> A.
%%--------------------------------------------------------------------
add_user(User) ->
case proplists:is_defined(password_hash, User) of
true ->
%% ver > 2.1.1
rabbit_mgmt_wm_user:put_user_hashed(pget(name, User),
pget(password_hash, User),
pget(administrator, User));
false ->
%% ver == 2.1.1
rabbit_mgmt_wm_user:put_user(pget(name, User),
pget(password, User),
pget(administrator, User))
end.
rabbit_mgmt_wm_user:put_user(User).
add_vhost(VHost) ->
VHostName = pget(name, VHost),

View File

@ -54,16 +54,6 @@ all_nodes() ->
make_entry(Node, Type, Running) ->
[{name, Node}, {type, Type}, {running, Running}]
++ case Running of
true -> rabbit_mgmt_external_stats:info(Node) ++
[{applications, applications(Node)}];
true -> rabbit_mgmt_external_stats:info(Node);
_ -> []
end.
applications(Node) ->
case rpc:call(Node, application, which_applications, []) of
{badrpc, _Reason} -> [];
Applications -> rabbit_mgmt_util:sort_list(
[rabbit_mgmt_format:application(A) ||
A <- Applications],
[], "name", false)
end.

View File

@ -17,7 +17,7 @@
-export([init/1, resource_exists/2, to_json/2,
content_types_provided/2, content_types_accepted/2,
is_authorized/2, allowed_methods/2, accept_content/2,
delete_resource/2, put_user/3, put_user_hashed/3]).
delete_resource/2, put_user/1]).
-include("rabbit_mgmt.hrl").
-include_lib("webmachine/include/webmachine.hrl").
@ -46,11 +46,11 @@ to_json(ReqData, Context) ->
rabbit_mgmt_util:reply(rabbit_mgmt_format:user(User), ReqData, Context).
accept_content(ReqData, Context) ->
User = rabbit_mgmt_util:id(user, ReqData),
rabbit_mgmt_util:with_decode(
[password, administrator], ReqData, Context,
fun([Password, IsAdmin]) ->
put_user(User, Password, IsAdmin),
Username = rabbit_mgmt_util:id(user, ReqData),
rabbit_mgmt_util:with_decode_opts(
[administrator], ReqData, Context,
fun(User) ->
put_user([{name, Username} | User]),
{true, ReqData, Context}
end).
@ -67,31 +67,33 @@ is_authorized(ReqData, Context) ->
user(ReqData) ->
rabbit_access_control:lookup_user(rabbit_mgmt_util:id(user, ReqData)).
put_user(User, Password, IsAdmin) ->
put_user0(
User, IsAdmin,
fun (User0) ->
rabbit_access_control:change_password(User0, Password)
end).
put_user(User) ->
case {proplists:is_defined(password, User),
proplists:is_defined(password_hash, User)} of
{true, _} ->
Pass = proplists:get_value(password, User),
put_user(User, Pass, fun rabbit_access_control:change_password/2);
{_, true} ->
Hash = base64:decode(proplists:get_value(password_hash, User)),
put_user(User, Hash,
fun rabbit_access_control:change_password_hash/2);
_ ->
put_user(User, <<>>,
fun rabbit_access_control:change_password_hash/2)
end.
put_user_hashed(User, PasswordHash64, IsAdmin) ->
PasswordHash = base64:decode(PasswordHash64),
put_user0(
User, IsAdmin,
fun (User0) ->
rabbit_access_control:change_password_hash(User0, PasswordHash)
end).
put_user0(User, IsAdmin, PWFun) ->
case rabbit_access_control:lookup_user(User) of
put_user(User, PWArg, PWFun) ->
Username = proplists:get_value(name, User),
IsAdmin = proplists:get_value(administrator, User),
case rabbit_access_control:lookup_user(Username) of
{error, not_found} ->
rabbit_access_control:add_user(
User, rabbit_guid:binstring_guid("tmp_"));
Username, rabbit_guid:binstring_guid("tmp_"));
_ ->
ok
end,
PWFun(User),
PWFun(Username, PWArg),
case rabbit_mgmt_util:parse_bool(IsAdmin) of
true -> rabbit_access_control:set_admin(User);
false -> rabbit_access_control:clear_admin(User)
true -> rabbit_access_control:set_admin(Username);
false -> rabbit_access_control:clear_admin(Username)
end.

View File

@ -209,7 +209,7 @@ test_channel_aggregation(Conn, Chan) ->
[{publish, 100}], pget(publishes, Ch))
end].
test_exchange_aggregation(Conn, Chan) ->
test_exchange_aggregation(_Conn, Chan) ->
X1 = <<"exchange-aggregation">>,
declare_exchange(Chan, X1),
Q1 = declare_queue(Chan),
@ -229,7 +229,7 @@ test_exchange_aggregation(Conn, Chan) ->
[{publish, 100}], pget(outgoing, X))
end].
test_queue_aggregation(Conn, Chan) ->
test_queue_aggregation(_Conn, Chan) ->
X1 = <<"queue-aggregation-1">>,
X2 = <<"queue-aggregation-2">>,
declare_exchange(Chan, X1),

View File

@ -72,7 +72,9 @@ users_test() ->
http_get("/users/myuser", ?NOT_FOUND),
http_put_raw("/users/myuser", "Something not JSON", ?BAD_REQUEST),
http_put("/users/myuser", [{flim, <<"flam">>}], ?BAD_REQUEST),
http_put("/users/myuser", [{password, <<"myuser">>},
http_put("/users/myuser", [{administrator, false}], ?NO_CONTENT),
http_put("/users/myuser", [{password_hash,
<<"IECV6PZI/Invh0DL187KFpkO5Jc=">>},
{administrator, false}], ?NO_CONTENT),
http_put("/users/myuser", [{password, <<"password">>},
{administrator, true}], ?NO_CONTENT),
@ -458,8 +460,8 @@ permissions_amqp_test() ->
get_conn(Username, Password) ->
{ok, Conn} = amqp_connection:start(network, #amqp_params{
username = Username,
password = Password}),
username = list_to_binary(Username),
password = list_to_binary(Password)}),
LocalPort = rabbit_mgmt_test_db:local_port(Conn),
ConnPath = binary_to_list(
rabbit_mgmt_format:print(