Skip to content
This repository was archived by the owner on Nov 16, 2020. It is now read-only.

Commit da8e6ff

Browse files
committed
Include node in HTTP API and UI
Allows for cluster management from a single node and management interface behind a load balancer [#157481542]
1 parent 55a3f11 commit da8e6ff

9 files changed

+112
-43
lines changed

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,15 @@ some other way.
3838

3939
```
4040
GET /api/traces
41+
GET /api/traces/<node>
4142
GET /api/traces/<vhost>
43+
GET /api/traces/<node>/<vhost>
4244
GET PUT DELETE /api/traces/<vhost>/<name>
45+
GET PUT DELETE /api/traces/<node>/<vhost>/<name>
4346
GET /api/trace-files
47+
GET /api/trace-files/<node>
4448
GET DELETE /api/trace-files/<name> (GET returns the file as text/plain)
49+
GET DELETE /api/trace-files/<node>/<name> (GET returns the file as text/plain)
4550
```
4651

4752
Example for how to create a trace using [RabbitMQ HTTP API](http://www.rabbitmq.com/management.html):

priv/www/js/tmpl/traces.ejs

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,13 @@
1-
<h1>Traces</h1>
1+
<h1>Traces: <b><%= node.name %></b></h1>
2+
<p>
3+
Node:
4+
<select id="traces-node">
5+
<% for (var i = 0; i < nodes.length; i++) { %>
6+
<option name="#/traces/<%= fmt_string(nodes[i].name) %>"<% if (nodes[i].name == node.name) { %>selected="selected"<% } %>><%= nodes[i].name %></option>
7+
<% } %>
8+
</select>
9+
</p>
10+
211
<div class="section">
312
<h2>All traces</h2>
413
<div class="hider updatable">
@@ -49,7 +58,7 @@
4958
</td>
5059
<% } %>
5160
<td>
52-
<form action="#/traces" method="delete">
61+
<form action="#/traces/<%= node.name %>" method="delete">
5362
<input type="hidden" name="vhost" value="<%= fmt_string(trace.vhost) %>"/>
5463
<input type="hidden" name="name" value="<%= fmt_string(trace.name) %>"/>
5564
<input type="submit" value="Stop"/>
@@ -80,10 +89,10 @@
8089
var file = files[i];
8190
%>
8291
<tr<%= alt_rows(i)%>>
83-
<td><%= link_trace(file.name) %></td>
92+
<td><%= link_trace(node.name, file.name) %></td>
8493
<td class="r"><%= fmt_bytes(file.size) %></td>
8594
<td>
86-
<form action="#/trace-files" method="delete" class="inline-form">
95+
<form action="#/trace-files/<%= node.name %>" method="delete" class="inline-form">
8796
<input type="hidden" name="name" value="<%= fmt_string(file.name) %>"/>
8897
<input type="submit" value="Delete" />
8998
</form>
@@ -104,7 +113,7 @@
104113
<div class="section">
105114
<h2>Add a new trace</h2>
106115
<div class="hider">
107-
<form action="#/traces" method="put">
116+
<form action="#/traces/<%= node.name %>" method="put">
108117
<table class="form">
109118
<% if (vhosts_interesting) { %>
110119
<tr>

priv/www/js/tracing.js

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,41 @@
11
dispatcher_add(function(sammy) {
22
sammy.get('#/traces', function() {
3-
render({'traces': '/traces',
4-
'vhosts': '/vhosts',
5-
'files': '/trace-files'},
6-
'traces', '#/traces');
3+
var nodes = JSON.parse(sync_get('/nodes'));
4+
go_to('#/traces/' + nodes[0].name);
75
});
8-
sammy.get('#/traces/:vhost/:name', function() {
9-
var path = '/traces/' + esc(this.params['vhost']) + '/' + esc(this.params['name']);
6+
sammy.get('#/traces/:node', function() {
7+
render({'traces': '/traces/' + esc(this.params['node']),
8+
'vhosts': '/vhosts',
9+
'node': '/nodes/' + esc(this.params['node']),
10+
'nodes': '/nodes',
11+
'files': '/trace-files/' + esc(this.params['node'])},
12+
'traces', '#/traces')
13+
});
14+
sammy.get('#/traces/:node/:vhost/:name', function() {
15+
var path = '/traces/' + esc(this.params['node']) + '/' + esc(this.params['vhost']) + '/' + esc(this.params['name']);
1016
render({'trace': path},
1117
'trace', '#/traces');
1218
});
13-
sammy.put('#/traces', function() {
19+
sammy.put('#/traces/:node', function() {
1420
if (this.params['max_payload_bytes'] === '') {
1521
delete this.params['max_payload_bytes'];
1622
}
1723
else {
1824
this.params['max_payload_bytes'] =
1925
parseInt(this.params['max_payload_bytes']);
2026
}
21-
if (sync_put(this, '/traces/:vhost/:name'))
27+
if (sync_put(this, '/traces/' + esc(this.params['node']) + '/:vhost/:name'))
2228
update();
2329
return false;
2430
});
25-
sammy.del('#/traces', function() {
26-
if (sync_delete(this, '/traces/:vhost/:name'))
31+
sammy.del('#/traces/:node', function() {
32+
if (sync_delete(this, '/traces/' + esc(this.params['node'])
33+
+ '/:vhost/:name'))
2734
partial_update();
2835
return false;
2936
});
30-
sammy.del('#/trace-files', function() {
31-
if (sync_delete(this, '/trace-files/:name'))
37+
sammy.del('#/trace-files/:node', function() {
38+
if (sync_delete(this, '/trace-files/' + esc(this.params['node']) + '/:name'))
3239
partial_update();
3340
return false;
3441
});
@@ -39,8 +46,13 @@ NAVIGATION['Admin'][0]['Tracing'] = ['#/traces', 'administrator'];
3946
HELP['tracing-max-payload'] =
4047
'Maximum size of payload to log, in bytes. Payloads larger than this limit will be truncated. Leave blank to prevent truncation. Set to 0 to prevent logging of payload altogether.';
4148

42-
function link_trace(name) {
43-
return _link_to(name, 'api/trace-files/' + esc(name));
49+
$(document).on('change', 'select#traces-node', function() {
50+
var url='#/traces/' + $(this).val();
51+
go_to(url);
52+
});
53+
54+
function link_trace(node, name) {
55+
return _link_to(name, 'api/trace-files/' + esc(node) + '/' + esc(name));
4456
}
4557

4658
function link_trace_queue(trace) {

src/rabbit_tracing_mgmt.erl

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,15 @@
2020

2121
-export([dispatcher/0, web_ui/0]).
2222

23-
dispatcher() -> [{"/traces", rabbit_tracing_wm_traces, []},
24-
{"/traces/:vhost", rabbit_tracing_wm_traces, []},
25-
{"/traces/:vhost/:name", rabbit_tracing_wm_trace, []},
26-
{"/trace-files", rabbit_tracing_wm_files, []},
27-
{"/trace-files/:name", rabbit_tracing_wm_file, []}].
23+
dispatcher() -> [{"/traces", rabbit_tracing_wm_traces, []},
24+
{"/traces/:node", rabbit_tracing_wm_traces, []},
25+
{"/traces/:vhost", rabbit_tracing_wm_traces, []},
26+
{"/traces/:node/:vhost", rabbit_tracing_wm_traces, []},
27+
{"/traces/:vhost/:name", rabbit_tracing_wm_trace, []},
28+
{"/traces/:node/:vhost/:name", rabbit_tracing_wm_trace, []},
29+
{"/trace-files", rabbit_tracing_wm_files, []},
30+
{"/trace-files/:node", rabbit_tracing_wm_files, []},
31+
{"/trace-files/:name", rabbit_tracing_wm_file, []},
32+
{"/trace-files/:node/:name", rabbit_tracing_wm_file, []}].
2833

2934
web_ui() -> [{javascript, <<"tracing.js">>}].

src/rabbit_tracing_util.erl

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,25 @@
11
-module(rabbit_tracing_util).
22

33
-export([coerce_env_value/2]).
4+
-export([apply_on_node/5]).
45

56
coerce_env_value(username, Val) -> rabbit_data_coercion:to_binary(Val);
67
coerce_env_value(password, Val) -> rabbit_data_coercion:to_binary(Val);
78
coerce_env_value(_, Val) -> Val.
9+
10+
apply_on_node(ReqData, Context, Mod, Fun, Args) ->
11+
case rabbit_mgmt_util:id(node, ReqData) of
12+
none ->
13+
apply(Mod, Fun, Args);
14+
Node0 ->
15+
Node = binary_to_atom(Node0, utf8),
16+
case rpc:call(Node, Mod, Fun, Args) of
17+
{badrpc, _} = Error ->
18+
Msg = io_lib:format("Node ~p could not be contacted: ~p",
19+
[Node, Error]),
20+
rabbit_log:warning(Msg, []),
21+
rabbit_mgmt_util:bad_request(list_to_binary(Msg), ReqData, Context);
22+
Any ->
23+
Any
24+
end
25+
end.

src/rabbit_tracing_wm_file.erl

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
-export([init/2, resource_exists/2, serve/2, content_types_provided/2,
1919
is_authorized/2, allowed_methods/2, delete_resource/2]).
20+
-export([serve/1]).
2021

2122
-include_lib("rabbitmq_management_agent/include/rabbit_mgmt_records.hrl").
2223

@@ -33,16 +34,26 @@ allowed_methods(ReqData, Context) ->
3334

3435
resource_exists(ReqData, Context) ->
3536
Name = rabbit_mgmt_util:id(name, ReqData),
36-
{rabbit_tracing_files:exists(Name), ReqData, Context}.
37+
Exists = rabbit_tracing_util:apply_on_node(ReqData, Context, rabbit_tracing_files,
38+
exists, [Name]),
39+
{Exists, ReqData, Context}.
3740

3841
serve(ReqData, Context) ->
3942
Name = rabbit_mgmt_util:id(name, ReqData),
40-
{ok, Content} = file:read_file(rabbit_tracing_files:full_path(Name)),
43+
Content = rabbit_tracing_util:apply_on_node(ReqData, Context,
44+
rabbit_tracing_wm_file,
45+
serve, [Name]),
4146
{Content, ReqData, Context}.
4247

48+
serve(Name) ->
49+
Path = rabbit_tracing_files:full_path(Name),
50+
{ok, Content} = file:read_file(Path),
51+
Content.
52+
4353
delete_resource(ReqData, Context) ->
4454
Name = rabbit_mgmt_util:id(name, ReqData),
45-
ok = rabbit_tracing_files:delete(Name),
55+
ok = rabbit_tracing_util:apply_on_node(ReqData, Context, rabbit_tracing_files,
56+
delete, [Name]),
4657
{true, ReqData, Context}.
4758

4859
is_authorized(ReqData, Context) ->

src/rabbit_tracing_wm_files.erl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,9 @@ content_types_provided(ReqData, Context) ->
2929
{[{<<"application/json">>, to_json}], ReqData, Context}.
3030

3131
to_json(ReqData, Context) ->
32-
rabbit_mgmt_util:reply(rabbit_tracing_files:list(), ReqData, Context).
32+
List = rabbit_tracing_util:apply_on_node(ReqData, Context,
33+
rabbit_tracing_files, list, []),
34+
rabbit_mgmt_util:reply(List, ReqData, Context).
3335

3436
is_authorized(ReqData, Context) ->
3537
rabbit_mgmt_util:is_authorized_admin(ReqData, Context).

src/rabbit_tracing_wm_trace.erl

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,13 @@ allowed_methods(ReqData, Context) ->
4242
{[<<"HEAD">>, <<"GET">>, <<"PUT">>, <<"DELETE">>], ReqData, Context}.
4343

4444
resource_exists(ReqData, Context) ->
45-
{case trace(ReqData) of
45+
{case trace(ReqData, Context) of
4646
not_found -> false;
4747
_ -> true
4848
end, ReqData, Context}.
4949

5050
to_json(ReqData, Context) ->
51-
rabbit_mgmt_util:reply(trace(ReqData), ReqData, Context).
51+
rabbit_mgmt_util:reply(trace(ReqData, Context), ReqData, Context).
5252

5353
accept_content(ReqData0, Ctx) ->
5454
case rabbit_mgmt_util:vhost(ReqData0) of
@@ -59,9 +59,10 @@ accept_content(ReqData0, Ctx) ->
5959
rabbit_mgmt_util:with_decode(
6060
[format, pattern], ReqData0, Ctx,
6161
fun([_, _], Trace, ReqData) ->
62-
Fs = [fun val_payload_bytes/3, fun val_format/3,
63-
fun val_create/3],
64-
case lists:foldl(fun (F, ok) -> F(VHost, Name, Trace);
62+
Fs = [fun val_payload_bytes/5, fun val_format/5,
63+
fun val_create/5],
64+
case lists:foldl(fun (F, ok) -> F(ReqData, Ctx, VHost,
65+
Name, Trace);
6566
(_F, Err) -> Err
6667
end, ok, Fs) of
6768
ok -> {true, ReqData, Ctx};
@@ -74,36 +75,40 @@ accept_content(ReqData0, Ctx) ->
7475

7576
delete_resource(ReqData, Context) ->
7677
VHost = rabbit_mgmt_util:vhost(ReqData),
77-
Name = rabbit_mgmt_util:id(name, ReqData),
78-
ok = rabbit_tracing_traces:stop(VHost, Name),
78+
rabbit_tracing_util:apply_on_node(ReqData, Context, rabbit_tracing_traces, stop,
79+
[VHost, rabbit_mgmt_util:id(name, ReqData)]),
7980
{true, ReqData, Context}.
8081

8182
is_authorized(ReqData, Context) ->
8283
rabbit_mgmt_util:is_authorized_admin(ReqData, Context).
8384

8485
%%--------------------------------------------------------------------
8586

86-
trace(ReqData) ->
87+
trace(ReqData, Context) ->
8788
case rabbit_mgmt_util:vhost(ReqData) of
8889
not_found -> not_found;
89-
VHost -> rabbit_tracing_traces:lookup(
90-
VHost, rabbit_mgmt_util:id(name, ReqData))
90+
VHost ->
91+
Name = rabbit_mgmt_util:id(name, ReqData),
92+
rabbit_tracing_util:apply_on_node(ReqData, Context, rabbit_tracing_traces,
93+
lookup, [VHost, Name])
9194
end.
9295

93-
val_payload_bytes(_VHost, _Name, Trace) ->
96+
val_payload_bytes(_ReqData, _Context, _VHost, _Name, Trace) ->
9497
case is_integer(maps:get(max_payload_bytes, Trace, 0)) of
9598
false -> <<"max_payload_bytes not integer">>;
9699
true -> ok
97100
end.
98101

99-
val_format(_VHost, _Name, Trace) ->
102+
val_format(_ReqData, _Context, _VHost, _Name, Trace) ->
100103
case lists:member(maps:get(format, Trace), [<<"json">>, <<"text">>]) of
101104
false -> <<"format not json or text">>;
102105
true -> ok
103106
end.
104107

105-
val_create(VHost, Name, Trace) ->
106-
case rabbit_tracing_traces:create(VHost, Name, maps:to_list(Trace)) of
108+
val_create(ReqData, Context, VHost, Name, Trace) ->
109+
case rabbit_tracing_util:apply_on_node(
110+
ReqData, Context, rabbit_tracing_traces, create,
111+
[VHost, Name, maps:to_list(Trace)]) of
107112
{ok, _} -> ok;
108113
_ -> ?ERR
109114
end.

src/rabbit_tracing_wm_traces.erl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,9 @@ content_types_provided(ReqData, Context) ->
2929
{[{<<"application/json">>, to_json}], ReqData, Context}.
3030

3131
to_json(ReqData, Context) ->
32-
rabbit_mgmt_util:reply(rabbit_tracing_traces:list(), ReqData, Context).
32+
List = rabbit_tracing_util:apply_on_node(ReqData, Context,
33+
rabbit_tracing_traces, list, []),
34+
rabbit_mgmt_util:reply(List, ReqData, Context).
3335

3436
is_authorized(ReqData, Context) ->
3537
rabbit_mgmt_util:is_authorized_admin(ReqData, Context).

0 commit comments

Comments
 (0)