diff --git a/deps/rabbitmq_management/priv/www/js/tmpl/queue.ejs b/deps/rabbitmq_management/priv/www/js/tmpl/queue.ejs
index b3a10f7b33..87ad536107 100644
--- a/deps/rabbitmq_management/priv/www/js/tmpl/queue.ejs
+++ b/deps/rabbitmq_management/priv/www/js/tmpl/queue.ejs
@@ -69,13 +69,6 @@
-
-
Consumers
-
-<%= format('consumers', {'mode': 'queue', 'consumers': queue.consumer_details}) %>
-
-
-
<% if (statistics_level == 'fine') { %>
Message Rates
@@ -104,10 +97,17 @@
<% } %>
-
Bindings
+
Consumers
+<%= format('consumers', {'mode': 'queue', 'consumers': queue.consumer_details}) %>
+
+
+
+
+
Bindings
+
Incoming to <%= queue.name %>
-
+
<%= format('bindings', {'mode': 'queue', 'bindings': bindings}) %>
@@ -118,11 +118,12 @@
-
-
<%= format('add-binding', {'mode': 'queue', 'parent': queue}) %>
+
+
+
Delete / Purge
@@ -143,10 +144,3 @@
-
-
-
Backing Queue Status
-
- <%= fmt_table_long(queue.backing_queue_status) %>
-
-
diff --git a/deps/rabbitmq_management/priv/www/js/tmpl/queues.ejs b/deps/rabbitmq_management/priv/www/js/tmpl/queues.ejs
index 369ee8888b..b7f8fce2d6 100644
--- a/deps/rabbitmq_management/priv/www/js/tmpl/queues.ejs
+++ b/deps/rabbitmq_management/priv/www/js/tmpl/queues.ejs
@@ -102,6 +102,18 @@
+<% if (nodes_interesting) { %>
+
+ On node:
+
+
+ <% for (var i = 0; i < nodes.length; i++) { %>
+ <%= nodes[i].name %>
+ <% } %>
+
+
+
+<% } %>
Auto delete:
diff --git a/deps/rabbitmq_management/priv/www/js/tmpl/table.ejs b/deps/rabbitmq_management/priv/www/js/tmpl/table.ejs
deleted file mode 100644
index cd1a7c9343..0000000000
--- a/deps/rabbitmq_management/priv/www/js/tmpl/table.ejs
+++ /dev/null
@@ -1,5 +0,0 @@
-
-<% for (var key in table) { %>
-<%= key %> <%= table[key] %>
-<% } %>
-
diff --git a/deps/rabbitmq_management/src/rabbit_mgmt_app.erl b/deps/rabbitmq_management/src/rabbit_mgmt_app.erl
index 7e697ecdbc..2ef2fdee9c 100644
--- a/deps/rabbitmq_management/src/rabbit_mgmt_app.erl
+++ b/deps/rabbitmq_management/src/rabbit_mgmt_app.erl
@@ -36,7 +36,11 @@
-define(PREFIX, "api").
-define(UI_PREFIX, "mgmt").
-define(CLI_PREFIX, "cli").
+-ifdef(trace).
+-define(SETUP_WM_TRACE, true).
+-else.
-define(SETUP_WM_TRACE, false).
+-endif.
%% Make sure our database is hooked in *before* listening on the network or
%% recovering queues (i.e. so there can't be any events fired before it starts).
@@ -61,11 +65,9 @@ stop(_State) ->
ok.
register_contexts() ->
- application:set_env(
- webmachine, dispatch_list,
- [{[?PREFIX | Path], F, A} ||
- {Path, F, A} <- rabbit_mgmt_dispatcher:dispatcher()]),
- application:set_env(webmachine, error_handler, webmachine_error_handler),
+ Dispatch =
+ [{[?PREFIX | Path], F, A} ||
+ {Path, F, A} <- rabbit_mgmt_dispatcher:dispatcher()],
rabbit_mochiweb:register_authenticated_static_context(
?UI_PREFIX, ?MODULE, "priv/www", "Management: Web UI",
fun (U, P) ->
@@ -75,7 +77,8 @@ register_contexts() ->
end
end),
rabbit_mochiweb:register_context_handler(?PREFIX,
- fun webmachine_mochiweb:loop/1,
+ rabbit_webmachine:makeloop(
+ Dispatch),
"Management: HTTP API"),
rabbit_mochiweb:register_static_context(?CLI_PREFIX, ?MODULE,
"priv/www-cli",
@@ -84,22 +87,22 @@ setup_wm_logging() ->
{ok, LogDir} = application:get_env(rabbit_management, http_log_dir),
case LogDir of
none ->
- ok;
+ rabbit_webmachine:setup(none);
_ ->
- application:set_env(webmachine, webmachine_logger_module,
- webmachine_logger),
+ rabbit_webmachine:setup(webmachine_logger),
webmachine_sup:start_logger(LogDir)
end.
%% This doesn't *entirely* seem to work. It fails to load a non-existent
%% image which seems to partly break it, but some stuff is usable.
setup_wm_trace_app() ->
- webmachine_router:start_link(),
- wmtrace_resource:add_dispatch_rule("wmtrace", "/tmp"),
+ Loop = rabbit_webmachine:makeloop([{["wmtrace", '*'],
+ wmtrace_resource,
+ [{trace_dir, "/tmp"}]}]),
rabbit_mochiweb:register_static_context(
"wmtrace/static", ?MODULE, "deps/webmachine/webmachine/priv/trace", none),
rabbit_mochiweb:register_context_handler("wmtrace",
- fun webmachine_mochiweb:loop/1,
+ Loop,
"Webmachine tracer").
log_startup() ->
{ok, Hostname} = inet:gethostname(),
diff --git a/deps/rabbitmq_management/src/rabbit_mgmt_format.erl b/deps/rabbitmq_management/src/rabbit_mgmt_format.erl
index 8172a90f20..15fff34063 100644
--- a/deps/rabbitmq_management/src/rabbit_mgmt_format.erl
+++ b/deps/rabbitmq_management/src/rabbit_mgmt_format.erl
@@ -20,7 +20,7 @@
-export([node_and_pid/1, protocol/1, resource/1, permissions/1, queue/1]).
-export([exchange/1, user/1, internal_user/1, binding/1, url/2]).
-export([pack_binding_props/2, unpack_binding_props/1, tokenise/1]).
--export([args_type/1, listener/1, properties/1]).
+-export([to_amqp_table/1, listener/1, properties/1]).
-include_lib("rabbit_common/include/rabbit.hrl").
@@ -71,8 +71,12 @@ properties(Table) -> {struct, [{Name, tuple(Value)} ||
{Name, Value} <- Table]}.
amqp_table(unknown) -> unknown;
-amqp_table(Table) -> {struct, [{Name, tuple(Value)} ||
- {Name, _Type, Value} <- Table]}.
+amqp_table(Table) -> {struct, [{Name, amqp_value(Type, Value)} ||
+ {Name, Type, Value} <- Table]}.
+
+amqp_value(array, Val) -> [amqp_value(T, V) || {T, V} <- Val];
+amqp_value(table, Val) -> amqp_table(Val);
+amqp_value(_Type, Val) -> Val.
tuple(unknown) -> unknown;
tuple(Tuple) when is_tuple(Tuple) -> [tuple(E) || E <- tuple_to_list(Tuple)];
@@ -156,7 +160,7 @@ unpack_binding_props(Str) ->
unpack_binding_props0([Key | Args]) ->
try
- {unquote_binding(Key), unpack_binding_args(Args)}
+ {unquote_binding(Key), to_amqp_table(unpack_binding_args(Args))}
catch E -> E
end;
unpack_binding_props0([]) ->
@@ -167,8 +171,7 @@ unpack_binding_args([]) ->
unpack_binding_args([K]) ->
throw({bad_request, {no_value, K}});
unpack_binding_args([K, V | Rest]) ->
- Value = unquote_binding(V),
- [{unquote_binding(K), args_type(Value), Value} | unpack_binding_args(Rest)].
+ [{unquote_binding(K), unquote_binding(V)} | unpack_binding_args(Rest)].
unquote_binding(Name) ->
list_to_binary(mochiweb_util:unquote(Name)).
@@ -185,6 +188,14 @@ tokenise(Str) ->
tokenise(string:sub_string(Str, Count + 2))]
end.
+to_amqp_table(T) ->
+ [to_amqp_table_row(K, V) || {K, V} <- T].
+
+to_amqp_table_row(K, Vs) when is_list(Vs) ->
+ {K, array, [{args_type(V), V} || V <- Vs]};
+to_amqp_table_row(K, V) ->
+ {K, args_type(V), V}.
+
args_type(X) when is_binary(X) ->
longstr;
args_type(X) when is_number(X) ->
diff --git a/deps/rabbitmq_management/src/rabbit_mgmt_util.erl b/deps/rabbitmq_management/src/rabbit_mgmt_util.erl
index c108a6d0ca..353d161249 100644
--- a/deps/rabbitmq_management/src/rabbit_mgmt_util.erl
+++ b/deps/rabbitmq_management/src/rabbit_mgmt_util.erl
@@ -24,6 +24,7 @@
-export([all_or_one_vhost/2, http_to_amqp/5, reply/3, filter_vhost/3]).
-export([filter_user/3, with_decode/5, redirect/2, args/1]).
-export([reply_list/3, reply_list/4, sort_list/4, destination_type/1]).
+-export([relativise/2]).
-include("rabbit_mgmt.hrl").
-include_lib("amqp_client/include/amqp_client.hrl").
@@ -256,10 +257,15 @@ http_to_amqp(MethodName, ReqData, Context, Transformers, Extra) ->
case decode(wrq:req_body(ReqData)) of
{ok, Props} ->
try
- rabbit_mgmt_util:amqp_request(
- VHost, ReqData, Context,
- props_to_method(
- MethodName, Props, Transformers, Extra))
+ Node =
+ case proplists:get_value(<<"node">>, Props) of
+ undefined -> node();
+ N -> rabbit_misc:makenode(
+ binary_to_list(N))
+ end,
+ amqp_request(VHost, ReqData, Context, Node,
+ props_to_method(
+ MethodName, Props, Transformers, Extra))
catch {error, Error} ->
bad_request(Error, ReqData, Context)
end;
@@ -295,12 +301,16 @@ parse_bool(true) -> true;
parse_bool(false) -> false;
parse_bool(V) -> throw({error, {not_boolean, V}}).
+amqp_request(VHost, ReqData, Context, Method) ->
+ amqp_request(VHost, ReqData, Context, node(), Method).
+
amqp_request(VHost, ReqData,
Context = #context{ user = #user { username = Username },
- password = Password }, Method) ->
+ password = Password }, Node, Method) ->
try
- Params = #amqp_params{username = Username,
- password = Password,
+ Params = #amqp_params{username = Username,
+ password = Password,
+ node = Node,
virtual_host = VHost},
case amqp_connection:start(direct, Params) of
{ok, Conn} ->
@@ -310,19 +320,23 @@ amqp_request(VHost, ReqData,
amqp_connection:close(Conn),
{true, ReqData, Context};
{error, auth_failure} ->
- not_authorised(<<"">>, ReqData, Context)
+ not_authorised(<<"">>, ReqData, Context);
+ {error, {nodedown, N}} ->
+ bad_request(
+ list_to_binary(
+ io_lib:format("Node ~s could not be contacted", [N])),
+ ReqData, Context)
end
catch
exit:{{server_initiated_close, ?NOT_FOUND, Reason}, _} ->
- not_found(list_to_binary(Reason), ReqData, Context);
+ not_found(Reason, ReqData, Context);
exit:{{server_initiated_close, ?ACCESS_REFUSED, Reason}, _} ->
- not_authorised(list_to_binary(Reason), ReqData, Context);
+ not_authorised(Reason, ReqData, Context);
exit:{{ServerClose, Code, Reason}, _}
when ServerClose =:= server_initiated_close;
ServerClose =:= server_initiated_hard_close ->
bad_request(list_to_binary(io_lib:format("~p ~s", [Code, Reason])),
- ReqData, Context);
- E:R -> io:format("~p~n", [{E,R}])
+ ReqData, Context)
end.
all_or_one_vhost(ReqData, Fun) ->
@@ -349,4 +363,23 @@ redirect(Location, ReqData) ->
args({struct, L}) ->
args(L);
args(L) ->
- [{K, rabbit_mgmt_format:args_type(V), V} || {K, V} <- L].
+ rabbit_mgmt_format:to_amqp_table(L).
+
+relativise("/" ++ F, "/" ++ T) ->
+ From = string:tokens(F, "/"),
+ To = string:tokens(T, "/"),
+ relativise0(From, To).
+
+relativise0([H], [H|_] = To) ->
+ string:join(To, "/");
+relativise0([H|From], [H|To]) ->
+ relativise0(From, To);
+relativise0(From, []) ->
+ relativise(From, [], 0);
+relativise0(From, To) ->
+ relativise(From, To, 1).
+
+relativise(From, To, Diff) ->
+ string:join(lists:duplicate(length(From) - Diff, "..") ++ To, "/").
+
+
diff --git a/deps/rabbitmq_management/src/rabbit_mgmt_wm_bindings.erl b/deps/rabbitmq_management/src/rabbit_mgmt_wm_bindings.erl
index a29c38b072..459f39bb4b 100644
--- a/deps/rabbitmq_management/src/rabbit_mgmt_wm_bindings.erl
+++ b/deps/rabbitmq_management/src/rabbit_mgmt_wm_bindings.erl
@@ -82,12 +82,14 @@ accept_content(ReqData, {_Mode, Context}) ->
rabbit_mgmt_util:with_decode(
[routing_key, arguments], ReqData, Context,
fun([Key, Args]) ->
- Loc = binary_to_list(
- rabbit_mgmt_format:url(
- "/api/bindings/~s/e/~s/~s/~s/~s",
- [VHost, Source, DestType, Dest,
- rabbit_mgmt_format:pack_binding_props(
- Key, rabbit_mgmt_util:args(Args))])),
+ Loc = rabbit_mgmt_util:relativise(
+ wrq:path(ReqData),
+ binary_to_list(
+ rabbit_mgmt_format:url(
+ "/api/bindings/~s/e/~s/~s/~s/~s",
+ [VHost, Source, DestType, Dest,
+ rabbit_mgmt_format:pack_binding_props(
+ Key, rabbit_mgmt_util:args(Args))]))),
ReqData2 = wrq:set_resp_header("Location", Loc, ReqData),
{true, ReqData2, Context2}
end)
diff --git a/deps/rabbitmq_management/test/rabbit_mgmt_test_http.erl b/deps/rabbitmq_management/test/rabbit_mgmt_test_http.erl
index dbbec6e38a..45006b0ebb 100644
--- a/deps/rabbitmq_management/test/rabbit_mgmt_test_http.erl
+++ b/deps/rabbitmq_management/test/rabbit_mgmt_test_http.erl
@@ -312,7 +312,7 @@ bindings_post_test() ->
http_post("/bindings/%2f/e/badexchange/q/myqueue", BArgs, ?NOT_FOUND),
http_post("/bindings/%2f/e/myexchange/q/myqueue", [{a, "b"}], ?BAD_REQUEST),
Headers = http_post("/bindings/%2f/e/myexchange/q/myqueue", BArgs, ?CREATED),
- "/api/bindings/%2F/e/myexchange/q/myqueue/routing_foo_bar" =
+ "../../../../%2F/e/myexchange/q/myqueue/routing_foo_bar" =
pget("location", Headers),
[{source,<<"myexchange">>},
{vhost,<<"/">>},
@@ -332,7 +332,7 @@ bindings_e2e_test() ->
http_post("/bindings/%2f/e/amq.direct/e/badexchange", BArgs, ?NOT_FOUND),
http_post("/bindings/%2f/e/badexchange/e/amq.fanout", BArgs, ?NOT_FOUND),
Headers = http_post("/bindings/%2f/e/amq.direct/e/amq.fanout", BArgs, ?CREATED),
- "/api/bindings/%2F/e/amq.direct/e/amq.fanout/routing" =
+ "../../../../%2F/e/amq.direct/e/amq.fanout/routing" =
pget("location", Headers),
[{source,<<"amq.direct">>},
{vhost,<<"/">>},
@@ -618,6 +618,19 @@ arguments_test() ->
http_delete("/queues/%2f/myqueue", ?NO_CONTENT),
ok.
+arguments_table_test() ->
+ Args = [{'upstreams', [<<"amqp://localhost/%2f/upstream1">>,
+ <<"amqp://localhost/%2f/upstream2">>]}],
+ XArgs = [{type, <<"headers">>},
+ {arguments, Args}],
+ http_put("/exchanges/%2f/myexchange", XArgs, ?NO_CONTENT),
+ AllConfig = http_get("/all-configuration", ?OK),
+ http_delete("/exchanges/%2f/myexchange", ?NO_CONTENT),
+ http_post("/all-configuration", AllConfig, ?NO_CONTENT),
+ Args = pget(arguments, http_get("/exchanges/%2f/myexchange", ?OK)),
+ http_delete("/exchanges/%2f/myexchange", ?NO_CONTENT),
+ ok.
+
queue_purge_test() ->
QArgs = [],
http_put("/queues/%2f/myqueue", QArgs, ?NO_CONTENT),
diff --git a/deps/rabbitmq_management/test/rabbit_mgmt_test_unit.erl b/deps/rabbitmq_management/test/rabbit_mgmt_test_unit.erl
index 82e38ea63d..f9b6d9302c 100644
--- a/deps/rabbitmq_management/test/rabbit_mgmt_test_unit.erl
+++ b/deps/rabbitmq_management/test/rabbit_mgmt_test_unit.erl
@@ -48,6 +48,15 @@ pack_binding_test() ->
rabbit_mgmt_format:unpack_binding_props(<<"bad_routing">>),
ok.
+relativise_test() ->
+ "baz" = rabbit_mgmt_util:relativise("/foo/bar/bash", "/foo/bar/baz"),
+ "../bax/baz" = rabbit_mgmt_util:relativise("/foo/bar/bash", "/foo/bax/baz"),
+ "../bax/baz" = rabbit_mgmt_util:relativise("/bar/bash", "/bax/baz"),
+ ".." = rabbit_mgmt_util:relativise("/foo/bar/bash", "/foo/bar"),
+ "../.." = rabbit_mgmt_util:relativise("/foo/bar/bash", "/foo"),
+ "bar/baz" = rabbit_mgmt_util:relativise("/foo/bar", "/foo/bar/baz"),
+ ok.
+
%%--------------------------------------------------------------------
assert_binding(Packed, Routing, Args) ->