API to restart a crashed vhost.

Administrator should be able to restart vhosts if they believe
it can be recovered.
Added buttons to vhost status tables and
the new HTTP API endpoint: /vhosts/:vhost/start/:node

Part of rabbitmq/rabbitmq-server#1321
[#149484305]
This commit is contained in:
Daniil Fedotov 2017-08-03 14:37:39 +01:00
parent 179e99bd93
commit 7f54319279
7 changed files with 113 additions and 13 deletions

View File

@ -235,6 +235,7 @@ table.two-col-layout > tbody > tr > td { width: 50%; vertical-align: top; }
input[type=submit], button { padding: 8px; border-radius: 5px; -moz-border-radius: 5px; color: black !important; text-decoration: none; cursor: pointer; font-weight: normal; }
table.list input[type=submit], table.list button { padding: 4px; }
table.mini input[type=submit], table.mini button { padding: 4px; }
input[type=submit], button {
background: #ddf;

View File

@ -282,6 +282,9 @@ dispatcher_add(function(sammy) {
}
}
});
sammy.post('#/restart_vhost', function(){
if(sync_post(this, '/vhosts/:vhost/start/:node')) update();
})
sammy.del('#/limits', function() {
if (sync_delete(this, '/vhost-limits/:vhost/:name')) update();
});

View File

@ -27,7 +27,15 @@
<% for (var node in vhost.cluster_state) { %>
<tr>
<th><%= fmt_escape_html(node) %> :</th>
<td><%= vhost.cluster_state[node] %></td>
<td><%= vhost.cluster_state[node] %>
<% if (vhost.cluster_state[node] == "stopped"){ %>
<form action="#/restart_vhost" method="post">
<input type="hidden" name="node" value="<%= node %>"/>
<input type="hidden" name="vhost" value="<%= vhost.name %>"/>
<input type="submit" value="Restart"/>
</form>
<% } %>
</td>
</tr>
<% } %>
</table>

View File

@ -69,7 +69,16 @@
%>
<tr>
<td><%= node %></td>
<td><%= state %></td>
<td>
<%= state %>
<% if (state == "stopped"){ %>
<form action="#/restart_vhost" method="post" class="confirm">
<input type="hidden" name="node" value="<%= node %>"/>
<input type="hidden" name="vhost" value="<%= vhost.name %>"/>
<input type="submit" value="Restart"/>
</form>
<% } %>
</td>
</tr>
<%
}

View File

@ -117,6 +117,7 @@ dispatcher() ->
{"/bindings/:vhost/e/:source/:dtype/:destination/:props", rabbit_mgmt_wm_binding, []},
{"/vhosts", rabbit_mgmt_wm_vhosts, []},
{"/vhosts/:vhost", rabbit_mgmt_wm_vhost, []},
{"/vhosts/:vhost/start/:node", rabbit_mgmt_wm_vhost_restart, []},
{"/vhosts/:vhost/permissions", rabbit_mgmt_wm_permissions_vhost, []},
{"/vhosts/:vhost/topic-permissions", rabbit_mgmt_wm_topic_permissions_vhost, []},
%% /connections/:connection is already taken, we cannot use our standard scheme here

View File

@ -0,0 +1,67 @@
%% 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 GoPivotal, Inc.
%% Copyright (c) 2011-2012 GoPivotal, Inc. All rights reserved.
%%
-module(rabbit_mgmt_wm_vhost_restart).
-export([init/3, rest_init/2, resource_exists/2, is_authorized/2,
allowed_methods/2, content_types_accepted/2, accept_content/2]).
-export([variances/2]).
-include_lib("rabbitmq_management_agent/include/rabbit_mgmt_records.hrl").
-include_lib("amqp_client/include/amqp_client.hrl").
%%--------------------------------------------------------------------
init(_, _, _) -> {upgrade, protocol, cowboy_rest}.
rest_init(Req, _Config) ->
{ok, rabbit_mgmt_cors:set_headers(Req, ?MODULE), #context{}}.
variances(Req, Context) ->
{[<<"accept-encoding">>, <<"origin">>], Req, Context}.
allowed_methods(ReqData, Context) ->
{[<<"POST">>, <<"OPTIONS">>], ReqData, Context}.
resource_exists(ReqData, Context) ->
VHost = id(ReqData),
{rabbit_vhost:exists(VHost), ReqData, Context}.
content_types_accepted(ReqData, Context) ->
{[{'*', accept_content}], ReqData, Context}.
accept_content(ReqData, Context) ->
VHost = id(ReqData),
NodeB = rabbit_mgmt_util:id(node, ReqData),
Node = binary_to_atom(NodeB, utf8),
case rabbit_vhost_sup_sup:start_vhost(VHost, Node) of
{ok, _} ->
{true, ReqData, Context};
{error, {already_started, _}} ->
{true, ReqData, Context};
{error, Err} ->
Message = io_lib:format("Request to node ~s failed with ~p",
[Node, Err]),
rabbit_mgmt_util:bad_request(list_to_binary(Message), ReqData, Context)
end.
is_authorized(ReqData, Context) ->
rabbit_mgmt_util:is_authorized_admin(ReqData, Context).
%%--------------------------------------------------------------------
id(ReqData) ->
rabbit_mgmt_util:id(vhost, ReqData).

View File

@ -334,6 +334,19 @@ vhosts_test(Config) ->
%% Check individually
assert_item(#{name => <<"/">>}, http_get(Config, "/vhosts/%2f", ?OK)),
assert_item(#{name => <<"myvhost">>},http_get(Config, "/vhosts/myvhost")),
%% Crash it
rabbit_ct_broker_helpers:force_vhost_failure(Config, <<"myvhost">>),
[NodeData] = http_get(Config, "/nodes"),
Node = binary_to_atom(maps:get(name, NodeData), utf8),
assert_item(#{name => <<"myvhost">>, cluster_state => #{Node => <<"stopped">>}},
http_get(Config, "/vhosts/myvhost")),
%% Restart it
http_post(Config, "/vhosts/myvhost/start/" ++ atom_to_list(Node), [], {group, '2xx'}),
assert_item(#{name => <<"myvhost">>, cluster_state => #{Node => <<"running">>}},
http_get(Config, "/vhosts/myvhost")),
%% Delete it
http_delete(Config, "/vhosts/myvhost", {group, '2xx'}),
%% It's not there
@ -344,17 +357,15 @@ vhosts_test(Config) ->
vhosts_trace_test(Config) ->
http_put(Config, "/vhosts/myvhost", none, {group, '2xx'}),
?assertMatch(#{name := <<"myvhost">>, tracing := false, cluster_state := _},
http_get(Config, "/vhosts/myvhost")),
Disabled = #{name => <<"myvhost">>, tracing => false},
Enabled = #{name => <<"myvhost">>, tracing => true},
assert_item(Disabled, http_get(Config, "/vhosts/myvhost")),
http_put(Config, "/vhosts/myvhost", [{tracing, true}], {group, '2xx'}),
?assertMatch(#{name := <<"myvhost">>, tracing := true, cluster_state := _},
http_get(Config, "/vhosts/myvhost")),
assert_item(Enabled, http_get(Config, "/vhosts/myvhost")),
http_put(Config, "/vhosts/myvhost", [{tracing, true}], {group, '2xx'}),
?assertMatch(#{name := <<"myvhost">>, tracing := true},
http_get(Config, "/vhosts/myvhost")),
assert_item(Enabled, http_get(Config, "/vhosts/myvhost")),
http_put(Config, "/vhosts/myvhost", [{tracing, false}], {group, '2xx'}),
?assertMatch(#{name := <<"myvhost">>, tracing := false},
http_get(Config, "/vhosts/myvhost")),
assert_item(Disabled, http_get(Config, "/vhosts/myvhost")),
http_delete(Config, "/vhosts/myvhost", {group, '2xx'}),
passed.
@ -809,9 +820,9 @@ queues_test(Config) ->
rabbit_ct_broker_helpers:force_vhost_failure(Config, <<"downvhost">>),
%% The vhost is down
#{name := <<"downvhost">>, tracing := false, cluster_state := DownClusterState} =
http_get(Config, "/vhosts/downvhost"),
?assertEqual([<<"stopped">>], lists:usort(maps:values(DownClusterState))),
Node = rabbit_ct_broker_helpers:get_node_config(Config, 0, nodename),
DownVHost = #{name => <<"downvhost">>, tracing => false, cluster_state => #{Node => <<"stopped">>}},
assert_item(DownVHost, http_get(Config, "/vhosts/downvhost")),
DownQueues = http_get(Config, "/queues/downvhost"),
DownQueue = http_get(Config, "/queues/downvhost/foo"),