Merge branch 'master' of github.com:processone/ejabberd-contrib

This commit is contained in:
Tom Quackenbush 2016-08-02 20:32:43 +00:00
commit 9359ce3b75
7 changed files with 271 additions and 21 deletions

35
mod_grafite/README.md Normal file
View File

@ -0,0 +1,35 @@
mod_grafite - Gathers statistics and publishes via statsd/grafite
author: Thiago Rocha Camargo (rochacamargothiago@gmail.com)
mod_grafite
==============
Gathers statistics and publishes via statsd/grafite
CONFIGURE
---------
Enable the module in ejabberd.yml for example with a basic configuration:
modules:
mod_grafite:
statsdhost: "carbonr.xmpp.com.br"
statsdport: 8125
Configurable options:
statsdhost: Host of the statsd server
statsdport: Port of the statsd server
EXAMPLE CONFIGURATION
---------------------
modules:
mod_grafite:
statsdhost: "carbonr.xmpp.com.br"
statsdport: 8125
FEATURE REQUESTS
----------------
- Add support for configurable Hooks

View File

@ -0,0 +1,5 @@
modules:
mod_grafite:
statsdhost: "carbonr.xmpp.com.br"
statsdport: 8125

View File

@ -0,0 +1,5 @@
author: "Thiago Rocha Camargo <rochacamargothiago at gmail.com>"
category: "statistics"
summary: "Publishes Statistics via statsd/grafite"
home: "https://github.com/processone/ejabberd-contrib/tree/master/"
url: "git@github.com:processone/ejabberd-contrib.git"

View File

@ -0,0 +1,201 @@
%%%----------------------------------------------------------------------
%%% File : mod_grafite.erl
%%% Author : Thiago Rocha Camargo
%%% Purpose : Gathers statistics and publishes via statsd/grafite
%%% Created :
%%% Id : $Id: mod_grafite.erl 0000 2016-07-11 16:42:30Z xmppjingle $
%%%----------------------------------------------------------------------
%%%% Definitions
-module(mod_grafite).
-author('rochacamargothiago@gmail.com').
-behaviour(gen_mod).
-include("ejabberd.hrl").
-include("logger.hrl").
-include("jlib.hrl").
-define(HOOKS, [offline_message_hook,
sm_register_connection_hook, sm_remove_connection_hook,
user_send_packet, user_receive_packet,
s2s_send_packet, s2s_receive_packet,
remove_user, register_user]).
-define(GLOBAL_HOOKS, [component_connected, component_disconnected]).
-export([start/2, stop/1, mod_opt_type/1,
depends/2, udp_loop_start/1, push/2]).
-export([offline_message_hook/3,
sm_register_connection_hook/3, sm_remove_connection_hook/3,
user_send_packet/4, user_receive_packet/5,
s2s_send_packet/3, s2s_receive_packet/3,
remove_user/2, register_user/2, component_connected/1,
component_disconnected/1]).
-record(state, {socket, host, port}).
-define(PROCNAME, ejabberd_mod_grafite).
-define(GRAFITE_KEY(Node, Host, Probe), "mod_grafite.ejabberd." ++
erlang:binary_to_list(Node) ++ "_" ++
erlang:binary_to_list(Host) ++ "." ++
erlang:atom_to_list(Probe)).
%%====================================================================
%% API
%%====================================================================
start(Host, Opts) ->
[ejabberd_hooks:add(Hook, Host, ?MODULE, Hook, 20)
|| Hook <- ?HOOKS],
[ejabberd_hooks:add(Hook, ?MODULE, Hook, 18)
|| Hook <- ?GLOBAL_HOOKS],
StatsDH = gen_mod:get_opt(statsdhost, Opts, fun(X) -> X end, "localhost"),
{ok, StatsDHost} = getaddrs(StatsDH),
StatsDPort = gen_mod:get_opt(statsdport, Opts, fun(X) -> X end, 8125),
register(?PROCNAME, spawn(?MODULE, udp_loop_start, [#state{host = StatsDHost, port = StatsDPort}])).
stop(Host) ->
[ejabberd_hooks:delete(Hook, Host, ?MODULE, Hook, 20)
|| Hook <- ?HOOKS],
[ejabberd_hooks:delete(Hook, Host, ?MODULE, Hook, 20)
|| Hook <- ?GLOBAL_HOOKS].
depends(_Host, _Opts) ->
[].
%%====================================================================
%% Hooks handlers
%%====================================================================
offline_message_hook(_From, #jid{lserver=LServer}, _Packet) ->
push(LServer, offline_message).
sm_register_connection_hook(_SID, #jid{lserver=LServer}, _Info) ->
push(LServer, sm_register_connection).
sm_remove_connection_hook(_SID, #jid{lserver=LServer}, _Info) ->
push(LServer, sm_remove_connection).
user_send_packet(Packet, _C2SState, #jid{lserver=LServer}, _To) ->
push(LServer, user_send_packet),
Packet.
user_receive_packet(Packet, _C2SState, _JID, _From, #jid{lserver=LServer}) ->
push(LServer, user_receive_packet),
Packet.
s2s_send_packet(#jid{lserver=LServer}, _To, _Packet) ->
push(LServer, s2s_send_packet).
s2s_receive_packet(_From, #jid{lserver=LServer}, _Packet) ->
push(LServer, s2s_receive_packet).
remove_user(_User, Server) ->
push(jid:nameprep(Server), remove_user).
register_user(_User, Server) ->
push(jid:nameprep(Server), register_user).
component_connected(Host) ->
push(Host, component_connected).
component_disconnected(Host) ->
push(Host, component_disconnected).
%%====================================================================
%% metrics push handler
%%====================================================================
push(Host, Probe) ->
Payload = encode_metrics(Host, Probe),
whereis(?PROCNAME) ! {send, Payload}.
encode_metrics(Host, Probe) ->
[_, NodeId] = str:tokens(jlib:atom_to_binary(node()), <<"@">>),
[Node | _] = str:tokens(NodeId, <<".">>),
Data = case Probe of
{Key, Val} ->
encode(gauge, ?GRAFITE_KEY(Node, Host, Probe), Val, 1.0);
Key ->
encode(gauge, ?GRAFITE_KEY(Node, Host, Probe), 1, 1.0)
end,
?INFO_MSG("Stats: ~p ~p ~n", [Data, encode(gauge, Key, 1, undefined)]),
Data.
%%====================================================================
%% Grafite/StatsD
%%====================================================================
encode(gauge, Key, Value, _SampleRate) ->
[Key, ":", format_value(Value), "|g"].
format_value(Value) when is_integer(Value) ->
integer_to_list(Value);
format_value(Value) when is_float(Value) ->
float_to_list(Value, [{decimals, 2}]).
%%====================================================================
%% UDP Utils
%%====================================================================
udp_loop_start(#state{}=S) ->
LocalPort = 44444,
case gen_udp:open(LocalPort) of
{ok, Socket} ->
?INFO_MSG("UDP Stats Socket Open: [~p]~n", [LocalPort]),
udp_loop(S#state{socket = Socket});
_ ->
?INFO_MSG("Could not start UDP Socket [~p]~n", [LocalPort])
end.
udp_loop(#state{} = S) ->
receive
{send, Packet} ->
send_udp(Packet, S),
udp_loop(S);
_ ->
udp_loop(S)
end.
send_udp(Payload, #state{socket = Socket, host = Host, port = Port} = State) ->
case gen_udp:send(Socket, Host, Port, Payload) of
ok ->
ok;
_Error ->
?INFO_MSG("UDP Send Failed: [~p] (~p)~n", [State, Payload])
end.
getaddrs({_, _, _, _} = Address) ->
{ok, Address};
getaddrs(Hostname) when is_binary(Hostname) ->
getaddrs(binary_to_list(Hostname));
getaddrs(Hostname) ->
case inet:getaddrs(Hostname, inet) of
{ok, Addrs} ->
{ok, random_element(Addrs)};
{error, Reason} ->
?ERROR_MSG("getaddrs error: ~p~n", [Reason]),
{error, Reason}
end.
random_element([Element]) ->
Element;
random_element([_|_] = List) ->
T = list_to_tuple(List),
Index = random(tuple_size(T)),
element(Index, T).
random(N) ->
erlang:phash2({self(), timestamp()}, N) + 1.
timestamp() ->
os:timestamp().
%%====================================================================
%% mod Options
%%====================================================================
mod_opt_type(statsdhost) -> fun(X) -> X end;
mod_opt_type(statsdport) -> fun(X) when is_integer(X) -> X end;
mod_opt_type(_) ->
[statsdhost, statsdport].

View File

@ -17,16 +17,16 @@ This module can also be used as a frontend to execute ejabberd commands.
CONFIGURATION
=============
To use this module, follow the general build instructions, and configure
in ejabberd.yml as described.
To use this module, follow the general build instructions.
You can modify the default module configuration file like this:
Enable the module:
To enable the module:
modules:
mod_rest:
allowed_ips:
- "127.0.0.1"
And enable the HTTP request handler in the listen section:
To enable the HTTP request handler in the listen section:
listen:
-
port: 5285
@ -40,7 +40,7 @@ With that configuration, you can send HTTP POST requests to the URL:
Configurable options:
allowed_ips: IP addresses that can use the rest service.
Allowed values: 'all' or a list of Erlang tuples.
Allowed values: 'all' or a list of Erlang strings.
Default value: all
Notice that the IP address is checked after the connection is established.
If you want to restrict the IP address that listens connections, and

View File

@ -29,8 +29,8 @@
-export([start/2,
stop/1,
split_line/1,
process/2
]).
process/2,
mod_opt_type/1]).
-include("ejabberd.hrl").
-include("logger.hrl").
@ -127,17 +127,7 @@ check_stanza(Stanza, _From, To, Host) ->
check_member_option(Host, ClientIp, allowed_ips) ->
true = case try_get_option(Host, allowed_ips, all) of
all -> true;
AllowedValues ->
case lists:all(fun(El) -> is_binary(El) end, AllowedValues) of
true ->
AllowedIps = lists:map(fun(El) ->
binary_to_ip_tuple(El)
end,
AllowedValues),
lists:member(ClientIp, AllowedIps);
false ->
lists:member(ClientIp, AllowedValues)
end
AllowedValues -> ip_matches(ClientIp, AllowedValues)
end;
check_member_option(Host, Element, Option) ->
true = case try_get_option(Host, Option, all) of
@ -145,9 +135,12 @@ check_member_option(Host, Element, Option) ->
AllowedValues -> lists:member(Element, AllowedValues)
end.
binary_to_ip_tuple(IpAddress) when is_binary(IpAddress) ->
{ok, IpTuple} = inet_parse:address(binary_to_list(IpAddress)),
IpTuple.
ip_matches(ClientIp, AllowedValues) ->
lists:any(fun(El) ->
{ok, Net, Mask} = acl:parse_ip_netmask(El),
acl:acl_rule_matches({ip,{Net,Mask}}, #{ip => {ClientIp,port}}, host)
end,
AllowedValues).
post_request(Stanza, From, To) ->
case ejabberd_router:route(From, To, Stanza) of
@ -179,3 +172,14 @@ splitend([92, 34], Res) -> {"", Res};
splitend([34, 32 | Line], Res) -> {Line, Res};
splitend([92, 34, 32 | Line], Res) -> {Line, Res};
splitend([Char | Line], Res) -> splitend(Line, [Char | Res]).
mod_opt_type(allowed_ips) ->
fun (all) -> all; (A) when is_list(A) -> A end;
mod_opt_type(allowed_destinations) ->
fun (all) -> all; (A) when is_list(A) -> A end;
mod_opt_type(allowed_stanza_types) ->
fun (all) -> all; (A) when is_list(A) -> A end;
mod_opt_type(access_commands) ->
fun (A) when is_list(A) -> A end;
mod_opt_type(_) ->
[allowed_ips, allowed_destinations, allowed_stanza_types, access_commands].