From 051795a5f1b20169ff3eabd7c1c2c0c551ca5951 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 20 Jan 2011 17:35:50 +0000 Subject: [PATCH 01/11] List all consumers: code, tests, docs, rabbitmqadmin. --- deps/rabbitmq_management/bin/rabbitmqadmin | 4 +-- .../priv/www-api/help.html | 8 +++++ .../src/rabbit_mgmt_db.erl | 14 +++++++- .../src/rabbit_mgmt_dispatcher.erl | 1 + .../src/rabbit_mgmt_wm_consumers.erl | 36 +++++++++++++++++++ .../test/rabbit_mgmt_test_http.erl | 15 ++++++++ 6 files changed, 75 insertions(+), 3 deletions(-) create mode 100644 deps/rabbitmq_management/src/rabbit_mgmt_wm_consumers.erl diff --git a/deps/rabbitmq_management/bin/rabbitmqadmin b/deps/rabbitmq_management/bin/rabbitmqadmin index 8d8c6b269f..3454cd939a 100755 --- a/deps/rabbitmq_management/bin/rabbitmqadmin +++ b/deps/rabbitmq_management/bin/rabbitmqadmin @@ -24,8 +24,8 @@ import json import os import socket -LISTABLE = ['connections', 'channels', 'exchanges', 'queues', 'bindings', - 'users', 'vhosts', 'permissions', 'nodes'] +LISTABLE = ['connections', 'channels', 'consumers', 'exchanges', 'queues', + 'bindings', 'users', 'vhosts', 'permissions', 'nodes'] SHOWABLE = ['overview'] diff --git a/deps/rabbitmq_management/priv/www-api/help.html b/deps/rabbitmq_management/priv/www-api/help.html index 9b42ed4a86..61862e7a0b 100644 --- a/deps/rabbitmq_management/priv/www-api/help.html +++ b/deps/rabbitmq_management/priv/www-api/help.html @@ -212,6 +212,14 @@ Content-Length: 0 /api/channels/channel Details about an individual channel. + + X + + + + /api/consumers + A list of all consumers. + X diff --git a/deps/rabbitmq_management/src/rabbit_mgmt_db.erl b/deps/rabbitmq_management/src/rabbit_mgmt_db.erl index c3b18948f7..e648394bf8 100644 --- a/deps/rabbitmq_management/src/rabbit_mgmt_db.erl +++ b/deps/rabbitmq_management/src/rabbit_mgmt_db.erl @@ -22,7 +22,7 @@ -export([get_queues/1, get_queue/1, get_exchanges/1, get_exchange/1, get_connections/0, get_connection/1, get_overview/1, - get_overview/0, get_channels/0, get_channel/1]). + get_overview/0, get_channels/0, get_channel/1, get_consumers/0]). %% TODO can these not be exported any more? -export([pget/2, add/2, rates/5]). @@ -99,6 +99,7 @@ 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_consumers() -> safe_call(get_consumers). get_overview(User) -> safe_call({get_overview, User}). get_overview() -> safe_call({get_overview, all}). @@ -239,6 +240,13 @@ handle_call({get_channel, Name}, _From, State = #state{tables = Tables}) -> [Res] = channel_stats(Chs, ?FINE_STATS_CHANNEL_DETAIL, Tables), {reply, result_or_error(Res), State}; +handle_call(get_consumers, _From, State = #state{tables = Tables}) -> + Consumers = + [augment_msg_stats(Obj, Tables) || + {_, Obj} <- ets:tab2list(orddict:fetch(consumers, Tables))], + + {reply, Consumers, State}; + handle_call({get_overview, User}, _From, State = #state{tables = Tables}) -> VHosts = case User of all -> rabbit_vhost:list(); @@ -615,3 +623,7 @@ channel_stats(Objs, FineSpecs, Tables) -> fun (Props) -> {'_', pget(pid, Props)} end, Tables), fine_stats_fun(FineSpecs, Tables), augment_msg_stats_fun(Tables)]). + +consumer_stats(Objs, Tables) -> + merge_stats(Objs, [basic_stats_fun(consumers, Tables), + augment_msg_stats_fun(Tables)]). diff --git a/deps/rabbitmq_management/src/rabbit_mgmt_dispatcher.erl b/deps/rabbitmq_management/src/rabbit_mgmt_dispatcher.erl index 156068ab9a..482b4a012b 100644 --- a/deps/rabbitmq_management/src/rabbit_mgmt_dispatcher.erl +++ b/deps/rabbitmq_management/src/rabbit_mgmt_dispatcher.erl @@ -27,6 +27,7 @@ dispatcher() -> {["connections", connection, "channels"], rabbit_mgmt_wm_connection_channels, []}, {["channels"], rabbit_mgmt_wm_channels, []}, {["channels", channel], rabbit_mgmt_wm_channel, []}, + {["consumers"], rabbit_mgmt_wm_consumers, []}, {["exchanges"], rabbit_mgmt_wm_exchanges, []}, {["exchanges", vhost], rabbit_mgmt_wm_exchanges, []}, {["exchanges", vhost, exchange], rabbit_mgmt_wm_exchange, []}, diff --git a/deps/rabbitmq_management/src/rabbit_mgmt_wm_consumers.erl b/deps/rabbitmq_management/src/rabbit_mgmt_wm_consumers.erl new file mode 100644 index 0000000000..0e2d4b79d5 --- /dev/null +++ b/deps/rabbitmq_management/src/rabbit_mgmt_wm_consumers.erl @@ -0,0 +1,36 @@ +%% 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) 2007-2010 VMware, Inc. All rights reserved. +-module(rabbit_mgmt_wm_consumers). + +-export([init/1, to_json/2, content_types_provided/2, is_authorized/2]). + +-include("rabbit_mgmt.hrl"). +-include_lib("webmachine/include/webmachine.hrl"). +-include_lib("rabbit_common/include/rabbit.hrl"). + +%%-------------------------------------------------------------------- + +init(_Config) -> {ok, #context{}}. + +content_types_provided(ReqData, Context) -> + {[{"application/json", to_json}], ReqData, Context}. + +to_json(ReqData, Context) -> + Chs = rabbit_mgmt_util:filter_user(rabbit_mgmt_db:get_consumers(), + ReqData, Context), + rabbit_mgmt_util:reply_list(Chs, ReqData, Context). + +is_authorized(ReqData, Context) -> + rabbit_mgmt_util:is_authorized(ReqData, Context). diff --git a/deps/rabbitmq_management/test/rabbit_mgmt_test_http.erl b/deps/rabbitmq_management/test/rabbit_mgmt_test_http.erl index dbbec6e38a..ef98456f50 100644 --- a/deps/rabbitmq_management/test/rabbit_mgmt_test_http.erl +++ b/deps/rabbitmq_management/test/rabbit_mgmt_test_http.erl @@ -498,6 +498,21 @@ permissions_connection_channel_test() -> http_get("/channels/foo", ?NOT_FOUND), ok. +consumers_test() -> + http_put("/queues/%2f/test", [], ?NO_CONTENT), + {Conn, _ConnPath, _ChPath} = get_conn("guest", "guest"), + {ok, Ch} = amqp_connection:open_channel(Conn), + amqp_channel:subscribe( + Ch, #'basic.consume'{queue = <<"test">>, + no_ack = false, + consumer_tag = <<"my-ctag">> }, self()), + assert_list([[{exclusive, false}, + {ack_required, true}, + {consumer_tag, <<"my-ctag">>}]], http_get("/consumers")), + amqp_connection:close(Conn), + http_delete("/queues/%2f/test", ?NO_CONTENT), + ok. + unicode_test() -> QArgs = [], http_put("/queues/%2f/♫♪♫♪", QArgs, ?NO_CONTENT), From c13b2feeef62a6fff899e2565fbf001f5ad7aa1d Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 20 Jan 2011 17:36:39 +0000 Subject: [PATCH 02/11] List all consumers: web UI. This is debatably useful, and I'm not sure about the extra tab. --- deps/rabbitmq_management/priv/www/index.html | 1 + deps/rabbitmq_management/priv/www/js/main.js | 1 + .../priv/www/js/tmpl/channel.ejs | 2 +- .../priv/www/js/tmpl/consumers.ejs | 15 ++++---- .../priv/www/js/tmpl/list-consumers.ejs | 35 +++++++++++++++++++ .../priv/www/js/tmpl/queue.ejs | 2 +- 6 files changed, 46 insertions(+), 10 deletions(-) create mode 100644 deps/rabbitmq_management/priv/www/js/tmpl/list-consumers.ejs diff --git a/deps/rabbitmq_management/priv/www/index.html b/deps/rabbitmq_management/priv/www/index.html index 5e4b270cc4..be3e535b24 100644 --- a/deps/rabbitmq_management/priv/www/index.html +++ b/deps/rabbitmq_management/priv/www/index.html @@ -27,6 +27,7 @@
  • Overview
  • Connections
  • Channels
  • +
  • Consumers
  • Exchanges
  • Queues
  • Users
  • diff --git a/deps/rabbitmq_management/priv/www/js/main.js b/deps/rabbitmq_management/priv/www/js/main.js index d84ded3957..0dd2e6f2e1 100644 --- a/deps/rabbitmq_management/priv/www/js/main.js +++ b/deps/rabbitmq_management/priv/www/js/main.js @@ -96,6 +96,7 @@ function dispatcher() { render({'channel': '/channels/' + esc(this.params['name'])}, 'channel', '#/channels'); }); + path('#/consumers', {'consumers': '/consumers'}, 'consumers'); path('#/exchanges', {'exchanges': '/exchanges', 'vhosts': '/vhosts'}, 'exchanges'); this.get('#/exchanges/:vhost/:name', function() { diff --git a/deps/rabbitmq_management/priv/www/js/tmpl/channel.ejs b/deps/rabbitmq_management/priv/www/js/tmpl/channel.ejs index 2e30ca682d..aa097cff26 100644 --- a/deps/rabbitmq_management/priv/www/js/tmpl/channel.ejs +++ b/deps/rabbitmq_management/priv/www/js/tmpl/channel.ejs @@ -62,7 +62,7 @@

    Consumers

    -<%= format('consumers', {'mode': 'channel', 'consumers': channel.consumer_details}) %> +<%= format('list-consumers', {'mode': 'channel', 'consumers': channel.consumer_details}) %>
    diff --git a/deps/rabbitmq_management/priv/www/js/tmpl/consumers.ejs b/deps/rabbitmq_management/priv/www/js/tmpl/consumers.ejs index de9de9176f..8845103a92 100644 --- a/deps/rabbitmq_management/priv/www/js/tmpl/consumers.ejs +++ b/deps/rabbitmq_management/priv/www/js/tmpl/consumers.ejs @@ -1,14 +1,15 @@ +

    Consumers

    +
    +

    All consumers

    +
    +<%= maybe_truncate(consumers) %> <% if (consumers.length > 0) { %> -<% if (mode == 'queue') { %> -<% } else { %> - -<% } %> @@ -18,13 +19,9 @@ var consumer = consumers[i]; %> > -<% if (mode == 'queue') { %> -<% } else { %> - -<% } %> @@ -33,3 +30,5 @@ <% } else { %>

    ... no consumers ...

    <% } %> + + diff --git a/deps/rabbitmq_management/priv/www/js/tmpl/list-consumers.ejs b/deps/rabbitmq_management/priv/www/js/tmpl/list-consumers.ejs new file mode 100644 index 0000000000..de9de9176f --- /dev/null +++ b/deps/rabbitmq_management/priv/www/js/tmpl/list-consumers.ejs @@ -0,0 +1,35 @@ +<% if (consumers.length > 0) { %> +
    Channel Consumer TagConsumer Tag QueueAck Required Exclusive
    <%= link_channel(consumer.channel_details.name) %> <%= consumer.consumer_tag %><%= consumer.consumer_tag %> <%= link_queue(consumer.queue_details.vhost, consumer.queue_details.name) %><%= fmt_boolean(consumer.ack_required) %> <%= fmt_boolean(consumer.exclusive) %>
    + + +<% if (mode == 'queue') { %> + + +<% } else { %> + + +<% } %> + + + + +<% + for (var i = 0; i < consumers.length; i++) { + var consumer = consumers[i]; +%> + > +<% if (mode == 'queue') { %> + + +<% } else { %> + + +<% } %> + + + +<% } %> +
    ChannelConsumer TagConsumer TagQueueAck RequiredExclusive
    <%= link_channel(consumer.channel_details.name) %><%= consumer.consumer_tag %><%= consumer.consumer_tag %><%= link_queue(consumer.queue_details.vhost, consumer.queue_details.name) %><%= fmt_boolean(consumer.ack_required) %><%= fmt_boolean(consumer.exclusive) %>
    +<% } else { %> +

    ... no consumers ...

    +<% } %> diff --git a/deps/rabbitmq_management/priv/www/js/tmpl/queue.ejs b/deps/rabbitmq_management/priv/www/js/tmpl/queue.ejs index 4ab98fecc2..a69f01972b 100644 --- a/deps/rabbitmq_management/priv/www/js/tmpl/queue.ejs +++ b/deps/rabbitmq_management/priv/www/js/tmpl/queue.ejs @@ -72,7 +72,7 @@

    Consumers

    -<%= format('consumers', {'mode': 'queue', 'consumers': queue.consumer_details}) %> +<%= format('list-consumers', {'mode': 'queue', 'consumers': queue.consumer_details}) %>
    From 1340dfbf4973fc6f8c5574fedc121c0c588c0a3a Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 20 Jan 2011 17:37:53 +0000 Subject: [PATCH 03/11] Bah! --- deps/rabbitmq_management/src/rabbit_mgmt_db.erl | 4 ---- 1 file changed, 4 deletions(-) diff --git a/deps/rabbitmq_management/src/rabbit_mgmt_db.erl b/deps/rabbitmq_management/src/rabbit_mgmt_db.erl index e648394bf8..74c6f56cc5 100644 --- a/deps/rabbitmq_management/src/rabbit_mgmt_db.erl +++ b/deps/rabbitmq_management/src/rabbit_mgmt_db.erl @@ -623,7 +623,3 @@ channel_stats(Objs, FineSpecs, Tables) -> fun (Props) -> {'_', pget(pid, Props)} end, Tables), fine_stats_fun(FineSpecs, Tables), augment_msg_stats_fun(Tables)]). - -consumer_stats(Objs, Tables) -> - merge_stats(Objs, [basic_stats_fun(consumers, Tables), - augment_msg_stats_fun(Tables)]). From bc1ac34c4f00c5cf5082f533fe70ed6d683a84b8 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 23 Apr 2014 14:10:59 +0100 Subject: [PATCH 04/11] Ignore empty string values in sub-lists. --- deps/rabbitmq_management/priv/www/js/main.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/deps/rabbitmq_management/priv/www/js/main.js b/deps/rabbitmq_management/priv/www/js/main.js index 68506d8931..f675c6e14d 100644 --- a/deps/rabbitmq_management/priv/www/js/main.js +++ b/deps/rabbitmq_management/priv/www/js/main.js @@ -994,11 +994,12 @@ function collapse_multifields(params0) { var v = params0[name + '_' + id + '_mfvalue']; var t = params0[name + '_' + id + '_mftype']; var val = null; + var top_level = id_parts.length == 1; if (t == 'list') { val = []; id_map[name][id] = val; } - else if (set(k) || set(v)) { + else if ((set(k) && top_level) || set(v)) { if (t == 'boolean') { if (v != 'true' && v != 'false') throw(k + ' must be "true" or "false"; got ' + v); @@ -1015,7 +1016,7 @@ function collapse_multifields(params0) { } } if (val != null) { - if (id_parts.length == 1) { + if (top_level) { params[name][k] = val; } else { From 87459a12e5899420a302578eeeca6d98be060dab Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 24 Apr 2014 13:20:29 +0100 Subject: [PATCH 05/11] Don't allow bad property types through. --- deps/rabbitmq_management/priv/www/js/main.js | 23 +++++++++++++++---- .../src/rabbit_mgmt_format.erl | 12 ++++++++-- .../test/src/rabbit_mgmt_test_http.erl | 9 ++++++++ 3 files changed, 38 insertions(+), 6 deletions(-) diff --git a/deps/rabbitmq_management/priv/www/js/main.js b/deps/rabbitmq_management/priv/www/js/main.js index f675c6e14d..3091d9071e 100644 --- a/deps/rabbitmq_management/priv/www/js/main.js +++ b/deps/rabbitmq_management/priv/www/js/main.js @@ -734,11 +734,26 @@ function publish_msg(params0) { params['properties']['delivery_mode'] = parseInt(params['delivery_mode']); if (params['headers'] != '') params['properties']['headers'] = params['headers']; - var props = ['content_type', 'content_encoding', 'priority', 'correlation_id', 'reply_to', 'expiration', 'message_id', 'timestamp', 'type', 'user_id', 'app_id', 'cluster_id']; + var props = [['content_type', 'str'], + ['content_encoding', 'str'], + ['correlation_id', 'str'], + ['reply_to', 'str'], + ['expiration', 'str'], + ['message_id', 'str'], + ['type', 'str'], + ['user_id', 'str'], + ['app_id', 'str'], + ['cluster_id', 'str'], + ['priority', 'int'], + ['timestamp', 'int']]; for (var i in props) { - var p = props[i]; - if (params['props'][p] != '') - params['properties'][p] = params['props'][p]; + var name = props[i][0]; + var type = props[i][1]; + if (params['props'][name] != undefined && params['props'][name] != '') { + var value = params['props'][name]; + if (type == 'int') value = parseInt(value); + params['properties'][name] = value; + } } with_req('POST', path, JSON.stringify(params), function(resp) { var result = jQuery.parseJSON(resp.responseText); diff --git a/deps/rabbitmq_management/src/rabbit_mgmt_format.erl b/deps/rabbitmq_management/src/rabbit_mgmt_format.erl index 617f3eedce..487ff860e6 100644 --- a/deps/rabbitmq_management/src/rabbit_mgmt_format.erl +++ b/deps/rabbitmq_management/src/rabbit_mgmt_format.erl @@ -277,8 +277,16 @@ to_basic_properties({struct, P}) -> to_basic_properties(P); to_basic_properties(Props) -> - Fmt = fun (headers, H) -> to_amqp_table(H); - (_K , V) -> V + E = fun (A, B) -> throw({error, {A, B}}) end, + Fmt = fun (headers, H) -> to_amqp_table(H); + (delivery_mode, V) when is_integer(V) -> V; + (delivery_mode, _V) -> E(not_int,delivery_mode); + (priority, V) when is_integer(V) -> V; + (priority, _V) -> E(not_int, priority); + (timestamp, V) when is_integer(V) -> V; + (timestamp, _V) -> E(not_int, timestamp); + (_, V) when is_binary(V) -> V; + (K, _V) -> E(not_string, K) end, {Res, _Ix} = lists:foldl( fun (K, {P, Ix}) -> diff --git a/deps/rabbitmq_management/test/src/rabbit_mgmt_test_http.erl b/deps/rabbitmq_management/test/src/rabbit_mgmt_test_http.erl index 123d5d4cc6..8daf5ffcc1 100644 --- a/deps/rabbitmq_management/test/src/rabbit_mgmt_test_http.erl +++ b/deps/rabbitmq_management/test/src/rabbit_mgmt_test_http.erl @@ -959,6 +959,15 @@ publish_fail_test() -> {payload, [<<"not a string">>]}, {payload_encoding, <<"string">>}], http_post("/exchanges/%2f/amq.default/publish", Msg3, ?BAD_REQUEST), + MsgTemplate = [{exchange, <<"">>}, + {routing_key, <<"myqueue">>}, + {payload, <<"Hello world">>}, + {payload_encoding, <<"string">>}], + [http_post("/exchanges/%2f/amq.default/publish", + [{properties, [BadProp]} | MsgTemplate], ?BAD_REQUEST) + || BadProp <- [{priority, <<"really high">>}, + {timestamp, <<"recently">>}, + {expiration, 1234}]], http_delete("/users/myuser", ?NO_CONTENT), ok. From 988ddaf57baf9c5e42ba11999d6352350ee05f91 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 1 May 2014 13:01:29 +0100 Subject: [PATCH 07/11] Oops, remove that --- .../priv/www/js/tmpl/list-consumers.ejs | 35 ------------------- 1 file changed, 35 deletions(-) delete mode 100644 deps/rabbitmq_management/priv/www/js/tmpl/list-consumers.ejs diff --git a/deps/rabbitmq_management/priv/www/js/tmpl/list-consumers.ejs b/deps/rabbitmq_management/priv/www/js/tmpl/list-consumers.ejs deleted file mode 100644 index de9de9176f..0000000000 --- a/deps/rabbitmq_management/priv/www/js/tmpl/list-consumers.ejs +++ /dev/null @@ -1,35 +0,0 @@ -<% if (consumers.length > 0) { %> - - - -<% if (mode == 'queue') { %> - - -<% } else { %> - - -<% } %> - - - - -<% - for (var i = 0; i < consumers.length; i++) { - var consumer = consumers[i]; -%> - > -<% if (mode == 'queue') { %> - - -<% } else { %> - - -<% } %> - - - -<% } %> -
    ChannelConsumer TagConsumer TagQueueAck RequiredExclusive
    <%= link_channel(consumer.channel_details.name) %><%= consumer.consumer_tag %><%= consumer.consumer_tag %><%= link_queue(consumer.queue_details.vhost, consumer.queue_details.name) %><%= fmt_boolean(consumer.ack_required) %><%= fmt_boolean(consumer.exclusive) %>
    -<% } else { %> -

    ... no consumers ...

    -<% } %> From 8e95aade4d0c3bf329a08a05a6af5433d03c0daa Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Thu, 1 May 2014 13:06:50 +0100 Subject: [PATCH 08/11] Allow filtering by vhost, test monitoring vs non monitoring filtering. --- .../priv/www/api/index.html | 8 +++++ .../src/rabbit_mgmt_db.erl | 14 +++++--- .../src/rabbit_mgmt_dispatcher.erl | 1 + .../src/rabbit_mgmt_wm_consumers.erl | 32 +++++++++++++++---- .../test/src/rabbit_mgmt_test_http.erl | 19 +++++++---- 5 files changed, 56 insertions(+), 18 deletions(-) diff --git a/deps/rabbitmq_management/priv/www/api/index.html b/deps/rabbitmq_management/priv/www/api/index.html index 646df67395..1fb5c14324 100644 --- a/deps/rabbitmq_management/priv/www/api/index.html +++ b/deps/rabbitmq_management/priv/www/api/index.html @@ -294,6 +294,14 @@ Content-Length: 0 /api/consumers A list of all consumers. + + X + + + + /api/consumers/vhost + A list of all consumers in a given virtual host. + X diff --git a/deps/rabbitmq_management/src/rabbit_mgmt_db.erl b/deps/rabbitmq_management/src/rabbit_mgmt_db.erl index 3c4f1a0d3e..f544c62a00 100644 --- a/deps/rabbitmq_management/src/rabbit_mgmt_db.erl +++ b/deps/rabbitmq_management/src/rabbit_mgmt_db.erl @@ -27,7 +27,7 @@ augment_nodes/1, augment_vhosts/2, get_channel/2, get_connection/2, get_all_channels/1, get_all_connections/1, - get_all_consumers/0, + get_all_consumers/0, get_all_consumers/1, get_overview/2, get_overview/1]). -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, @@ -203,7 +203,8 @@ get_connection(Name, R) -> safe_call({get_connection, Name, R}, not_found). get_all_channels(R) -> safe_call({get_all_channels, R}). get_all_connections(R) -> safe_call({get_all_connections, R}). -get_all_consumers() -> safe_call(get_all_consumers). +get_all_consumers() -> safe_call({get_all_consumers, all}). +get_all_consumers(V) -> safe_call({get_all_consumers, V}). get_overview(User, R) -> safe_call({get_overview, User, R}). get_overview(R) -> safe_call({get_overview, all, R}). @@ -293,11 +294,13 @@ handle_call({get_all_connections, Ranges}, _From, Conns = created_events(connection_stats, Tables), reply(connection_stats(Ranges, Conns, State), State); -handle_call(get_all_consumers, _From, State = #state{tables = Tables}) -> +handle_call({get_all_consumers, VHost}, + _From, State = #state{tables = Tables}) -> All = ets:tab2list(orddict:fetch(consumers_by_queue, Tables)), {reply, [augment_msg_stats( - augment_consumer(Obj), State) ||{{_Q, _Ch, _CTag}, Obj} <- All], - State}; + augment_consumer(Obj), State) || + {{#resource{virtual_host = VHostC}, _Ch, _CTag}, Obj} <- All, + VHost =:= all orelse VHost =:= VHostC], State}; handle_call({get_overview, User, Ranges}, _From, State = #state{tables = Tables}) -> @@ -1036,6 +1039,7 @@ augment_channel_pid(Pid, #state{tables = Tables}) -> {pget(connection, Ch), create}), [{name, pget(name, Ch)}, {number, pget(number, Ch)}, + {user, pget(user, Ch)}, {connection_name, pget(name, Conn)}, {peer_port, pget(peer_port, Conn)}, {peer_host, pget(peer_host, Conn)}]. diff --git a/deps/rabbitmq_management/src/rabbit_mgmt_dispatcher.erl b/deps/rabbitmq_management/src/rabbit_mgmt_dispatcher.erl index d8d4646458..1e7100fae3 100644 --- a/deps/rabbitmq_management/src/rabbit_mgmt_dispatcher.erl +++ b/deps/rabbitmq_management/src/rabbit_mgmt_dispatcher.erl @@ -56,6 +56,7 @@ dispatcher() -> {["channels"], rabbit_mgmt_wm_channels, []}, {["channels", channel], rabbit_mgmt_wm_channel, []}, {["consumers"], rabbit_mgmt_wm_consumers, []}, + {["consumers", vhost], rabbit_mgmt_wm_consumers, []}, {["exchanges"], rabbit_mgmt_wm_exchanges, []}, {["exchanges", vhost], rabbit_mgmt_wm_exchanges, []}, {["exchanges", vhost, exchange], rabbit_mgmt_wm_exchange, []}, diff --git a/deps/rabbitmq_management/src/rabbit_mgmt_wm_consumers.erl b/deps/rabbitmq_management/src/rabbit_mgmt_wm_consumers.erl index 3dcb640ecc..d531fc4d97 100644 --- a/deps/rabbitmq_management/src/rabbit_mgmt_wm_consumers.erl +++ b/deps/rabbitmq_management/src/rabbit_mgmt_wm_consumers.erl @@ -10,11 +10,15 @@ %% %% The Original Code is RabbitMQ Management Plugin. %% -%% The Initial Developer of the Original Code is VMware, Inc. -%% Copyright (c) 2007-2010 VMware, Inc. All rights reserved. +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2010-2014 GoPivotal, Inc. All rights reserved. + -module(rabbit_mgmt_wm_consumers). --export([init/1, to_json/2, content_types_provided/2, is_authorized/2]). +-export([init/1, to_json/2, content_types_provided/2, resource_exists/2, + is_authorized/2]). + +-import(rabbit_misc, [pget/2]). -include("rabbit_mgmt.hrl"). -include_lib("webmachine/include/webmachine.hrl"). @@ -27,10 +31,26 @@ init(_Config) -> {ok, #context{}}. content_types_provided(ReqData, Context) -> {[{"application/json", to_json}], ReqData, Context}. +resource_exists(ReqData, Context) -> + {case rabbit_mgmt_util:vhost(ReqData) of + vhost_not_found -> false; + _ -> true + end, ReqData, Context}. + to_json(ReqData, Context = #context{user = User}) -> - Chs = rabbit_mgmt_util:filter_user( - rabbit_mgmt_db:get_all_consumers(), User), - rabbit_mgmt_util:reply_list(Chs, ReqData, Context). + Consumers = case rabbit_mgmt_util:vhost(ReqData) of + none -> rabbit_mgmt_db:get_all_consumers(); + VHost -> rabbit_mgmt_db:get_all_consumers(VHost) + end, + rabbit_mgmt_util:reply_list( + filter_user(Consumers, User), ReqData, Context). is_authorized(ReqData, Context) -> rabbit_mgmt_util:is_authorized(ReqData, Context). + +filter_user(List, #user{username = Username, tags = Tags}) -> + case rabbit_mgmt_util:is_monitor(Tags) of + true -> List; + false -> [I || I <- List, + pget(user, pget(channel_details, I)) == Username] + end. diff --git a/deps/rabbitmq_management/test/src/rabbit_mgmt_test_http.erl b/deps/rabbitmq_management/test/src/rabbit_mgmt_test_http.erl index c00fc0d101..cf4fc8ef2a 100644 --- a/deps/rabbitmq_management/test/src/rabbit_mgmt_test_http.erl +++ b/deps/rabbitmq_management/test/src/rabbit_mgmt_test_http.erl @@ -520,21 +520,25 @@ get_conn(Username, Password) -> [LocalPort]), {Conn, ConnPath, ChPath, ConnChPath}. -permissions_connection_channel_test() -> +permissions_connection_channel_consumer_test() -> PermArgs = [{configure, <<".*">>}, {write, <<".*">>}, {read, <<".*">>}], http_put("/users/user", [{password, <<"user">>}, {tags, <<"management">>}], ?NO_CONTENT), http_put("/permissions/%2f/user", PermArgs, ?NO_CONTENT), http_put("/users/monitor", [{password, <<"monitor">>}, - {tags, <<"monitoring">>}], ?NO_CONTENT), + {tags, <<"monitoring">>}], ?NO_CONTENT), http_put("/permissions/%2f/monitor", PermArgs, ?NO_CONTENT), + http_put("/queues/%2f/test", [], ?NO_CONTENT), + {Conn1, UserConn, UserCh, UserConnCh} = get_conn("user", "user"), {Conn2, MonConn, MonCh, MonConnCh} = get_conn("monitor", "monitor"), {Conn3, AdmConn, AdmCh, AdmConnCh} = get_conn("guest", "guest"), - {ok, _Ch1} = amqp_connection:open_channel(Conn1), - {ok, _Ch2} = amqp_connection:open_channel(Conn2), - {ok, _Ch3} = amqp_connection:open_channel(Conn3), - + {ok, Ch1} = amqp_connection:open_channel(Conn1), + {ok, Ch2} = amqp_connection:open_channel(Conn2), + {ok, Ch3} = amqp_connection:open_channel(Conn3), + [amqp_channel:subscribe( + Ch, #'basic.consume'{queue = <<"test">>}, self()) || + Ch <- [Ch1, Ch2, Ch3]], AssertLength = fun (Path, User, Len) -> ?assertEqual(Len, length(http_get(Path, User, User, ?OK))) @@ -543,7 +547,7 @@ permissions_connection_channel_test() -> AssertLength(P, "user", 1), AssertLength(P, "monitor", 3), AssertLength(P, "guest", 3) - end || P <- ["/connections", "/channels"]], + end || P <- ["/connections", "/channels", "/consumers", "/consumers/%2f"]], AssertRead = fun(Path, UserStatus) -> http_get(Path, "user", "user", UserStatus), @@ -573,6 +577,7 @@ permissions_connection_channel_test() -> http_delete("/users/monitor", ?NO_CONTENT), http_get("/connections/foo", ?NOT_FOUND), http_get("/channels/foo", ?NOT_FOUND), + http_delete("/queues/%2f/test", ?NO_CONTENT), ok. consumers_test() -> From a8667bc507749b8e93d7066248d14de0b2ecd7c1 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 9 May 2014 15:23:57 +0100 Subject: [PATCH 09/11] add an export --- deps/rabbitmq_management/src/rabbit_mgmt_util.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/rabbitmq_management/src/rabbit_mgmt_util.erl b/deps/rabbitmq_management/src/rabbit_mgmt_util.erl index 2a425b10ce..235d674f61 100644 --- a/deps/rabbitmq_management/src/rabbit_mgmt_util.erl +++ b/deps/rabbitmq_management/src/rabbit_mgmt_util.erl @@ -28,7 +28,7 @@ -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, filter_user/2]). +-export([filter_conn_ch_list/3, filter_user/2, list_login_vhosts/1]). -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, is_monitor/1]). From d7be27dbf175bd39774ead723741dd587a0576a2 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 13 May 2014 13:32:48 +0100 Subject: [PATCH 10/11] Prioritise queries. --- deps/rabbitmq_management/src/rabbit_mgmt_db.erl | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/deps/rabbitmq_management/src/rabbit_mgmt_db.erl b/deps/rabbitmq_management/src/rabbit_mgmt_db.erl index f544c62a00..2ed40c24e9 100644 --- a/deps/rabbitmq_management/src/rabbit_mgmt_db.erl +++ b/deps/rabbitmq_management/src/rabbit_mgmt_db.erl @@ -31,8 +31,8 @@ get_overview/2, get_overview/1]). -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, - code_change/3, handle_pre_hibernate/1, prioritise_cast/3, - format_message_queue/2]). + code_change/3, handle_pre_hibernate/1, + prioritise_cast/3, prioritise_call/4, format_message_queue/2]). %% For testing -export([override_lookups/1, reset_lookups/0]). @@ -176,6 +176,9 @@ prioritise_cast({event, #event{type = Type, prioritise_cast(_Msg, _Len, _State) -> 0. +%% We want timely replies to queries even when overloaded! +prioritise_call(_Msg, _From, _Len, _State) -> 5. + %%---------------------------------------------------------------------------- %% API %%---------------------------------------------------------------------------- From b39b6d09e38a1deffe999d0a3b872c03a756595e Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 13 May 2014 15:47:22 +0100 Subject: [PATCH 11/11] Unbreak rabbit_mgmt_test_db post bug 26174. --- deps/rabbitmq_management/src/rabbit_mgmt_db.erl | 6 ++++++ .../test/src/rabbit_mgmt_test_db.erl | 10 +++++----- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/deps/rabbitmq_management/src/rabbit_mgmt_db.erl b/deps/rabbitmq_management/src/rabbit_mgmt_db.erl index 2ed40c24e9..4a7d82f4ef 100644 --- a/deps/rabbitmq_management/src/rabbit_mgmt_db.erl +++ b/deps/rabbitmq_management/src/rabbit_mgmt_db.erl @@ -345,6 +345,12 @@ handle_call({override_lookups, Lookups}, _From, State) -> handle_call(reset_lookups, _From, State) -> reply(ok, reset_lookups(State)); +%% Used in rabbit_mgmt_test_db where we need guarantees events have +%% been handled before querying +handle_call({event, Event = #event{reference = none}}, _From, State) -> + handle_event(Event, State), + reply(ok, State); + handle_call(_Request, _From, State) -> reply(not_understood, State). diff --git a/deps/rabbitmq_management/test/src/rabbit_mgmt_test_db.erl b/deps/rabbitmq_management/test/src/rabbit_mgmt_test_db.erl index ef2f5be09f..e73cbbe850 100644 --- a/deps/rabbitmq_management/test/src/rabbit_mgmt_test_db.erl +++ b/deps/rabbitmq_management/test/src/rabbit_mgmt_test_db.erl @@ -204,11 +204,11 @@ delete_ch(Name, Timestamp) -> event(channel_closed, [{pid, pid_del(Name)}], Timestamp). event(Type, Stats, Timestamp) -> - gen_server:cast({global, rabbit_mgmt_db}, - {event, #event{type = Type, - props = Stats, - reference = none, - timestamp = sec_to_triple(Timestamp)}}). + ok = gen_server:call(rabbit_mgmt_db, + {event, #event{type = Type, + props = Stats, + reference = none, + timestamp = sec_to_triple(Timestamp)}}). sec_to_triple(Sec) -> {Sec div 1000000, Sec rem 1000000, 0}.