Merge bug23580 (Add statistics for 'basic.return')
This commit is contained in:
commit
317d3f85f8
|
|
@ -1,5 +1,12 @@
|
|||
This package, the RabbitMQ Management Plugin is licensed under the MPL. For the
|
||||
MPL, please see LICENSE-MPL-RabbitMQ.
|
||||
|
||||
This package makes use of the following third party libraries:
|
||||
jQuery - http://jquery.com/ - MIT license
|
||||
EJS - http://embeddedjs.com/ - MIT license
|
||||
Sammy - http://code.quirkey.com/sammy/ - MIT license
|
||||
webmachine - http://webmachine.basho.com/ - Apache license, 2.0
|
||||
mochiweb - http://github.com/mochi/mochiweb/ - MIT license
|
||||
|
||||
If you have any questions regarding licensing, please contact us at
|
||||
info@rabbitmq.com.
|
||||
|
|
|
|||
|
|
@ -277,7 +277,7 @@ Content-Length: 0</pre>
|
|||
<td></td>
|
||||
<td class="path">/api/queues/<i>vhost</i>/<i>name</i></td>
|
||||
<td>An individual queue. To PUT a queue, you will need a body looking something like this:
|
||||
<pre>{"auto_delete":false,"durable":true,"arguments":[]}</pre>
|
||||
<pre>{"auto_delete":false,"durable":true,"arguments":[],"node":"rabbit@smacmullen"}</pre>
|
||||
All keys are optional.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
|
@ -382,7 +382,13 @@ Content-Length: 0</pre>
|
|||
<td class="path">/api/users/<i>name</i></td>
|
||||
<td>An individual user. To PUT a user, you will need a body looking something like this:
|
||||
<pre>{"password":"secret", "administrator":true}</pre>
|
||||
All keys are mandatory.</td>
|
||||
or:
|
||||
<pre>{"password_hash":"2lmoth8l4H0DViLaK9Fxi6l9ds8=", "administrator":true}</pre>
|
||||
The <code>administrator</code> key is mandatory. Either
|
||||
<code>password</code> or <code>password_hash</code>
|
||||
must be set. Setting <code>password_hash</code> to "" will ensure the
|
||||
user cannot use a password to log in.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>X</td>
|
||||
|
|
|
|||
|
|
@ -48,8 +48,11 @@ table.facts th, table.facts td { vertical-align: top; padding: 0 10px 10px 10px;
|
|||
table.facts-long th { text-align: right; font-weight: bold; }
|
||||
table.facts-long th, table.facts-long td { vertical-align: top; }
|
||||
|
||||
tr.alt1 td { background: #eee; }
|
||||
tr.alt2 td { background: #fff; }
|
||||
table.mini th { border: none; padding: 0 2px 2px 2px; text-align: right; }
|
||||
table.mini td { border: none; padding: 0 2px 2px 2px; }
|
||||
|
||||
tr.alt1>td { background: #eee; }
|
||||
tr.alt2>td { background: #fff; }
|
||||
|
||||
td.status div { padding: 5px; text-align: center; border-radius: 5px; -moz-border-radius: 5px; }
|
||||
td.status div.red { background: #F62817; color:white; }
|
||||
|
|
@ -122,12 +125,12 @@ td.binding-endpoint span.arrow { font-size: 200%; }
|
|||
|
||||
#scratch { display: none; }
|
||||
|
||||
tr.alt1 td {
|
||||
tr.alt1>td {
|
||||
background: -moz-linear-gradient(center top, #f0f0f0 0%,#e0e0e0 100%);
|
||||
background: -webkit-gradient(linear, left top, left bottom, color-stop(0, #f0f0f0),color-stop(1, #e0e0e0));
|
||||
}
|
||||
|
||||
tr.alt2 td {
|
||||
tr.alt2>td {
|
||||
background: -moz-linear-gradient(center top, #f8f8f8 0%,#ffffff 100%);
|
||||
background: -webkit-gradient(linear, left top, left bottom, color-stop(0, #f8f8f8),color-stop(1, #ffffff));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,10 @@
|
|||
UNKNOWN_REPR = '<span class="unknown">?</span>';
|
||||
DESCRIPTOR_THRESHOLDS=[[0.75, 'red'],
|
||||
[0.5, 'yellow']];
|
||||
FD_THRESHOLDS=[[0.95, 'red'],
|
||||
[0.8, 'yellow']];
|
||||
SOCKETS_THRESHOLDS=[[1.0, 'red'],
|
||||
[0.8, 'yellow']];
|
||||
PROCESS_THRESHOLDS=[[0.75, 'red'],
|
||||
[0.5, 'yellow']];
|
||||
MEMORY_THRESHOLDS=[[1.0, 'red']];
|
||||
|
||||
function fmt_string(str) {
|
||||
|
|
@ -57,7 +61,7 @@ function fmt_parameters(obj) {
|
|||
}
|
||||
var args = fmt_table_short(obj.arguments);
|
||||
if (args != '') {
|
||||
res += '<p>' + args + '</p>';
|
||||
res += args;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
|
@ -76,15 +80,14 @@ function fmt_channel_mode(ch) {
|
|||
|
||||
function fmt_color(r, thresholds) {
|
||||
if (r == undefined) return '';
|
||||
if (thresholds == undefined) thresholds = DESCRIPTOR_THRESHOLDS;
|
||||
|
||||
for (var i in thresholds) {
|
||||
var threshold = thresholds[i][0];
|
||||
var color = thresholds[i][1];
|
||||
var threshold = thresholds[i][0];
|
||||
var color = thresholds[i][1];
|
||||
|
||||
if (r > threshold) {
|
||||
return color;
|
||||
}
|
||||
if (r >= threshold) {
|
||||
return color;
|
||||
}
|
||||
}
|
||||
return 'green';
|
||||
}
|
||||
|
|
@ -142,19 +145,31 @@ function fmt_download_filename(host) {
|
|||
}
|
||||
|
||||
function fmt_table_short(table) {
|
||||
return '<table class="mini">' + fmt_table_body(table, ':') + '</table>';
|
||||
}
|
||||
|
||||
function fmt_table_long(table) {
|
||||
return '<table class="facts">' + fmt_table_body(table, '') +
|
||||
'</table><span class="br"></span>';
|
||||
}
|
||||
|
||||
function fmt_table_body(table, x) {
|
||||
var res = '';
|
||||
for (k in table) {
|
||||
res += k + '=' + table[k] + '<br/>';
|
||||
res += '<tr><th>' + k + x + '</th><td>' + fmt_amqp_value(table[k]) +
|
||||
'</td>';
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
function fmt_table_long(table) {
|
||||
var res = '<table class="facts">';
|
||||
for (k in table) {
|
||||
res += '<tr><th>' + k + '</th><td>' + table[k] + '</td>';
|
||||
function fmt_amqp_value(val) {
|
||||
if (val instanceof Array) {
|
||||
return val.join("<br/>");
|
||||
} else if (val instanceof Object) {
|
||||
return fmt_table_short(val);
|
||||
} else {
|
||||
return val;
|
||||
}
|
||||
return res + '</table><span class="br"></span>';
|
||||
}
|
||||
|
||||
function fmt_uptime(u) {
|
||||
|
|
@ -290,4 +305,4 @@ function fmt_sort(display, sort) {
|
|||
'</span>';
|
||||
}
|
||||
return '<a class="sort" sort="' + sort + '">' + prefix + display + '</a>';
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,6 +46,19 @@ HELP = {
|
|||
<dd>Channel is transactional.</dd> \
|
||||
<dl>',
|
||||
|
||||
'file-descriptors':
|
||||
'File descriptor count and limit, as reported by the operating system. \
|
||||
The count includes network sockets and file handlers.<br/> \
|
||||
To optimize disk access RabbitMQ uses as many free descriptors as are \
|
||||
available, so the count may safely approach the limit. \
|
||||
However, if most of the file descriptors are used by sockets then \
|
||||
persister performance will be negatively impacted.',
|
||||
|
||||
'socket-descriptors':
|
||||
'The network sockets count and limit managed by RabbitMQ.<br/> \
|
||||
When the limit is exhausted RabbitMQ will stop accepting new \
|
||||
network connections.',
|
||||
|
||||
'foo': 'foo' // No comma.
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -143,7 +143,7 @@ function dispatcher() {
|
|||
return false;
|
||||
});
|
||||
|
||||
path('#/queues', {'queues': '/queues', 'vhosts': '/vhosts'}, 'queues');
|
||||
path('#/queues', {'queues': '/queues', 'vhosts': '/vhosts', 'nodes': '/nodes'}, 'queues');
|
||||
this.get('#/queues/:vhost/:name', function() {
|
||||
var path = '/queues/' + esc(this.params['vhost']) + '/' + esc(this.params['name']);
|
||||
render({'queue': path,
|
||||
|
|
@ -411,7 +411,7 @@ function postprocess() {
|
|||
}
|
||||
});
|
||||
$('#download-configuration').click(function() {
|
||||
var path = '/api/all-configuration?download=' +
|
||||
var path = '../api/all-configuration?download=' +
|
||||
esc($('#download-filename').val());
|
||||
window.location = path;
|
||||
setTimeout('app.run()');
|
||||
|
|
@ -536,7 +536,7 @@ function toggle_visibility(item) {
|
|||
function with_reqs(reqs, acc, fun) {
|
||||
if (keys(reqs).length > 0) {
|
||||
var key = keys(reqs)[0];
|
||||
with_req('/api' + reqs[key], function(resp) {
|
||||
with_req('../api' + reqs[key], function(resp) {
|
||||
acc[key] = jQuery.parseJSON(resp.responseText);
|
||||
var remainder = {};
|
||||
for (var k in reqs) {
|
||||
|
|
@ -641,7 +641,7 @@ function sync_req(type, params0, path_template) {
|
|||
return false;
|
||||
}
|
||||
var req = xmlHttpRequest();
|
||||
req.open(type, '/api' + path, false);
|
||||
req.open(type, '../api' + path, false);
|
||||
req.setRequestHeader('content-type', 'application/json');
|
||||
try {
|
||||
if (type == 'GET')
|
||||
|
|
@ -689,7 +689,8 @@ function fill_path_template(template, params) {
|
|||
}
|
||||
|
||||
// Better suggestions appreciated
|
||||
var INTEGER_ARGUMENTS = map(['x-expires']);
|
||||
var INTEGER_ARGUMENTS = map(['x-expires', 'x-message-ttl']);
|
||||
var ARRAY_ARGUMENTS = map(['upstreams']); // Used by the federation plugin
|
||||
|
||||
function params_magic(params) {
|
||||
return maybe_remove_password(
|
||||
|
|
@ -718,6 +719,8 @@ function collapse_multifields(params0) {
|
|||
var v = params0[name + '_' + id + '_mfvalue'];
|
||||
if (k in INTEGER_ARGUMENTS) {
|
||||
v = parseInt(v);
|
||||
} else if (k in ARRAY_ARGUMENTS) {
|
||||
v = v.split(" ");
|
||||
}
|
||||
params[name][k] = v;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
<div class="section-hidden">
|
||||
<h2>Add Binding</h2>
|
||||
<div class="hider">
|
||||
<h3>Add Binding</h3>
|
||||
<form action="#/bindings" method="post">
|
||||
<table class="bindings">
|
||||
<tr>
|
||||
|
|
@ -68,5 +66,3 @@
|
|||
</tr>
|
||||
</table>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -123,7 +123,7 @@
|
|||
<div class="section-hidden">
|
||||
<h2>Client Library</h2>
|
||||
<div class="hider">
|
||||
<%= format('table', {'table': connection.client_properties}) %>
|
||||
<%= fmt_table_long(connection.client_properties) %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@
|
|||
|
||||
<div class="section-hidden">
|
||||
<h2>Bindings</h2>
|
||||
<div class="hider updatable">
|
||||
<div class="hider">
|
||||
<% if (exchange.name == "") { %>
|
||||
<h3>Default exchange</h3>
|
||||
<p>
|
||||
|
|
@ -65,7 +65,7 @@
|
|||
<% } else { %>
|
||||
<% if (bindings_destination.length > 0) { %>
|
||||
<h3>Incoming to <b><%= fmt_exchange(exchange.name) %></b></h3>
|
||||
<table class="bindings">
|
||||
<table class="bindings updatable">
|
||||
<tr>
|
||||
<td>
|
||||
<%= format('bindings', {'mode': 'exchange_destination', 'bindings': bindings_destination}) %>
|
||||
|
|
@ -76,9 +76,9 @@
|
|||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<% } %>
|
||||
<% } %>
|
||||
<h3>Outgoing from <b><%= fmt_exchange(exchange.name) %></b></h3>
|
||||
<table class="bindings">
|
||||
<table class="bindings updatable">
|
||||
<tr>
|
||||
<td class="binding-endpoint">
|
||||
<span class="object"><%= fmt_exchange(exchange.name) %></span>
|
||||
|
|
@ -89,14 +89,12 @@
|
|||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<%= format('add-binding', {'mode': 'exchange_source', 'parent': exchange}) %>
|
||||
<% } %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<% if (exchange.name != "") { %>
|
||||
|
||||
<%= format('add-binding', {'mode': 'exchange_source', 'parent': exchange}) %>
|
||||
|
||||
<div class="section-hidden">
|
||||
<h2>Delete This Exchange</h2>
|
||||
<div class="hider">
|
||||
|
|
|
|||
|
|
@ -6,22 +6,36 @@
|
|||
<table class="facts">
|
||||
<tr>
|
||||
<th>
|
||||
File Descriptors
|
||||
File Descriptors <span class="help" id="file-descriptors"></span>
|
||||
<sub>(used / available)</sub>
|
||||
</th>
|
||||
<td class="status">
|
||||
<div class="<%= fmt_color(node.fd_used / node.fd_total) %>">
|
||||
<div class="<%= fmt_color(node.fd_used / node.fd_total,
|
||||
FD_THRESHOLDS) %>">
|
||||
<%= node.fd_used %> / <%= node.fd_total %>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>
|
||||
Socket Descriptors <span class="help" id="socket-descriptors"></span>
|
||||
<sub>(used / available)</sub>
|
||||
</th>
|
||||
<td class="status">
|
||||
<div class="<%= fmt_color(node.sockets_used / node.sockets_total,
|
||||
SOCKETS_THRESHOLDS) %>">
|
||||
<%= node.sockets_used %> / <%= node.sockets_total %>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>
|
||||
Erlang Processes
|
||||
<sub>(used / available)</sub>
|
||||
</th>
|
||||
<td class="status">
|
||||
<div class="<%= fmt_color(node.proc_used / node.proc_total) %>">
|
||||
<div class="<%= fmt_color(node.proc_used / node.proc_total,
|
||||
PROCESS_THRESHOLDS) %>">
|
||||
<%= node.proc_used %> / <%= node.proc_total %>
|
||||
</div>
|
||||
</td>
|
||||
|
|
|
|||
|
|
@ -34,7 +34,11 @@
|
|||
<tr>
|
||||
<th>Name</th>
|
||||
<th>
|
||||
File Descriptors
|
||||
File Descriptors <span class="help" id="file-descriptors"></span>
|
||||
<sub>(used / available)</sub>
|
||||
</th>
|
||||
<th>
|
||||
Socket Descriptors <span class="help" id="socket-descriptors"></span>
|
||||
<sub>(used / available)</sub>
|
||||
</th>
|
||||
<th>
|
||||
|
|
@ -74,12 +78,20 @@
|
|||
</td>
|
||||
<% } else { %>
|
||||
<td class="status">
|
||||
<div class="<%= fmt_color(node.fd_used / node.fd_total) %>">
|
||||
<div class="<%= fmt_color(node.fd_used / node.fd_total,
|
||||
FD_THRESHOLDS) %>">
|
||||
<%= node.fd_used %> / <%= node.fd_total %>
|
||||
</div>
|
||||
</td>
|
||||
<td class="status">
|
||||
<div class="<%= fmt_color(node.proc_used / node.proc_total) %>">
|
||||
<div class="<%= fmt_color(node.sockets_used / node.sockets_total,
|
||||
SOCKETS_THRESHOLDS) %>">
|
||||
<%= node.sockets_used %> / <%= node.sockets_total %>
|
||||
</div>
|
||||
</td>
|
||||
<td class="status">
|
||||
<div class="<%= fmt_color(node.proc_used / node.proc_total,
|
||||
PROCESS_THRESHOLDS) %>">
|
||||
<%= node.proc_used %> / <%= node.proc_total %>
|
||||
</div>
|
||||
</td>
|
||||
|
|
@ -148,7 +160,7 @@ for (var i = 0; i < overview.listeners.length; i++) {
|
|||
<div class="section-hidden administrator-only">
|
||||
<h2>Import / Export Configuration</h2>
|
||||
<div class="hider">
|
||||
<form action="/api/all-configuration" method="post" enctype="multipart/form-data">
|
||||
<form action="../api/all-configuration" method="post" enctype="multipart/form-data">
|
||||
<table class="two-col-layout">
|
||||
<tr>
|
||||
<td>
|
||||
|
|
|
|||
|
|
@ -69,13 +69,6 @@
|
|||
</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>
|
||||
|
|
@ -104,10 +97,17 @@
|
|||
<% } %>
|
||||
|
||||
<div class="section-hidden">
|
||||
<h2>Bindings</h2>
|
||||
<h2>Consumers</h2>
|
||||
<div class="hider updatable">
|
||||
<%= format('consumers', {'mode': 'queue', 'consumers': queue.consumer_details}) %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section-hidden">
|
||||
<h2>Bindings</h2>
|
||||
<div class="hider">
|
||||
<h3>Incoming to <b><%= queue.name %></b></h3>
|
||||
<table class="bindings">
|
||||
<table class="bindings updatable">
|
||||
<tr>
|
||||
<td>
|
||||
<%= format('bindings', {'mode': 'queue', 'bindings': bindings}) %>
|
||||
|
|
@ -118,11 +118,12 @@
|
|||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<%= format('add-binding', {'mode': 'queue', 'parent': queue}) %>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section-hidden">
|
||||
<h2>Delete / Purge</h2>
|
||||
<div class="hider">
|
||||
|
|
@ -143,10 +144,3 @@
|
|||
<span class="br"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section-hidden">
|
||||
<h2>Backing Queue Status</h2>
|
||||
<div class="hider updatable">
|
||||
<%= fmt_table_long(queue.backing_queue_status) %>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -102,6 +102,18 @@
|
|||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<% if (nodes_interesting) { %>
|
||||
<tr>
|
||||
<th><label>On node:</label></th>
|
||||
<td>
|
||||
<select name="node">
|
||||
<% for (var i = 0; i < nodes.length; i++) { %>
|
||||
<option value="<%= nodes[i].name %>"><%= nodes[i].name %></option>
|
||||
<% } %>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<% } %>
|
||||
<tr>
|
||||
<th><label>Auto delete:</label></th>
|
||||
<td>
|
||||
|
|
|
|||
|
|
@ -1,5 +0,0 @@
|
|||
<table class="facts-long">
|
||||
<% for (var key in table) { %>
|
||||
<tr><th><%= key %></th><td><%= table[key] %></td></tr>
|
||||
<% } %>
|
||||
</table>
|
||||
|
|
@ -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(),
|
||||
|
|
|
|||
|
|
@ -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) ->
|
||||
|
|
|
|||
|
|
@ -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, "/").
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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) ->
|
||||
|
|
|
|||
Loading…
Reference in New Issue