Skip to content
This repository has been archived by the owner on Sep 22, 2024. It is now read-only.

Commit

Permalink
Merge pull request #63 from mkacper/ets-cluster-view
Browse files Browse the repository at this point in the history
ETS cluster view
  • Loading branch information
Michal Slaski authored Jun 21, 2017
2 parents efb9c32 + 8bb867b commit 28d507f
Show file tree
Hide file tree
Showing 36 changed files with 1,406 additions and 8,772 deletions.
21 changes: 12 additions & 9 deletions apps/epl/src/epl.erl
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
-module(epl).
-include_lib("epl/include/epl.hrl").

-export([lookup/1,
-export([get_default_node/0,
lookup/1,
subscribe/0,
subscribe/1,
subscribe/2,
Expand All @@ -28,6 +29,12 @@
%% API functions
%% ===================================================================

%% @doc Gets default node passes as an argument to the script when starting
%% erlangpl.
-spec get_default_node() -> atom().
get_default_node() ->
proplists:get_value(node, lookup(node)).

%% @doc Lookups for given `Key' in epl_priv ets.
-spec lookup(Key :: term()) -> [tuple()].
lookup(Key) ->
Expand All @@ -39,9 +46,9 @@ lookup(Key) ->
%% gen_server.
-spec subscribe() -> [ok].
subscribe() ->
enable_dynamic_sub(),
Nodes = get_all_nodes(),
[subscribe(N) || N <- Nodes],
enable_dynamic_sub().
[subscribe(N) || N <- Nodes].

%% @doc Adds calling process to `Node' tracer's subscribers list.
-spec subscribe(Node :: atom() | default_node) -> ok.
Expand All @@ -59,9 +66,9 @@ subscribe(Node, Pid) ->
%% @doc Removes calling process from every epl_tracers' subscribers list.
-spec unsubscribe() -> [ok].
unsubscribe() ->
disable_dynamic_sub(),
Nodes = get_all_nodes(),
[unsubscribe(N) || N <- Nodes],
disable_dynamic_sub().
[unsubscribe(N) || N <- Nodes].

%% @doc Removes calling process from `Node' tracer's subscribers list.
-spec unsubscribe(Node :: atom() | default_node) -> ok.
Expand Down Expand Up @@ -159,10 +166,6 @@ log_prefix(error) -> "ERROR: ".
get_all_nodes() ->
erlang:nodes().

get_default_node() ->
[{node, Node}] = lookup(node),
Node.

enable_dynamic_sub() ->
epl_subs_manager:enable_dynamic_sub(self()).

Expand Down
7 changes: 7 additions & 0 deletions apps/epl/src/epl_app.erl
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
%% Application callbacks
-export([start/2, stop/1]).

%% Consts
-define(ERL_DISTR_TIMEOUT, 500).

%% ===================================================================
%% Application callbacks
%% ===================================================================
Expand Down Expand Up @@ -455,5 +458,9 @@ maybe_start_elixir(Args) ->
end.

start_epl_tracers() ->
wait_for_erl_distr(),
Nodes = erlang:nodes(),
[epl_tracer_sup:start_child([N]) || N <- Nodes].

wait_for_erl_distr() ->
timer:sleep(?ERL_DISTR_TIMEOUT).
106 changes: 16 additions & 90 deletions apps/epl/src/epl_traffic.erl
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@ handle_info({data, {Node, _Timestamp}, Proplist},
traffic = OldTraffic,
msg_pass = OldMsgPass}) ->

{Viz1, NewTraffic} = update_traffic_graph(Node, OldTraffic, new(Node)),
{Viz1, NewTraffic} = update_traffic_graph(Node, OldTraffic,
epl_viz_map:new(Node)),


%% We're starting from observed node which is our graph entry point
Expand Down Expand Up @@ -166,19 +167,20 @@ update_traffic_graph(EntryNode, OldCounters, Vizceral) ->

%% start creating a map, which represents the Vizceral JSON document
%% region named <<"INTERNET">> represents the observed node
V1 = push_region(EntryNode, Vizceral),
V1 = epl_viz_map:push_region(EntryNode, Vizceral),

%% add as many regions as there are nodes in the cluster
V2 = lists:foldl(fun({Node,_,_}, V) ->
push_region(binarify(Node), V)
epl_viz_map:push_region(epl_viz_map:binarify(Node),
V)
end, V1, NewCounters),

%% add links between "INTERNET" and all nodes
%% and compute delta between old and new net_kernel counters
V3 = lists:foldl(
fun({Node, NewIn, NewOut}, V) ->
{OldIn, OldOut} = get_in_out(Node, OldCounters),
push_region_connection(EntryNode, binarify(Node),
push_region_connection(EntryNode, epl_viz_map:binarify(Node),
{NewOut-OldOut, NewIn-OldIn, 0},
#{}, V)
end, V2, NewCounters),
Expand All @@ -203,101 +205,24 @@ command(Fun, Args) ->
%%%===================================================================
%%% functions manipulating Vizceral map
%%%===================================================================
new(EntryNode) ->
entity(global, "edge", [], #{connections => [], entryNode => EntryNode}).

entity(Renderer, Name, Nodes, Additional) ->
Map = #{
renderer => Renderer,
name => namify(Name),
displayName => binarify(Name),
nodes => Nodes
},
maps:merge(Map, Additional).

binarify(Name) when is_list(Name) ->
list_to_binary(Name);
binarify(Name) when is_atom(Name) ->
atom_to_binary(Name, latin1);
binarify(Name) when is_pid(Name) ->
list_to_binary(pid_to_list(Name));
binarify(Name) when is_port(Name) ->
list_to_binary(erlang:port_to_list(Name));
binarify(Name) when is_binary(Name) ->
Name.


namify(Name) when is_binary(Name) ->
Name1 = binary:replace(binarify(Name), <<"@">>, <<"_at_">>),
Name2 = binary:replace(binarify(Name1), <<"<">>, <<"">>),
Name3 = binary:replace(binarify(Name2), <<">">>, <<"">>),
binary:replace(binarify(Name3), <<".">>, <<"_">>, [global]);
namify(Name) ->
namify(binarify(Name)).



%% ---------------------- Regions ---------------------
push_region(Name, Vizceral) ->
push_region(Name, #{}, Vizceral).

push_region(Name, Additional, Vizceral) ->
%% We assume that INTERNET is entryNode for every region
%% Vizceral has backward compatibility and in region view INTERNET node is
%% default entryNode if other isn't specified
A = maps:merge(#{
connections => [],
maxVolume => 5000
},
Additional),
push_node(region, Name, A, Vizceral).

pull_region(Name, Vizceral) ->
{Region, Newlist} = pull_node(Name, Vizceral),
{Region, maps:merge(Vizceral, #{nodes => Newlist})}.

%% ---------------------- Nodes -----------------------
push_node(Renderer, Name, Additional, Entity) ->
#{nodes := Nodes} = Entity,
Newnode = entity(Renderer, Name, [], Additional),
maps:merge(Entity, #{nodes => [Newnode | Nodes]}).

pull_node(Name, Entity) ->
#{nodes := Nodes} = Entity,
{[Node], Rest} = lists:partition(
fun(A) ->
maps:get(name, A) == namify(Name)
end, Nodes),
{Node, Rest}.

%% ------------------- Connections --------------------
push_connection(Source, Target, {N, W, D} , Additional, To) ->
#{connections := Connections} = To,
New = maps:merge(Additional,
#{source => namify(Source),
target => namify(Target),
metrics => #{normal => N,
danger => D,
warning => W}
}),
maps:merge(To, #{connections => [New | Connections]}).

push_region_connection(Source, Target, {N, W, D}, Additional, Vizceral) ->
%% Will crash on nonexisting
pull_region(Source, Vizceral),
pull_region(Target, Vizceral),
epl_viz_map:pull_region(Source, Vizceral),
epl_viz_map:pull_region(Target, Vizceral),
%% Outgoing traffic
Viz = push_connection(Source, Target, {N, 0, D}, Additional, Vizceral),
Viz = epl_viz_map:push_connection(Source, Target, {N, 0, D}, Additional, Vizceral),
%% Incoming traffic
push_connection(Target, Source, {W, 0, D}, Additional, Viz).
epl_viz_map:push_connection(Target, Source, {W, 0, D}, Additional, Viz).

push_focused_connection(S, T, RN, NWD, Vizceral) ->
push_focused_connection(S, T, RN, NWD, #{}, Vizceral).

push_focused_connection(Source, Target, RegionName, {N, W, D}, A, Vizceral) ->
{Region, NewV} = pull_region(RegionName, Vizceral),
NewR = push_connection(Source, Target, {N,W,D}, A, Region),
push_region(RegionName, NewR, NewV).
{Region, NewV} = epl_viz_map:pull_region(RegionName, Vizceral),
NewR = epl_viz_map:push_connection(Source, Target, {N,W,D}, A, Region),
epl_viz_map:push_region(RegionName, NewR, NewV).

%% ---------------------- Focused ---------------------
push_focused(Name, Region, Vizceral) ->
Expand All @@ -307,7 +232,8 @@ push_focused(Name, RegionName, Additional, Vizceral) ->
#{nodes := Nodes} = Vizceral,
{[Region], Rest} = lists:partition(
fun(A) ->
maps:get(name, A) == namify(RegionName)
maps:get(name, A)
== epl_viz_map:namify(RegionName)
end, Nodes),
NewRegion = push_node(focusedChild, Name, Additional, Region),
NewRegion = epl_viz_map:push_node(focusedChild, Name, Additional, Region),
maps:merge(Vizceral, #{nodes => [NewRegion | Rest]}).
140 changes: 140 additions & 0 deletions apps/epl/src/epl_viz_map.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
%%%-------------------------------------------------------------------
%% @doc epl_viz_map module.
%% Provides API to create Vizceral map.
%% @end
%%%-------------------------------------------------------------------

-module(epl_viz_map).

%% API
-export([new/1,
push_region/2,
pull_region/2,
push_region/3,
push_node/4,
pull_node/2,
push_connection/5,
push_additional_node_info/3,
binarify/1,
namify/1]).

%% Types
-export_type([name/0]).

-type name() :: list() | atom() | pid() | port() | binary().

%%====================================================================
%% API functions
%%====================================================================

%% @doc Creates new Vizceral map.
-spec new(EntryNode :: binary()) -> map().
new(EntryNode) ->
entity(global, "edge", [], #{connections => [], entryNode => EntryNode}).

%% ---------------------- Regions ---------------------
%% @doc Pushes region into `Vizceral' map.
-spec push_region(Name :: name(), Vizceral :: map()) -> map().
push_region(Name, Vizceral) ->
push_region(Name, #{}, Vizceral).

%% @doc Pushes region with `Additional' information into `Vizceral' map.
-spec push_region(Name :: name(), Additional :: map(), Vizceral :: map()) ->
map().
push_region(Name, Additional, Vizceral) ->
%% We assume that INTERNET is entryNode for every region
%% Vizceral has backward compatibility and in region view INTERNET node is
%% default entryNode if other isn't specified
A = maps:merge(#{
connections => [],
maxVolume => 5000
},
Additional),
push_node(region, Name, A, Vizceral).

%% @doc Gets region's info from `Vizceral' map.
-spec pull_region(Name :: name(), Vizceral :: map()) -> {map(), map()}.
pull_region(Name, Vizceral) ->
{Region, Newlist} = pull_node(Name, Vizceral),
{Region, maps:merge(Vizceral, #{nodes => Newlist})}.

%% ---------------------- Nodes -----------------------
%% @doc Pushes node into Vizceral `Enity'.
-spec push_node(Renderer :: atom(), Name :: name(), Additional :: map(),
Entity :: map()) -> map().
push_node(Renderer, Name, Additional, Entity) ->
#{nodes := Nodes} = Entity,
Newnode = entity(Renderer, Name, [], Additional),
maps:merge(Entity, #{nodes => [Newnode | Nodes]}).

%% @doc Gets node's info from Vizceral `Entity'.
-spec pull_node(Name :: name(), Entity :: map()) -> {map(), list()}.
pull_node(Name, Entity) ->
#{nodes := Nodes} = Entity,
{[Node], Rest} = lists:partition(
fun(A) ->
maps:get(name, A) == namify(Name)
end, Nodes),
{Node, Rest}.

%% @doc Pushes additional `Info' into cluster nodes section in `Vizceral' map.
-spec push_additional_node_info(Info :: map(), Name :: atom(),
Vizceral :: #{nodes => [map()]}) -> map().
push_additional_node_info(Info, Name, Vizceral) ->
{Node, Rest} = pull_node(Name, Vizceral),
UpdatedNode = maps:merge(Node, Info),
maps:merge(Vizceral, #{nodes => [UpdatedNode | Rest]}).

%% ------------------- Connections --------------------
%% @doc Pushes connection section to `To'.
-spec push_connection(Source :: name(), Target :: name(), {N :: integer(),
W :: integer(),
D :: integer()},
Additional :: map(), To :: map()) -> map().
push_connection(Source, Target, {N, W, D}, Additional, To) ->
#{connections := Connections} = To,
New = maps:merge(Additional,
#{source => namify(Source),
target => namify(Target),
metrics => #{normal => N,
danger => D,
warning => W}
}),
maps:merge(To, #{connections => [New | Connections]}).

%%----------------------- Names -----------------------
%% @doc Transforms `Name' to binary.
-spec binarify(Name :: name()) -> binary().
binarify(Name) when is_list(Name) ->
list_to_binary(Name);
binarify(Name) when is_atom(Name) ->
atom_to_binary(Name, latin1);
binarify(Name) when is_pid(Name) ->
list_to_binary(pid_to_list(Name));
binarify(Name) when is_port(Name) ->
list_to_binary(erlang:port_to_list(Name));
binarify(Name) when is_binary(Name) ->
Name.

%% @doc Transforms `Name' to particular format.
-spec namify(Name :: name()) -> binary().
namify(Name) when is_binary(Name) ->
Name1 = binary:replace(binarify(Name), <<"@">>, <<"_at_">>),
Name2 = binary:replace(binarify(Name1), <<"<">>, <<"">>),
Name3 = binary:replace(binarify(Name2), <<">">>, <<"">>),
binary:replace(binarify(Name3), <<".">>, <<"_">>, [global]);
namify(Name) ->
namify(binarify(Name)).

%%====================================================================
%% Internals
%%====================================================================

entity(Renderer, Name, Nodes, Additional) ->
Map = #{
renderer => Renderer,
name => namify(Name),
displayName => binarify(Name),
nodes => Nodes
},
maps:merge(Map, Additional).
1 change: 1 addition & 0 deletions apps/epl/src/erlangpl.erl
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
main(_) ->
{ok, _} = application:ensure_all_started(epl),
{ok, _} = application:ensure_all_started(epl_st),
{ok, _} = application:ensure_all_started(epl_ets),

%% Start applications because escript can't take -boot argument
%% TODO: start sasl if escript run with debug flags
Expand Down
Loading

0 comments on commit 28d507f

Please sign in to comment.