From 7c212cefe9fdc4a3a788859d1298729f6fb8e5b9 Mon Sep 17 00:00:00 2001
From: Rickard Green
As from Kernel version 2.11.4, and ERTS version - 5.5.4, the following is guaranteed:
-Notice that this is not guaranteed for Kernel - versions before 2.11.4.
-As from Kernel version 2.11.4, subscriptions can also be
- made before the
As from Kernel version 2.13, and ERTS version - 5.7, the following is guaranteed:
-Delivery guarantees of
+
+
+ As of OTP @OTP-16362@, a
Notice that this is not guaranteed for Kernel - versions before 2.13.
The format of the node status change messages depends on
+ the empty list or if net_kernel:monitor_nodes/1 is called,
+ the format is as follows:
+
{nodeup, Node} | {nodedown, Node}
Node = node()
- If Options is not [] , the format is
- as follows:
-
-{nodeup, Node, InfoList} | {nodedown, Node, InfoList}
+
+ When Options is the empty map or empty
+ list, the caller will only subscribe for status change messages
+ for visible nodes. That is, only nodes that appear in the
+ result of
+ erlang:nodes/0 .
+
+
+ If Options equals anything other than the
+ empty list, the format of the status change messages is as follows:
+
+
+{nodeup, Node, Info} | {nodedown, Node, Info}
Node = node()
- InfoList = [{Tag, Val}]
- InfoList is a list of tuples. Its contents depends on
- Options , see below.
- Also, when OptionList == [] , only visible nodes, that
- is, nodes that appear in the result of
- erlang:nodes/0 ,
- are monitored.
- Option can be any of the following:
+ Info = #{Tag => Val} | [{Tag, Val}]
+
+ Info is either a map or a list of 2-tuples. Its content
+ depends on Options . If Options
+ is a map, Info will also be a map. If Options
+ is a list, Info will also be a list.
+
+
+ When Options is a map, currently
+ the following associations are allowed:
+
- {node_type, NodeType}
+ connection_id => boolean()
+ -
+
+ If the value of the association equals true , a
+ connection_id => ConnectionId association will be
+ included in the Info map where ConnectionId
+ is the connection identifier of the connection coming up
+ or going down. For more info about this connection
+ identifier see the documentation of
+ erlang:nodes/2 .
+
+
+ node_type => NodeType
-
Valid values for NodeType :
visible
Subscribe to node status change messages for visible
- nodes only. The tuple {node_type, visible} is
- included in InfoList .
+ nodes only. The association node_type => visible will
+ be included in the Info map.
hidden
Subscribe to node status change messages for hidden
- nodes only. The tuple {node_type, hidden} is
- included in InfoList .
+ nodes only. The association node_type => hidden will
+ be included in the Info map.
all
Subscribe to node status change messages for both
- visible and hidden nodes. The tuple
- {node_type, visible | hidden} is included in
- InfoList .
+ visible and hidden nodes. The association
+ node_type => visible | hidden will be included in
+ the Info map.
+
+ If no node_type => NodeType association
+ is included in the Options map, the
+ caller will subscribe for status change messages for visible
+ nodes only, but no node_type => visible
+ association will be included in the Info map.
+
- nodedown_reason
+ nodedown_reason => boolean()
-
-
The tuple {nodedown_reason, Reason} is included in
- InfoList in nodedown messages.
+
+ If the value of the association equals true , a
+ nodedown_reason => Reason association will be
+ included in the Info map for nodedown
+ messages.
+
+
Reason can, depending on which
- distribution module or process that is used be any term,
+ distribution module or process that is used, be any term,
but for the standard TCP distribution module it is
- any of the following:
+ one of the following:
connection_setup_failed
@@ -263,6 +302,82 @@ $ erl -sname foobar
+
+ When Options is a list, currently
+ ListOption can be one of the following:
+
+
+ connection_id
+ -
+
+ A {connection_id, ConnectionId} tuple will be
+ included in Info where ConnectionId is the
+ connection identifier of the connection coming up or
+ going down. For more info about this connection identifier
+ see the documentation of
+ erlang:nodes/2 .
+
+
+ {node_type, NodeType }
+ -
+
Valid values for NodeType :
+
+ visible
+ Subscribe to node status change messages for visible
+ nodes only. The tuple {node_type, visible} will be
+ included in the Info list.
+ hidden
+ Subscribe to node status change messages for hidden
+ nodes only. The tuple {node_type, hidden} will be
+ included in the Info list.
+ all
+ Subscribe to node status change messages for both
+ visible and hidden nodes. The tuple
+ {node_type, visible | hidden} will be included in
+ the Info list.
+
+
+ If no {node_type, NodeType } option
+ has been given. The caller will subscribe for status
+ change messages for visible nodes only, but no
+ {node_type, visible} tuple will be included in the
+ Info list.
+
+
+ nodedown_reason
+ -
+
+ The tuple {nodedown_reason, Reason} will be included
+ in the Info list for nodedown messages.
+
+
+ See the documentation of the
+ nodedown_reason
+ => boolean() association above for information
+ about possible Reason values.
+
+
+
+ Example:
+
+(a@localhost)1> net_kernel:monitor_nodes(true, #{connection_id=>true, node_type=>all, nodedown_reason=>true}).
+ok
+(a@localhost)2> flush().
+Shell got {nodeup,b@localhost,
+ #{connection_id => 3067552,node_type => visible}}
+Shell got {nodeup,c@localhost,
+ #{connection_id => 13892107,node_type => hidden}}
+Shell got {nodedown,b@localhost,
+ #{connection_id => 3067552,node_type => visible,
+ nodedown_reason => connection_closed}}
+Shell got {nodedown,c@localhost,
+ #{connection_id => 13892107,node_type => hidden,
+ nodedown_reason => net_tick_timeout}}
+Shell got {nodeup,b@localhost,
+ #{connection_id => 3067553,node_type => visible}}
+ok
+(a@localhost)3>
+
diff --git a/lib/kernel/src/kernel.app.src b/lib/kernel/src/kernel.app.src
index 234d71f745..1e49d081fc 100644
--- a/lib/kernel/src/kernel.app.src
+++ b/lib/kernel/src/kernel.app.src
@@ -150,6 +150,6 @@
{prevent_overlapping_partitions, false}
]},
{mod, {kernel, []}},
- {runtime_dependencies, ["erts-@OTP-17843@", "stdlib-3.5", "sasl-3.0"]}
+ {runtime_dependencies, ["erts-@OTP-17843:OTP-17934@", "stdlib-3.5", "sasl-3.0"]}
]
}.
diff --git a/lib/kernel/src/net_kernel.erl b/lib/kernel/src/net_kernel.erl
index b857000a80..e4e2ca6e49 100644
--- a/lib/kernel/src/net_kernel.erl
+++ b/lib/kernel/src/net_kernel.erl
@@ -256,15 +256,41 @@ monitor_nodes(Flag) ->
-spec monitor_nodes(Flag, Options) -> ok | Error when
Flag :: boolean(),
- Options :: [Option],
- Option :: {node_type, NodeType}
- | nodedown_reason,
+ Options :: OptionsList | OptionsMap,
+ OptionsList :: [ListOption],
+ ListOption :: connection_id
+ | {node_type, NodeType}
+ | nodedown_reason,
+ OptionsMap :: #{connection_id => boolean(),
+ node_type => NodeType,
+ nodedown_reason => boolean()},
NodeType :: visible | hidden | all,
Error :: error | {error, term()}.
monitor_nodes(Flag, Opts) ->
- case catch process_flag({monitor_nodes, Opts}, Flag) of
- N when is_integer(N) -> ok;
- _ -> mk_monitor_nodes_error(Flag, Opts)
+ try
+ MapOpts = if is_map(Opts) ->
+ error = maps:find(list, Opts),
+ Opts;
+ is_list(Opts) ->
+ lists:foldl(fun (nodedown_reason, Acc) ->
+ Acc#{nodedown_reason => true};
+ (connection_id, Acc) ->
+ Acc#{connection_id => true};
+ ({node_type, Val}, Acc) ->
+ case maps:find(node_type, Acc) of
+ error -> ok;
+ {ok, Val} -> ok
+ end,
+ Acc#{node_type => Val}
+ end,
+ #{list => true},
+ Opts)
+ end,
+ true = is_integer(process_flag({monitor_nodes, MapOpts}, Flag)),
+ ok
+ catch
+ _:_ ->
+ mk_monitor_nodes_error(Flag, Opts)
end.
%% ...
@@ -1194,8 +1220,45 @@ check_options(Opts) when is_list(Opts) ->
_ ->
{error, {unknown_options, RestOpts2}}
end;
+check_options(Opts) when is_map(Opts) ->
+ BadMap0 = case maps:find(connection_id, Opts) of
+ error ->
+ Opts;
+ {ok, CIdBool} when is_boolean(CIdBool) ->
+ maps:remove(connection_id, Opts);
+ {ok, BadCIdVal} ->
+ throw({error,
+ {bad_option_value,
+ #{connection_id => BadCIdVal}}})
+ end,
+ BadMap1 = case maps:find(nodedown_reason, BadMap0) of
+ error ->
+ BadMap0;
+ {ok, NRBool} when is_boolean(NRBool) ->
+ maps:remove(nodedown_reason, BadMap0);
+ {ok, BadNRVal} ->
+ throw({error,
+ {bad_option_value,
+ #{nodedown_reason => BadNRVal}}})
+ end,
+ BadMap2 = case maps:find(node_type, BadMap1) of
+ error ->
+ BadMap1;
+ {ok, NTVal} when NTVal == visible; NTVal == hidden; NTVal == all ->
+ maps:remove(node_type, BadMap1);
+ {ok, BadNTVal} ->
+ throw({error,
+ {bad_option_value,
+ #{node_type => BadNTVal}}})
+ end,
+ if map_size(BadMap2) == 0 ->
+ {error, internal_error};
+ true ->
+ throw({error, {unknown_options, BadMap2}})
+ end;
check_options(Opts) ->
- {error, {options_not_a_list, Opts}}.
+ {error, {invalid_options, Opts}}.
+
mk_monitor_nodes_error(Flag, _Opts) when Flag =/= true, Flag =/= false ->
error;
diff --git a/lib/kernel/test/erl_distribution_SUITE.erl b/lib/kernel/test/erl_distribution_SUITE.erl
index e4fe27c619..7d7108e44b 100644
--- a/lib/kernel/test/erl_distribution_SUITE.erl
+++ b/lib/kernel/test/erl_distribution_SUITE.erl
@@ -1108,7 +1108,9 @@ monitor_nodes_misc(DCfg, _Config) ->
MonNodeState = monitor_node_state(),
ok = net_kernel:monitor_nodes(true),
ok = net_kernel:monitor_nodes(true, [{node_type, all}, nodedown_reason]),
- ok = net_kernel:monitor_nodes(true, [nodedown_reason, {node_type, all}]),
+ ok = net_kernel:monitor_nodes(true, [nodedown_reason, {node_type, all}, connection_id]),
+ ok = net_kernel:monitor_nodes(true, #{node_type => all, nodedown_reason => true}),
+ ok = net_kernel:monitor_nodes(true, #{node_type => all, nodedown_reason => true, connection_id => true}),
Names = get_numbered_nodenames(3, node),
[NN1, NN2, NN3] = Names,
@@ -1117,27 +1119,90 @@ monitor_nodes_misc(DCfg, _Config) ->
receive {nodeup, N1} -> ok end,
+ receive {nodeup, N1, #{node_type := visible}} -> ok end,
+ receive {nodeup, N2, #{node_type := hidden}} -> ok end,
receive {nodeup, N1, [{node_type, visible}]} -> ok end,
- receive {nodeup, N1, [{node_type, visible}]} -> ok end,
- receive {nodeup, N2, [{node_type, hidden}]} -> ok end,
receive {nodeup, N2, [{node_type, hidden}]} -> ok end,
+ NodesInfo = erlang:nodes(connected, #{connection_id => true}),
+
+ {N1, #{connection_id := N1CId}} = lists:keyfind(N1, 1, NodesInfo),
+ {N2, #{connection_id := N2CId}} = lists:keyfind(N2, 1, NodesInfo),
+
+ ct:pal("N1: ~p ~p~n", [N1, N1CId]),
+ ct:pal("N2: ~p ~p~n", [N2, N2CId]),
+
+ receive {nodeup, N1, #{node_type := visible, connection_id := N1CId}} -> ok end,
+ receive {nodeup, N2, #{node_type := hidden, connection_id := N2CId}} -> ok end,
+
+ N1UpInfoSorted = lists:sort([{node_type, visible},{connection_id, N1CId}]),
+ N2UpInfoSorted = lists:sort([{node_type, hidden},{connection_id, N2CId}]),
+
+ receive {nodeup, N1, UpN1Info} -> N1UpInfoSorted = lists:sort(UpN1Info) end,
+ receive {nodeup, N2, UpN2Info} -> N2UpInfoSorted = lists:sort(UpN2Info) end,
+
stop_node(N1),
stop_node(N2),
- VisbleDownInfo = lists:sort([{node_type, visible},
- {nodedown_reason, connection_closed}]),
- HiddenDownInfo = lists:sort([{node_type, hidden},
- {nodedown_reason, connection_closed}]),
-
receive {nodedown, N1} -> ok end,
- receive {nodedown, N1, Info1A} -> VisbleDownInfo = lists:sort(Info1A) end,
- receive {nodedown, N1, Info1B} -> VisbleDownInfo = lists:sort(Info1B) end,
- receive {nodedown, N2, Info2A} -> HiddenDownInfo = lists:sort(Info2A) end,
- receive {nodedown, N2, Info2B} -> HiddenDownInfo = lists:sort(Info2B) end,
+ receive {nodedown, N1, #{node_type := visible,
+ nodedown_reason := connection_closed}} -> ok end,
+ receive {nodedown, N1, #{node_type := visible,
+ nodedown_reason := connection_closed,
+ connection_id := N1CId}} -> ok end,
+ receive {nodedown, N2, #{node_type := hidden,
+ nodedown_reason := connection_closed}} -> ok end,
+ receive {nodedown, N2, #{node_type := hidden,
+ nodedown_reason := connection_closed,
+ connection_id := N2CId}} -> ok end,
+
+ N1ADownInfoSorted = lists:sort([{node_type, visible},
+ {nodedown_reason, connection_closed}]),
+ N1BDownInfoSorted = lists:sort([{node_type, visible},
+ {nodedown_reason, connection_closed},
+ {connection_id, N1CId}]),
+ N2ADownInfoSorted = lists:sort([{node_type, hidden},
+ {nodedown_reason, connection_closed}]),
+ N2BDownInfoSorted = lists:sort([{node_type, hidden},
+ {nodedown_reason, connection_closed},
+ {connection_id, N2CId}]),
+
+ receive
+ {nodedown, N1, N1Info1} ->
+ case lists:sort(N1Info1) of
+ N1ADownInfoSorted ->
+ receive
+ {nodedown, N1, N1Info2} ->
+ N1BDownInfoSorted = lists:sort(N1Info2)
+ end;
+ N1BDownInfoSorted ->
+ receive
+ {nodedown, N1, N1Info2} ->
+ N1ADownInfoSorted = lists:sort(N1Info2)
+ end
+ end
+ end,
+ receive
+ {nodedown, N2, N2Info1} ->
+ case lists:sort(N2Info1) of
+ N2ADownInfoSorted ->
+ receive
+ {nodedown, N2, N2Info2} ->
+ N2BDownInfoSorted = lists:sort(N2Info2)
+ end;
+ N2BDownInfoSorted ->
+ receive
+ {nodedown, N2, N2Info2} ->
+ N2ADownInfoSorted = lists:sort(N2Info2)
+ end
+ end
+ end,
ok = net_kernel:monitor_nodes(false, [{node_type, all}, nodedown_reason]),
+ ok = net_kernel:monitor_nodes(false, [nodedown_reason, {node_type, all}, connection_id]),
+ ok = net_kernel:monitor_nodes(false, #{node_type => all, nodedown_reason => true}),
+ ok = net_kernel:monitor_nodes(false, #{node_type => all, nodedown_reason => true, connection_id => true}),
{ok, N3} = start_node(DCfg, NN3),
receive {nodeup, N3} -> ok end,
@@ -1273,7 +1338,11 @@ monitor_nodes_errors(Config) when is_list(Config) ->
[gurka]}} = net_kernel:monitor_nodes(true,
[gurka]),
{error,
- {options_not_a_list,
+ {unknown_options,
+ #{gurka := true}}} = net_kernel:monitor_nodes(true,
+ #{gurka => true}),
+ {error,
+ {invalid_options,
gurka}} = net_kernel:monitor_nodes(true,
gurka),
{error,
@@ -1295,6 +1364,10 @@ monitor_nodes_errors(Config) when is_list(Config) ->
{node_type,
blaha}}}
= net_kernel:monitor_nodes(true, [{node_type, blaha}]),
+ {error,
+ {bad_option_value,
+ #{node_type := blaha}}}
+ = net_kernel:monitor_nodes(true, #{node_type => blaha}),
MonNodeState = monitor_node_state(),
ok.
diff --git a/lib/stdlib/src/erl_internal.erl b/lib/stdlib/src/erl_internal.erl
index 939abaff00..bf170fd7b4 100644
--- a/lib/stdlib/src/erl_internal.erl
+++ b/lib/stdlib/src/erl_internal.erl
@@ -351,6 +351,7 @@ bif(node, 0) -> true;
bif(node, 1) -> true;
bif(nodes, 0) -> true;
bif(nodes, 1) -> true;
+bif(nodes, 2) -> true;
bif(now, 0) -> true;
bif(open_port, 2) -> true;
bif(pid_to_list, 1) -> true;
diff --git a/lib/stdlib/src/stdlib.app.src b/lib/stdlib/src/stdlib.app.src
index f1a1afaf72..a48dbf0fc5 100644
--- a/lib/stdlib/src/stdlib.app.src
+++ b/lib/stdlib/src/stdlib.app.src
@@ -108,6 +108,6 @@
dets]},
{applications, [kernel]},
{env, []},
- {runtime_dependencies, ["sasl-3.0","kernel-6.0","erts-10.7.1","crypto-3.3",
+ {runtime_dependencies, ["sasl-3.0","kernel-6.0","erts-@OTP-17934@","crypto-3.3",
"compiler-5.0"]}
]}.
--
cgit v1.2.1