Merge bug24914

This commit is contained in:
Simon MacMullen 2012-09-14 16:06:08 +01:00
commit bd732b7494
17 changed files with 133 additions and 89 deletions

View File

@ -1,5 +1,5 @@
body { font: 12px Verdana,sans-serif; color: #484848; padding: 8px 35px; }
#logo { width: 204px; height: 37px; margin-bottom: 20px; background: url(../img/rabbitmqlogo.png); }
#logo { margin-bottom: 20px; }
#login-version { float: right; color: #444; }
#login-version p { padding: 0 0 0.2em 0; margin: 0; text-align: right; }
#login-version b { color: black; font-weight: normal; }
@ -27,6 +27,9 @@ b, dt { color: black; font-weight: normal; }
dd { margin-bottom: 5px; }
div.box, div.section, div.section-hidden { overflow: auto; width: 100%; }
.left { float: left; }
.right { float: right; }
.help { color: #888; cursor: pointer; }
.help:hover { color: #444; }

View File

@ -17,7 +17,11 @@
<body>
<div id="header">
<div id="login-version"></div>
<div id="logo"></div>
<div id="logo">
<a href="#/">
<img src="img/rabbitmqlogo.png" width="204" height="37"/>
</a>
</div>
<div id="menu">
<p id="vhost-form">
<label for="show-vhost">Virtual host: </label>

View File

@ -161,7 +161,7 @@ dispatcher_add(function(sammy) {
'policy', '#/policies');
});
sammy.put('#/policies', function() {
put_parameter(this, ['prefix'], []);
put_parameter(this, ['key', 'pattern', 'policy'], ['priority'], []);
return false;
});
sammy.del('#/policies', function() {

View File

@ -415,6 +415,22 @@ function fmt_shortened_uri(uri0) {
}
}
function fmt_client_name(properties) {
var res = [];
if (properties.product != undefined) {
res.push(properties.product);
}
if (properties.platform != undefined) {
res.push(properties.platform);
}
res = res.join(" / ");
if (properties.version != undefined) {
res += '<sub>' + properties.version + '</sub>';
}
return res;
}
function alt_rows(i) {
return (i % 2 == 0) ? ' class="alt1"' : ' class="alt2"';
}

View File

@ -135,7 +135,11 @@ HELP = {
User can do everything monitoring can do, manage users, \
vhosts and permissions, and close other user\'s connections. \
</dd> \
</dl>',
</dl> \
<p> \
Note that you can set any tag here; the links for the above three \
tags are just for convenience. \
</p>',
'queued-messages':
'Total messages in all queues:\
@ -177,6 +181,8 @@ HELP = {
'disk-monitoring-no-watermark' : 'There is no disk space low watermark set. RabbitMQ will not take any action to avoid running out of disk space.',
'resource-counts' : 'Shows total number of objects for all virtual hosts the current user has access to.',
'foo': 'foo' // No comma.
};

View File

@ -778,7 +778,7 @@ function maybe_remove_fields(params) {
return params;
}
function put_parameter(sammy, mandatory_keys, num_keys) {
function put_parameter(sammy, mandatory_keys, num_keys, bool_keys) {
for (var i in sammy.params) {
if (i === 'length' || !sammy.params.hasOwnProperty(i)) continue;
if (sammy.params[i] == '' && mandatory_keys.indexOf(i) == -1) {
@ -787,6 +787,9 @@ function put_parameter(sammy, mandatory_keys, num_keys) {
else if (num_keys.indexOf(i) != -1) {
sammy.params[i] = parseInt(sammy.params[i]);
}
else if (bool_keys.indexOf(i) != -1) {
sammy.params[i] = sammy.params[i] == 'true';
}
}
var params = {"component": sammy.params.component,
"vhost": sammy.params.vhost,

View File

@ -5,12 +5,13 @@
<table class="list">
<thead>
<tr>
<th colspan="<% if (nodes_interesting) { %>6<% } else { %>5<% } %>">Network</th>
<th colspan="<% if (nodes_interesting) { %>7<% } else { %>6<% } %>">Network</th>
<th colspan="<% if (vhosts_interesting) { %>5<% } else { %>4<% } %>">Overview</th>
</tr>
<tr>
<th><%= fmt_sort('Protocol', 'protocol') %></th>
<th><%= fmt_sort('Name', 'name') %></th>
<th><%= fmt_sort('Protocol', 'protocol') %></th>
<th><%= fmt_sort('Client', 'properties') %></th>
<% if (nodes_interesting) { %>
<th><%= fmt_sort('Node', 'node') %></th>
<% } %>
@ -31,13 +32,14 @@
var connection = connections[i];
%>
<tr<%= alt_rows(i)%>>
<td><%= link_conn(connection.name) %></td>
<td>
<%= connection.protocol %>
<% if (connection.ssl) { %>
<sub>SSL</sub>
<% } %>
</td>
<td><%= link_conn(connection.name) %></td>
<td><%= fmt_client_name(connection.client_properties) %></td>
<% if (nodes_interesting) { %>
<td><%= connection.node %></td>
<% } %>

View File

@ -3,6 +3,7 @@
<h2>Totals</h2>
<div class="hider updatable">
<% if (overview.statistics_db_node != 'not_running') { %>
<div class="left">
<h3>Queued messages <span class="help" id="queued-messages"></span></h3>
<div class="box">
<%= queue_length(overview.queue_totals, 'Ready', 'messages_ready') %>
@ -18,6 +19,39 @@
<% } else { %>
Totals not available
<% } %>
</div>
<div class="right">
<h3>Global counts <span class="help" id="resource-counts"></span></h3>
<table class="list">
<tr>
<th></th>
<th>Count</th>
</tr>
<tr class="alt1">
<td class="r">Connections</td>
<td class="c"><%= overview.object_totals.connections %></td>
</tr>
<tr class="alt2">
<td class="r">Channels</td>
<td class="c"><%= overview.object_totals.channels %></td>
</tr>
<tr class="alt1">
<td class="r">Exchanges</td>
<td class="c"><%= overview.object_totals.exchanges %></td>
</tr>
<tr class="alt2">
<td class="r">Queues</td>
<td class="c"><%= overview.object_totals.queues %></td>
</tr>
<% if (overview.object_totals['consumers'] != undefined) { %> <tr>
<tr class="alt1">
<td class="r">Consumers</td>
<td class="c"><%= overview.object_totals.consumers %></td>
</tr>
<% } %>
</table>
</div>
</div>
</div>

View File

@ -10,21 +10,24 @@
<th>Virtual Host</th>
<% } %>
<th>Name</th>
<th>Prefix</th>
<th>Regexp</th>
<th>Priority</th>
<th>Policy</th>
</tr>
</thead>
<tbody>
<%
for (var i = 0; i < policies.length; i++) {
var policy = policies[i];
var sorted_policies = policies.sort(function(a,b) {return b.value['priority'] || 0 - a.value['priority'] || 0} );
for (var i = 0; i < sorted_policies.length; i++) {
var policy = sorted_policies[i];
%>
<tr<%= alt_rows(i)%>>
<% if (vhosts_interesting) { %>
<td><%= fmt_string(policy.vhost) %></td>
<% } %>
<td><%= link_policy(policy.vhost, policy.key) %></td>
<td><%= fmt_string(policy.value['prefix']) %></td>
<td><%= fmt_string(policy.value['pattern']) %></td>
<td><%= fmt_string(policy.value['priority']) %></td>
<td><%= fmt_table_short(policy.value['policy']) %></td>
</tr>
<% } %>
@ -62,12 +65,17 @@
<td><input type="text" name="key"/><span class="mand">*</span></td>
</tr>
<tr>
<th><label>Prefix:</label></th>
<td><input type="text" name="prefix"/></td>
<th><label>Regexp:</label></th>
<td><input type="text" name="pattern"/><span class="mand">*</span></td>
</tr>
<tr>
<th><label>Priority:</label></th>
<td><input type="text" name="priority"/></td>
</tr>
<tr>
<th><label>Policy:</label></th>
<td><span class="multifield" id="policy"></span></td>
<td><span class="multifield mand" id="policy"></span></td>
<td><span class="mand">*</span></td>
</tr>
</table>
<input type="submit" value="Add policy"/>

View File

@ -9,8 +9,12 @@
<td><%= fmt_string(policy.vhost) %></td>
</tr>
<tr>
<th>Prefix</th>
<td><%= fmt_string(policy.value.prefix) %></td>
<th>Regexp</th>
<td><%= fmt_string(policy.value.pattern) %></td>
</tr>
<tr>
<th>Priority</th>
<td><%= fmt_string(policy.value.priority) %></td>
</tr>
<tr>
<th>Policy</th>

View File

@ -98,6 +98,7 @@ start_link() ->
%% mirrored_supervisor to maintain the uniqueness of this process.
case gen_server2:start_link(?MODULE, [], []) of
{ok, Pid} -> yes = global:re_register_name(?MODULE, Pid),
rabbit:force_event_refresh(),
{ok, Pid};
Else -> Else
end.
@ -213,7 +214,6 @@ init([]) ->
%% When Rabbit is overloaded, it's usually especially important
%% that the management plugin work.
process_flag(priority, high),
rabbit:force_event_refresh(),
{ok, Interval} = application:get_env(rabbit, collect_statistics_interval),
rabbit_log:info("Statistics database started.~n"),
{ok, #state{interval = Interval,
@ -272,7 +272,8 @@ handle_call({get_overview, User}, _From, State = #state{tables = Tables}) ->
Qs0 = [rabbit_mgmt_format:queue(Q) || V <- VHosts,
Q <- rabbit_amqqueue:list(V)],
Qs1 = basic_queue_stats(Qs0, State),
Totals = sum(Qs1, ?OVERVIEW_QUEUE_STATS),
QueueTotals = sum(Qs1, ?OVERVIEW_QUEUE_STATS),
Filter = fun(Id, Name) ->
lists:member(pget(vhost, pget(Name, Id)), VHosts)
end,
@ -285,8 +286,27 @@ handle_call({get_overview, User}, _From, State = #state{tables = Tables}) ->
end,
Publish = F(channel_exchange_stats, exchange),
Consume = F(channel_queue_stats, queue_details),
F2 = case User of
all -> fun (L) -> length(L) end;
_ -> fun (L) -> length(rabbit_mgmt_util:filter_user(L, User)) end
end,
%% Filtering out the user's consumers would be rather expensive so let's
%% just not show it
Consumers = case User of
all -> [{consumers,
ets:info(orddict:fetch(consumers, Tables), size)}];
_ -> []
end,
ObjectTotals = Consumers ++
[{queues, length(Qs0)},
{exchanges, length([X || V <- VHosts,
X <- rabbit_exchange:list(V)])},
{connections, F2(created_events(connection_stats, Tables))},
{channels, F2(created_events(channel_stats, Tables))}],
reply([{message_stats, Publish ++ Consume},
{queue_totals, Totals}], State);
{queue_totals, QueueTotals},
{object_totals, ObjectTotals}], State);
handle_call(_Request, _From, State) ->
reply(not_understood, State).
@ -377,7 +397,7 @@ handle_event(#event{type = consumer_deleted, props = Props}, State) ->
Props, State);
handle_event(#event{type = queue_mirror_deaths, props = Props},
State = #state{tables = Tables}) ->
#state{tables = Tables}) ->
Dead = pget(pids, Props),
Table = orddict:fetch(queue_stats, Tables),
%% Only the master can be in the DB, but it's easier just to
@ -422,10 +442,8 @@ handle_stats(TName, Stats0, Timestamp, Funs,
handle_deleted(TName, #event{props = Props}, State = #state{tables = Tables}) ->
Table = orddict:fetch(TName, Tables),
Pid = pget(pid, Props),
Name = pget(name, Props),
ets:delete(Table, {id(Pid), create}),
ets:delete(Table, {id(Pid), stats}),
ets:delete(Table, {Name, synchronised_slaves}),
{ok, State}.
handle_consumer(Fun, Props,
@ -560,18 +578,6 @@ consumer_details_fun(PatternFun, State = #state{tables = Tables}) ->
ets:match(Table, {Pattern, '$1'}))]}]
end.
synchronised_slaves_fun(#state{tables = Tables}) ->
Table = orddict:fetch(queue_stats, Tables),
fun (Props) -> QName = rabbit_misc:r(pget(vhost, Props), queue,
pget(name, Props)),
Key = {QName, synchronised_slaves},
case ets:lookup(Table, Key) of
[] -> [];
[{_, N}] -> [{synchronised_slave_nodes, N}]
end
end.
zero_old_rates(Stats, State) -> [maybe_zero_rate(S, State) || S <- Stats].
maybe_zero_rate({Key, Val}, #state{interval = Interval}) ->
@ -659,8 +665,7 @@ detail_queue_stats(Objs, State) ->
queue_funs(State))).
queue_funs(State) ->
[basic_stats_fun(queue_stats, pid, State), augment_msg_stats_fun(State),
synchronised_slaves_fun(State)].
[basic_stats_fun(queue_stats, pid, State), augment_msg_stats_fun(State)].
exchange_stats(Objs, FineSpecs, State) ->
merge_stats(Objs, [fine_stats_fun(FineSpecs, State),

View File

@ -104,16 +104,7 @@ utf8_safe(V) ->
<<"Invalid UTF-8, base64 is: ", Enc/binary>>
end.
parameter(P) -> pset(value, param0(pget(value, P)), P).
param0(L = [{_, _} | _]) -> {struct, [{K, param0(V)} || {K, V} <- L]};
param0(L) when is_list(L) -> [param0(I) || I <- L];
param0(B) when is_binary(B) -> B;
param0(N) when is_number(N) -> N;
param0(null) -> null;
param0(true) -> true;
param0(false) -> false.
parameter(P) -> pset(value, rabbit_misc:term_to_json(pget(value, P)), P).
tuple(unknown) -> unknown;
tuple(Tuple) when is_tuple(Tuple) -> [tuple(E) || E <- tuple_to_list(Tuple)];

View File

@ -1,34 +0,0 @@
%% The contents of this file are subject to the Mozilla Public License
%% Version 1.1 (the "License"); you may not use this file except in
%% compliance with the License. You may obtain a copy of the License at
%% http://www.mozilla.org/MPL/
%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
%% License for the specific language governing rights and limitations
%% under the License.
%%
%% The Original Code is RabbitMQ Management Plugin.
%%
%% The Initial Developer of the Original Code is VMware, Inc.
%% Copyright (c) 2010-2012 VMware, Inc. All rights reserved.
%%
-module(rabbit_mgmt_parse).
-export([parameter_value/1]).
%% TODO there is a bunch of stuff in format / util that should be moved here.
%% The idea is format means to JSON, this module means from JSON
parameter_value({struct, Props}) ->
[{K, parameter_value(V)} || {K, V} <- Props];
parameter_value(L) when is_list(L) ->
[parameter_value(V) || V <- L];
parameter_value(N) when is_number(N) -> N;
parameter_value(B) when is_binary(B) -> B;
parameter_value(null) -> null;
parameter_value(true) -> true;
parameter_value(false) -> false.

View File

@ -27,8 +27,8 @@
-export([with_channel/4, with_channel/5]).
-export([props_to_method/2, props_to_method/4]).
-export([all_or_one_vhost/2, http_to_amqp/5, reply/3, filter_vhost/3]).
-export([filter_conn_ch_list/3, with_decode/5, decode/1, decode/2, redirect/2,
args/1]).
-export([filter_conn_ch_list/3, filter_user/2]).
-export([with_decode/5, decode/1, decode/2, redirect/2, args/1]).
-export([reply_list/3, reply_list/4, sort_list/2, destination_type/1]).
-export([post_respond/1, columns/1, want_column/2, is_monitor/1]).
-export([list_visible_vhosts/1, b64decode_or_throw/1]).
@ -388,8 +388,10 @@ filter_vhost(List, _ReqData, Context) ->
VHosts = list_login_vhosts(Context#context.user),
[I || I <- List, lists:member(pget(vhost, I), VHosts)].
filter_user(List, _ReqData,
#context{user = #user{username = Username, tags = Tags}}) ->
filter_user(List, _ReqData, #context{user = User}) ->
filter_user(List, User).
filter_user(List, #user{username = Username, tags = Tags}) ->
case is_monitor(Tags) of
true -> List;
false -> [I || I <- List, pget(user, I) == Username]

View File

@ -195,8 +195,7 @@ add_parameter(Param) ->
Comp = pget(component, Param),
Key = pget(key, Param),
case rabbit_runtime_parameters:set(
VHost, Comp, Key,
rabbit_mgmt_parse:parameter_value(pget(value, Param))) of
VHost, Comp, Key, rabbit_misc:json_to_term(pget(value, Param))) of
ok -> ok;
{error_string, E} -> S = rabbit_misc:format(" (~s/~s/~s)",
[VHost, Comp, Key]),

View File

@ -60,7 +60,7 @@ accept_content(ReqData, Context) ->
fun([Value], _) ->
case rabbit_runtime_parameters:set(
VHost, component(ReqData), key(ReqData),
rabbit_mgmt_parse:parameter_value(Value)) of
rabbit_misc:json_to_term(Value)) of
ok ->
{true, ReqData, Context};
{error_string, Reason} ->

View File

@ -576,7 +576,8 @@ definitions_test() ->
[{users, []},
{vhosts, []},
{permissions, []},
{parameters, [[{value, [{<<"prefix">>, <<"">>},
{parameters, [[{value, [{<<"pattern">>, <<".*">>},
{<<"priority">>, 1},
{<<"policy">>, [{<<"a">>, <<"b">>}]}
]},
{vhost, <<"/">>},