[#11] adding Stats Collector

This commit is contained in:
Felipe Ripoll 2018-05-13 16:45:32 -06:00
parent 1815f1aabe
commit 16acef3f1a
5 changed files with 159 additions and 11 deletions

View File

@ -7,9 +7,7 @@ defmodule POAAgent.Entity.Ethereum.Statistics do
mining?: boolean,
hashrate: non_neg_integer,
peers: non_neg_integer,
pending: non_neg_integer,
gas_price: Literal.Decimal.t(),
block: POAAgent.Entity.Ethereum.Block.t(),
syncing?: boolean,
uptime: non_neg_integer
}
@ -19,9 +17,7 @@ defmodule POAAgent.Entity.Ethereum.Statistics do
mining?: nil,
hashrate: nil,
peers: nil,
pending: nil,
gas_price: nil,
block: %POAAgent.Entity.Ethereum.Block{},
syncing?: nil,
uptime: nil
]
@ -34,8 +30,7 @@ defmodule POAAgent.Entity.Ethereum.Statistics do
gas_price: :gasPrice,
syncing?: :syncing
]
x = Enum.reduce(mapping, x, &POAAgent.Entity.Name.change/2)
Map.update!(x, :block, &POAAgent.Entity.NameConvention.from_elixir_to_node/1)
Enum.reduce(mapping, x, &POAAgent.Entity.Name.change/2)
end
end
end

View File

@ -81,11 +81,14 @@ defmodule POAAgent.Plugins.Collector do
@doc """
A callback executed when the Collector Plugin starts.
The argument is retrieved from the configuration file when the Collector is defined
It must return `{:ok, state}`, that `state` will be keept as in `GenServer` and can be
It can return `{:ok, state}`, that `state` will be keept as in `GenServer` and can be
retrieved in the `collect/1` function.
There are some cases where we want to send data to the transfer after initialize the
Collector, if that is the case you must return `{:transfer, data, state}` where the data is the
metrics we want to send to the transfer
"""
@callback init_collector(args :: term()) ::
{:ok, any()}
@callback init_collector(args :: term()) :: {:ok, state :: any()}
| {:transfer, data :: any(), state :: any()}
@doc """
In this callback is where the metrics collection logic must be placed.
@ -113,7 +116,14 @@ defmodule POAAgent.Plugins.Collector do
@doc false
def init(state) do
{:ok, internal_state} = init_collector(state[:args])
internal_state =
case init_collector(state[:args]) do
{:ok, internal_state} ->
internal_state
{:transfer, _, _} = transfer ->
transfer |> transfer(state.label, state.transfers)
end
set_collector_timer(state.frequency)
{:ok, Map.put(state, :internal_state, internal_state)}
end

View File

@ -0,0 +1,103 @@
defmodule POAAgent.Plugins.Collectors.Eth.Stats do
use POAAgent.Plugins.Collector
@moduledoc """
This is a Collector's Plugin which makes requests to a Ethereum node in order to know if
the stats has been changed.
This Collector needs the url of the node to iteract. That url must be placed in the args field
in the config file. For example:
{:eth_stats, POAAgent.Plugins.Collectors.Eth.Stats, 5000, :eth_stats, [url: "http://localhost:8545"]}
In this example, the Collector will check with the Ethereum node every 5 seconds if the stats of the
node has changed. If that is the case it will send it to the Transfers
encapsulated in a `POAAgent.Entity.Ethereum.Statistics` struct
"""
@typep internal_state :: %{last_stats: POAAgent.Entity.Ethereum.Statistics.t | nil,
tries: non_neg_integer,
down: non_neg_integer}
@doc false
@spec init_collector(term()) :: {:ok, internal_state()}
def init_collector(args) do
:ok = config(args)
case get_stats(0, 0) do
{:ok, stats, tries, down} ->
{:transfer, stats, %{last_stats: stats, tries: tries, down: down}}
{:error, tries, down} ->
{:ok, %{last_stats: nil, tries: tries, down: down}}
end
end
@doc false
@spec collect(internal_state()) :: term()
def collect(%{last_stats: last_stats, tries: tries, down: down} = state) do
case get_stats(tries, down) do
{:ok, ^last_stats, tries, down} ->
{:notransfer, %{state | tries: tries, down: down}}
{:ok, stats, tries, down} ->
{:transfer, stats, %{state | last_stats: stats, tries: tries, down: down}}
{:error, tries, down} ->
{:notransfer, %{state | tries: tries, down: down}}
end
end
@doc false
@spec terminate(internal_state()) :: :ok
def terminate(_state) do
:ok
end
@doc false
defp config([url: url]) do
Application.put_env(:ethereumex, :url, url)
:ok
end
@doc false
defp get_stats(tries, down) do
tries = tries + 1
with {:ok, peers} <- Ethereumex.HttpClient.net_peer_count(),
{:ok, mining} <- Ethereumex.HttpClient.eth_mining(),
{:ok, hashrate} <- Ethereumex.HttpClient.eth_hashrate(),
{:ok, syncing} <- Ethereumex.HttpClient.eth_syncing(),
{:ok, gas_price} <- Ethereumex.HttpClient.eth_gas_price()
do
peers = String.to_integer(POAAgent.Format.Literal.Hex.decimalize(peers))
hashrate = String.to_integer(POAAgent.Format.Literal.Hex.decimalize(hashrate))
gas_price = String.to_integer(POAAgent.Format.Literal.Hex.decimalize(gas_price))
down =
if peers == 0 do
down + 1
else
down
end
stats =
%POAAgent.Entity.Ethereum.Statistics{
active?: peers > 0,
mining?: mining,
hashrate: hashrate,
peers: peers,
gas_price: gas_price,
syncing?: syncing,
uptime: uptime(tries, down)
}
{:ok, stats, tries, down}
else
_error -> {:error, tries, down}
end
end
defp uptime(tries, down) do
((tries - down) / tries) * 100
end
end

View File

@ -47,7 +47,8 @@ defmodule POAAgent.MixProject do
POAAgent.Plugins.Transfer,
],
"Ethereum Plugins": [
POAAgent.Plugins.Collectors.Eth.LatestBlock
POAAgent.Plugins.Collectors.Eth.LatestBlock,
POAAgent.Plugins.Collectors.Eth.Stats
]
]
]

View File

@ -0,0 +1,39 @@
defmodule POAAgent.Plugins.Collectors.Eth.StatsTest do
use ExUnit.Case
import Mock
test "stats sent to the transfer" do
with_mock Ethereumex.HttpClient, [
net_peer_count: fn() -> {:ok, "0x3"} end,
eth_mining: fn() -> {:ok, false} end,
eth_hashrate: fn() -> {:ok, "0x0"} end,
eth_syncing: fn() -> {:ok, syncing()} end,
eth_gas_price: fn() -> {:ok, "0x0"} end
] do
{:transfer, stats, _} = POAAgent.Plugins.Collectors.Eth.Stats.collect(%{last_stats: nil, tries: 0, down: 0})
assert stats == expected_stats()
end
end
def syncing() do
%{
"currentBlock" => "0x44ee0",
"highestBlock" => "0x44ee2",
"startingBlock" => "0x40ad3",
"warpChunksAmount" => nil,
"warpChunksProcessed" => nil
}
end
def expected_stats() do
%POAAgent.Entity.Ethereum.Statistics{
active?: true,
gas_price: 0,
hashrate: 0,
mining?: false,
peers: 3,
syncing?: syncing(),
uptime: 100.0}
end
end