2013-01-16 17:47:23 +00:00
|
|
|
#!/usr/bin/env escript
|
2011-01-31 16:43:31 +00:00
|
|
|
%% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*-
|
2011-01-21 15:12:09 +00:00
|
|
|
%% ex: ft=erlang ts=4 sw=4 et
|
2010-01-08 19:16:57 +00:00
|
|
|
%% -------------------------------------------------------------------
|
|
|
|
%%
|
|
|
|
%% nodetool: Helper Script for interacting with live nodes
|
|
|
|
%%
|
|
|
|
%% -------------------------------------------------------------------
|
|
|
|
main(Args) ->
|
2011-01-20 17:56:42 +00:00
|
|
|
ok = start_epmd(),
|
2010-01-08 19:16:57 +00:00
|
|
|
%% Extract the args
|
|
|
|
{RestArgs, TargetNode} = process_args(Args, [], undefined),
|
|
|
|
|
2013-01-16 17:47:23 +00:00
|
|
|
%% any commands that don't need a running node
|
|
|
|
case RestArgs of
|
|
|
|
["chkconfig", File] ->
|
|
|
|
case file:consult(File) of
|
|
|
|
{ok, _} ->
|
|
|
|
io:format("ok\n"),
|
|
|
|
halt(0);
|
|
|
|
{error, {Line, Mod, Term}} ->
|
|
|
|
io:format(standard_error, ["Error on line ",
|
|
|
|
file:format_error({Line, Mod, Term}), "\n"], []),
|
|
|
|
halt(1);
|
|
|
|
{error, R} ->
|
|
|
|
io:format(standard_error, ["Error reading config file: ",
|
|
|
|
file:format_error(R), "\n"], []),
|
|
|
|
halt(1)
|
|
|
|
end;
|
|
|
|
_ ->
|
|
|
|
ok
|
|
|
|
end,
|
|
|
|
|
2010-01-08 19:16:57 +00:00
|
|
|
%% See if the node is currently running -- if it's not, we'll bail
|
2013-01-16 17:47:23 +00:00
|
|
|
case {net_kernel:hidden_connect_node(TargetNode),
|
|
|
|
net_adm:ping(TargetNode)} of
|
2010-08-18 20:49:51 +00:00
|
|
|
{true, pong} ->
|
2010-01-08 19:16:57 +00:00
|
|
|
ok;
|
2013-01-16 17:47:23 +00:00
|
|
|
{false,pong} ->
|
|
|
|
io:format("Failed to connect to node ~p .\n", [TargetNode]),
|
|
|
|
halt(1);
|
2010-08-18 20:49:51 +00:00
|
|
|
{_, pang} ->
|
2010-01-08 19:16:57 +00:00
|
|
|
io:format("Node ~p not responding to pings.\n", [TargetNode]),
|
|
|
|
halt(1)
|
|
|
|
end,
|
|
|
|
|
|
|
|
case RestArgs of
|
2013-01-16 17:47:23 +00:00
|
|
|
["getpid"] ->
|
|
|
|
io:format("~p\n",
|
|
|
|
[list_to_integer(rpc:call(TargetNode, os, getpid, []))]);
|
2010-01-08 19:16:57 +00:00
|
|
|
["ping"] ->
|
2013-01-16 17:47:23 +00:00
|
|
|
%% If we got this far, the node already responsed to a
|
|
|
|
%% ping, so just dump a "pong"
|
2010-01-08 19:16:57 +00:00
|
|
|
io:format("pong\n");
|
|
|
|
["stop"] ->
|
|
|
|
io:format("~p\n", [rpc:call(TargetNode, init, stop, [], 60000)]);
|
|
|
|
["restart"] ->
|
|
|
|
io:format("~p\n", [rpc:call(TargetNode, init, restart, [], 60000)]);
|
|
|
|
["reboot"] ->
|
|
|
|
io:format("~p\n", [rpc:call(TargetNode, init, reboot, [], 60000)]);
|
|
|
|
["rpc", Module, Function | RpcArgs] ->
|
2013-01-16 17:47:23 +00:00
|
|
|
case rpc:call(TargetNode,
|
|
|
|
list_to_atom(Module),
|
|
|
|
list_to_atom(Function),
|
2011-01-21 15:11:07 +00:00
|
|
|
[RpcArgs], 60000) of
|
2010-01-08 19:16:57 +00:00
|
|
|
ok ->
|
|
|
|
ok;
|
|
|
|
{badrpc, Reason} ->
|
|
|
|
io:format("RPC to ~p failed: ~p\n", [TargetNode, Reason]),
|
|
|
|
halt(1);
|
|
|
|
_ ->
|
|
|
|
halt(1)
|
|
|
|
end;
|
2013-01-16 17:47:23 +00:00
|
|
|
["rpc_infinity", Module, Function | RpcArgs] ->
|
|
|
|
case rpc:call(TargetNode,
|
|
|
|
list_to_atom(Module),
|
|
|
|
list_to_atom(Function),
|
|
|
|
[RpcArgs], infinity) of
|
|
|
|
ok ->
|
|
|
|
ok;
|
|
|
|
{badrpc, Reason} ->
|
|
|
|
io:format("RPC to ~p failed: ~p\n", [TargetNode, Reason]),
|
|
|
|
halt(1);
|
|
|
|
_ ->
|
|
|
|
halt(1)
|
|
|
|
end;
|
2011-01-21 15:11:07 +00:00
|
|
|
["rpcterms", Module, Function, ArgsAsString] ->
|
2013-01-16 17:47:23 +00:00
|
|
|
case rpc:call(TargetNode,
|
|
|
|
list_to_atom(Module),
|
|
|
|
list_to_atom(Function),
|
2011-01-21 15:11:07 +00:00
|
|
|
consult(ArgsAsString), 60000) of
|
|
|
|
{badrpc, Reason} ->
|
|
|
|
io:format("RPC to ~p failed: ~p\n", [TargetNode, Reason]),
|
|
|
|
halt(1);
|
|
|
|
Other ->
|
|
|
|
io:format("~p\n", [Other])
|
|
|
|
end;
|
2014-10-20 07:40:43 +00:00
|
|
|
["eval", Str0] ->
|
|
|
|
Str = string:strip(Str0, right, $.) ++ ".",
|
|
|
|
Bindings = erl_eval:new_bindings(),
|
|
|
|
case rpc:call(TargetNode,
|
|
|
|
erl_eval,
|
|
|
|
exprs,
|
|
|
|
[parse(Str), Bindings],
|
|
|
|
60000) of
|
|
|
|
{badrpc, Reason} ->
|
|
|
|
io:format("RPC to ~p failed: ~p\n", [TargetNode, Reason]),
|
|
|
|
halt(1);
|
|
|
|
{value, Value, _Bindings} ->
|
|
|
|
io:format("~p\n", [Value])
|
|
|
|
end;
|
2015-01-20 07:18:10 +00:00
|
|
|
["upgrade", ReleasePackage] ->
|
|
|
|
%% TODO: This script currently does NOT support slim releases.
|
|
|
|
%% Necessary steps to upgrade a slim release are as follows:
|
|
|
|
%% 1. unpack relup archive manually
|
|
|
|
%% 2. copy releases directory and necessary libraries
|
|
|
|
%% 3. using release_hander:set_unpacked/2 .
|
|
|
|
%% For more details, see https://github.com/rebar/rebar/pull/52
|
|
|
|
%% and https://github.com/rebar/rebar/issues/202
|
|
|
|
{ok, Vsn} = rpc:call(TargetNode, release_handler, unpack_release,
|
|
|
|
[ReleasePackage], 60000),
|
|
|
|
io:format("Unpacked Release ~p\n", [Vsn]),
|
|
|
|
{ok, OtherVsn, Desc} = rpc:call(TargetNode, release_handler,
|
|
|
|
check_install_release, [Vsn], 60000),
|
|
|
|
{ok, OtherVsn, Desc} = rpc:call(TargetNode, release_handler,
|
|
|
|
install_release, [Vsn], 60000),
|
|
|
|
io:format("Installed Release ~p\n", [Vsn]),
|
|
|
|
ok = rpc:call(TargetNode, release_handler, make_permanent, [Vsn], 60000),
|
|
|
|
io:format("Made Release ~p Permanent\n", [Vsn]);
|
2010-01-08 19:16:57 +00:00
|
|
|
Other ->
|
|
|
|
io:format("Other: ~p\n", [Other]),
|
2015-01-20 07:18:10 +00:00
|
|
|
io:format("Usage: nodetool {chkconfig|getpid|ping|stop|restart|reboot|rpc|rpc_infinity|rpcterms|eval|upgrade}\n")
|
2010-01-08 19:16:57 +00:00
|
|
|
end,
|
|
|
|
net_kernel:stop().
|
|
|
|
|
|
|
|
process_args([], Acc, TargetNode) ->
|
|
|
|
{lists:reverse(Acc), TargetNode};
|
|
|
|
process_args(["-setcookie", Cookie | Rest], Acc, TargetNode) ->
|
|
|
|
erlang:set_cookie(node(), list_to_atom(Cookie)),
|
|
|
|
process_args(Rest, Acc, TargetNode);
|
|
|
|
process_args(["-name", TargetName | Rest], Acc, _) ->
|
|
|
|
ThisNode = append_node_suffix(TargetName, "_maint_"),
|
|
|
|
{ok, _} = net_kernel:start([ThisNode, longnames]),
|
|
|
|
process_args(Rest, Acc, nodename(TargetName));
|
|
|
|
process_args(["-sname", TargetName | Rest], Acc, _) ->
|
|
|
|
ThisNode = append_node_suffix(TargetName, "_maint_"),
|
|
|
|
{ok, _} = net_kernel:start([ThisNode, shortnames]),
|
|
|
|
process_args(Rest, Acc, nodename(TargetName));
|
|
|
|
process_args([Arg | Rest], Acc, Opts) ->
|
|
|
|
process_args(Rest, [Arg | Acc], Opts).
|
|
|
|
|
|
|
|
|
2011-01-20 17:56:42 +00:00
|
|
|
start_epmd() ->
|
|
|
|
[] = os:cmd(epmd_path() ++ " -daemon"),
|
|
|
|
ok.
|
|
|
|
|
|
|
|
epmd_path() ->
|
|
|
|
ErtsBinDir = filename:dirname(escript:script_name()),
|
|
|
|
Name = "epmd",
|
|
|
|
case os:find_executable(Name, ErtsBinDir) of
|
|
|
|
false ->
|
|
|
|
case os:find_executable(Name) of
|
|
|
|
false ->
|
|
|
|
io:format("Could not find epmd.~n"),
|
|
|
|
halt(1);
|
|
|
|
GlobalEpmd ->
|
|
|
|
GlobalEpmd
|
|
|
|
end;
|
|
|
|
Epmd ->
|
|
|
|
Epmd
|
|
|
|
end.
|
|
|
|
|
|
|
|
|
2010-01-08 19:16:57 +00:00
|
|
|
nodename(Name) ->
|
|
|
|
case string:tokens(Name, "@") of
|
|
|
|
[_Node, _Host] ->
|
|
|
|
list_to_atom(Name);
|
|
|
|
[Node] ->
|
|
|
|
[_, Host] = string:tokens(atom_to_list(node()), "@"),
|
|
|
|
list_to_atom(lists:concat([Node, "@", Host]))
|
|
|
|
end.
|
|
|
|
|
|
|
|
append_node_suffix(Name, Suffix) ->
|
|
|
|
case string:tokens(Name, "@") of
|
|
|
|
[Node, Host] ->
|
|
|
|
list_to_atom(lists:concat([Node, Suffix, os:getpid(), "@", Host]));
|
|
|
|
[Node] ->
|
|
|
|
list_to_atom(lists:concat([Node, Suffix, os:getpid()]))
|
|
|
|
end.
|
2011-01-21 15:11:07 +00:00
|
|
|
|
|
|
|
|
|
|
|
%%
|
|
|
|
%% Given a string or binary, parse it into a list of terms, ala file:consult/0
|
|
|
|
%%
|
|
|
|
consult(Str) when is_list(Str) ->
|
|
|
|
consult([], Str, []);
|
|
|
|
consult(Bin) when is_binary(Bin)->
|
|
|
|
consult([], binary_to_list(Bin), []).
|
|
|
|
|
|
|
|
consult(Cont, Str, Acc) ->
|
|
|
|
case erl_scan:tokens(Cont, Str, 0) of
|
|
|
|
{done, Result, Remaining} ->
|
|
|
|
case Result of
|
|
|
|
{ok, Tokens, _} ->
|
|
|
|
{ok, Term} = erl_parse:parse_term(Tokens),
|
|
|
|
consult([], Remaining, [Term | Acc]);
|
|
|
|
{eof, _Other} ->
|
|
|
|
lists:reverse(Acc);
|
|
|
|
{error, Info, _} ->
|
|
|
|
{error, Info}
|
|
|
|
end;
|
|
|
|
{more, Cont1} ->
|
|
|
|
consult(Cont1, eof, Acc)
|
|
|
|
end.
|
2014-10-20 07:40:43 +00:00
|
|
|
|
|
|
|
parse(Str) ->
|
|
|
|
{ok, Tokens, _} = erl_scan:string(Str),
|
|
|
|
{ok, Exprs} = erl_parse:parse_exprs(Tokens),
|
|
|
|
Exprs.
|