Packages

Web based metrics, monitoring, and observer.

Current section

Files

Jump to
wobserver_ng lib wobserver util node discovery.ex
Raw

lib/wobserver/util/node/discovery.ex

defmodule Wobserver.Util.Node.Discovery do
@moduledoc ~S"""
Helps discovering other nodes to connect to.
The method used can be set in the config file by setting:
```elixir
config :wobserver,
discovery: :dns,
```
The following methods can be used: (default: `:none`)
- `:none`, just returns the local node.
- `:dns`, use DNS to search for other nodes.
The option `discovery_search` needs to be set to filter entries.
- `:custom`, a function as String.
# Example config
No discovery: (is default)
```elixir
config :wobserver,
port: 80,
discovery: :none
```
Using dns as discovery service:
```elixir
config :wobserver,
port: 80,
discovery: :dns,
discovery_search: "google.nl"
```
Using a custom function:
```elixir
config :wobserver,
port: 80,
discovery: :custom,
discovery_search: &MyApp.CustomDiscovery.discover/0
```
Using a anonymous function:
```elixir
config :wobserver,
port: 80,
discovery: :custom,
discovery_search: fn -> [] end
```
"""
alias Wobserver.Util.Node.Remote
# Finding
@doc ~S"""
Searches for a node in the discovered nodes.
The `search` will be used to filter through names, hosts and host:port combinations.
The special values for search are:
- `local`, will always return the local node.
"""
@spec find(String.t()) ::
:local
| :unknown
| {:remote, Remote.t()}
def find("local"), do: :local
def find(search) do
found_node =
Enum.find(discover(), fn %{name: name, host: host, port: port} ->
search == name ||
search == host ||
search == "#{host}:#{port}"
end)
cond do
found_node == nil -> :unknown
local?(found_node) -> :local
true -> {:remote, found_node}
end
end
@doc ~S"""
Retuns the local node.
"""
def local do
discover()
|> Enum.find(fn %{local?: is_local} -> is_local end)
end
# Discoverying
@doc ~S"""
Discovers other nodes to connect to.
The method used can be set in the config file by setting:
config :wobserver,
discovery: :dns,
The following methods can be used: (default: `:none`)
- `:none`, just returns the local node.
- `:dns`, use DNS to search for other nodes.
The option `discovery_search` needs to be set to filter entries.
- `:custom`, a function as String.
# Example config
No discovery: (is default)
config :wobserver,
discovery: :none
Using dns:
config :wobserver,
discovery: :dns,
discovery_search: "google.nl"
Using a custom function:
config :wobserver,
discovery: :custom,
discovery_search: "&MyApp.CustomDiscovery.discover/0"
"""
@spec discover :: list(Remote.t())
def discover do
nodes =
:wobserver
|> Application.get_env(:discovery, :none)
|> discovery_call
case Enum.find(nodes, fn %{local?: is_local} -> is_local end) do
nil -> discovery_call(:none) ++ nodes
_ -> nodes
end
end
@spec discovery_call(:dns) :: list(Remote.t())
defp discovery_call(:dns),
do: dns_discover(Application.get_env(:wobserver, :discovery_search, nil))
@spec discovery_call(:custom) :: list(Remote.t())
defp discovery_call(:custom) do
method = Application.get_env(:wobserver, :discovery_search, fn -> [] end)
cond do
is_binary(method) ->
{call, []} = Code.eval_string(method)
call.()
is_function(method) ->
method.()
true ->
[]
end
end
@spec discovery_call(:none) :: list(Remote.t())
defp discovery_call(:none) do
[
%Remote{
name: get_local_ip(),
host: get_local_ip(),
port: WobserverNG.Application.port(),
local?: true
}
]
end
@spec dns_discover(search :: String.t()) :: list(Remote.t())
defp dns_discover(search) when is_binary(search) do
search
|> String.to_charlist()
|> :inet_res.lookup(:in, :a)
|> Enum.map(&dns_to_node/1)
end
defp dns_discover(_), do: []
# Helpers
defp local?(%{name: "local"}), do: true
defp local?(%{host: "127.0.0.1", port: port}),
do: port == WobserverNG.Application.port()
defp local?(%{host: ip, port: port}),
do: ip == get_local_ip() && port == WobserverNG.Application.port()
defp local?(_), do: false
defp ip_to_string({ip1, ip2, ip3, ip4}), do: "#{ip1}.#{ip2}.#{ip3}.#{ip4}"
defp dns_to_node(ip) do
remote_ip = ip_to_string(ip)
remote = %Remote{
name: remote_ip,
host: remote_ip,
port: WobserverNG.Application.port(),
local?: false
}
%Remote{remote | local?: local?(remote)}
end
defp get_local_ip do
# with {:ok, ips} <- :inet.getif(),
# {ip, _, _} <- List.first(ips),
# do: ip_to_string(ip)
{:ok, ips} = :inet.getif()
{ip, _, _} = List.first(ips)
ip_to_string(ip)
end
end