Merge pull request #6864 from rabbitmq/rabbitmq-server-6853
diagnostics check_port_connectivity: support --address
This commit is contained in:
commit
9b0d2d3e60
|
@ -14,7 +14,7 @@ defmodule RabbitMQ.CLI.Diagnostics.Commands.CheckPortConnectivityCommand do
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import RabbitMQ.CLI.Diagnostics.Helpers,
|
import RabbitMQ.CLI.Diagnostics.Helpers,
|
||||||
only: [check_listener_connectivity: 3]
|
only: [check_listener_connectivity: 4]
|
||||||
|
|
||||||
import RabbitMQ.CLI.Core.Platform, only: [line_separator: 0]
|
import RabbitMQ.CLI.Core.Platform, only: [line_separator: 0]
|
||||||
import RabbitMQ.CLI.Core.Listeners
|
import RabbitMQ.CLI.Core.Listeners
|
||||||
|
@ -23,7 +23,8 @@ defmodule RabbitMQ.CLI.Diagnostics.Commands.CheckPortConnectivityCommand do
|
||||||
|
|
||||||
@default_timeout 30_000
|
@default_timeout 30_000
|
||||||
|
|
||||||
use RabbitMQ.CLI.Core.AcceptsDefaultSwitchesAndTimeout
|
def switches(), do: [timeout: :integer, address: :string]
|
||||||
|
def aliases(), do: [t: :timeout, a: :address]
|
||||||
|
|
||||||
def merge_defaults(args, opts) do
|
def merge_defaults(args, opts) do
|
||||||
timeout =
|
timeout =
|
||||||
|
@ -33,13 +34,14 @@ defmodule RabbitMQ.CLI.Diagnostics.Commands.CheckPortConnectivityCommand do
|
||||||
other -> other
|
other -> other
|
||||||
end
|
end
|
||||||
|
|
||||||
{args, Map.merge(opts, %{timeout: timeout})}
|
opts1 = Map.merge(%{address: nil}, opts)
|
||||||
|
{args, Map.merge(opts1, %{timeout: timeout})}
|
||||||
end
|
end
|
||||||
|
|
||||||
use RabbitMQ.CLI.Core.AcceptsNoPositionalArguments
|
use RabbitMQ.CLI.Core.AcceptsNoPositionalArguments
|
||||||
use RabbitMQ.CLI.Core.RequiresRabbitAppRunning
|
use RabbitMQ.CLI.Core.RequiresRabbitAppRunning
|
||||||
|
|
||||||
def run([], %{node: node_name, timeout: timeout}) do
|
def run([], %{node: node_name, address: target_ip, timeout: timeout}) do
|
||||||
case :rabbit_misc.rpc_call(node_name, :rabbit_networking, :active_listeners, [], timeout) do
|
case :rabbit_misc.rpc_call(node_name, :rabbit_networking, :active_listeners, [], timeout) do
|
||||||
{:error, _} = err ->
|
{:error, _} = err ->
|
||||||
err
|
err
|
||||||
|
@ -52,7 +54,7 @@ defmodule RabbitMQ.CLI.Diagnostics.Commands.CheckPortConnectivityCommand do
|
||||||
|
|
||||||
case locals do
|
case locals do
|
||||||
[] -> {true, locals}
|
[] -> {true, locals}
|
||||||
_ -> check_connectivity_of(locals, node_name, timeout)
|
_ -> check_connectivity_of(locals, node_name, target_ip, timeout)
|
||||||
end
|
end
|
||||||
|
|
||||||
other ->
|
other ->
|
||||||
|
@ -64,7 +66,7 @@ defmodule RabbitMQ.CLI.Diagnostics.Commands.CheckPortConnectivityCommand do
|
||||||
{:ok, %{"result" => "ok", "node" => node_name, "listeners" => listener_maps(listeners)}}
|
{:ok, %{"result" => "ok", "node" => node_name, "listeners" => listener_maps(listeners)}}
|
||||||
end
|
end
|
||||||
|
|
||||||
def output({true, listeners}, %{node: node_name}) do
|
def output({true, listeners}, %{node: node_name, address: nil}) do
|
||||||
ports =
|
ports =
|
||||||
listeners
|
listeners
|
||||||
|> listener_maps
|
|> listener_maps
|
||||||
|
@ -72,7 +74,20 @@ defmodule RabbitMQ.CLI.Diagnostics.Commands.CheckPortConnectivityCommand do
|
||||||
|> Enum.sort()
|
|> Enum.sort()
|
||||||
|> Enum.join(", ")
|
|> Enum.join(", ")
|
||||||
|
|
||||||
{:ok, "Successfully connected to ports #{ports} on node #{node_name}."}
|
{:ok,
|
||||||
|
"Successfully connected to ports #{ports} on node #{node_name} (using node hostname resolution)"}
|
||||||
|
end
|
||||||
|
|
||||||
|
def output({true, listeners}, %{node: node_name, address: target_ip}) do
|
||||||
|
ports =
|
||||||
|
listeners
|
||||||
|
|> listener_maps
|
||||||
|
|> Enum.map(fn %{port: p} -> p end)
|
||||||
|
|> Enum.sort()
|
||||||
|
|> Enum.join(", ")
|
||||||
|
|
||||||
|
{:ok,
|
||||||
|
"Successfully connected to ports #{ports} on node #{node_name} (using #{target_ip} for target IP address)"}
|
||||||
end
|
end
|
||||||
|
|
||||||
def output({false, failures}, %{formatter: "json", node: node_name}) do
|
def output({false, failures}, %{formatter: "json", node: node_name}) do
|
||||||
|
@ -93,24 +108,28 @@ defmodule RabbitMQ.CLI.Diagnostics.Commands.CheckPortConnectivityCommand do
|
||||||
|
|
||||||
def help_section(), do: :observability_and_health_checks
|
def help_section(), do: :observability_and_health_checks
|
||||||
|
|
||||||
def usage, do: "check_port_connectivity"
|
def usage, do: "check_port_connectivity [--address <target address>]"
|
||||||
|
|
||||||
def banner([], %{node: node_name}) do
|
def banner([], %{node: node_name, address: nil}) do
|
||||||
"Testing TCP connections to all active listeners on node #{node_name} ..."
|
"Testing TCP connections to all active listeners on node #{node_name} using hostname resolution ..."
|
||||||
|
end
|
||||||
|
|
||||||
|
def banner([], %{node: node_name, address: target_ip}) do
|
||||||
|
"Testing TCP connections to all active listeners on node #{node_name} using #{target_ip} for node IP address ..."
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
#
|
||||||
# Implementation
|
# Implementation
|
||||||
#
|
#
|
||||||
|
|
||||||
defp check_connectivity_of(listeners, node_name, timeout) do
|
defp check_connectivity_of(listeners, node_name, address, timeout) do
|
||||||
# per listener timeout
|
# per listener timeout
|
||||||
t = Kernel.trunc(timeout / (length(listeners) + 1))
|
t = Kernel.trunc(timeout / (length(listeners) + 1))
|
||||||
|
|
||||||
failures =
|
failures =
|
||||||
Enum.reject(
|
Enum.reject(
|
||||||
listeners,
|
listeners,
|
||||||
fn l -> check_listener_connectivity(listener_map(l), node_name, t) end
|
fn l -> check_listener_connectivity(listener_map(l), node_name, address, t) end
|
||||||
)
|
)
|
||||||
|
|
||||||
case failures do
|
case failures do
|
||||||
|
|
|
@ -5,20 +5,33 @@
|
||||||
## Copyright (c) 2007-2023 VMware, Inc. or its affiliates. All rights reserved.
|
## Copyright (c) 2007-2023 VMware, Inc. or its affiliates. All rights reserved.
|
||||||
|
|
||||||
defmodule RabbitMQ.CLI.Diagnostics.Helpers do
|
defmodule RabbitMQ.CLI.Diagnostics.Helpers do
|
||||||
def test_connection(hostname, port, timeout) do
|
def test_connection(hostname_or_ip, port, timeout) do
|
||||||
case :gen_tcp.connect(hostname, port, [], timeout) do
|
hostname_as_list = :rabbit_data_coercion.to_list(hostname_or_ip)
|
||||||
{:error, _} -> :gen_tcp.connect(hostname, port, [:inet6], timeout)
|
|
||||||
|
case :gen_tcp.connect(hostname_as_list, port, [], timeout) do
|
||||||
|
{:error, _} -> :gen_tcp.connect(hostname_as_list, port, [:inet6], timeout)
|
||||||
r -> r
|
r -> r
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def check_port_connectivity(port, node_name, timeout) do
|
def check_port_connectivity(port, node_name, timeout) do
|
||||||
|
check_port_connectivity(port, node_name, nil, timeout)
|
||||||
|
end
|
||||||
|
|
||||||
|
def check_port_connectivity(port, node_name, nil, timeout) do
|
||||||
regex = Regex.recompile!(~r/^(.+)@/)
|
regex = Regex.recompile!(~r/^(.+)@/)
|
||||||
hostname = Regex.replace(regex, to_string(node_name), "") |> to_charlist
|
hostname = Regex.replace(regex, to_string(node_name), "") |> to_charlist
|
||||||
|
|
||||||
|
check_port_connectivity(port, node_name, hostname, timeout)
|
||||||
|
end
|
||||||
|
|
||||||
|
def check_port_connectivity(port, node_name, hostname_or_ip, timeout) do
|
||||||
try do
|
try do
|
||||||
case test_connection(hostname, port, timeout) do
|
IO.puts("Will connect to #{hostname_or_ip}:#{port}")
|
||||||
{:error, _} ->
|
|
||||||
|
case test_connection(hostname_or_ip, port, timeout) do
|
||||||
|
{:error, err} ->
|
||||||
|
IO.puts("Error connecting to #{hostname_or_ip}:#{port}: #{err}")
|
||||||
false
|
false
|
||||||
|
|
||||||
{:ok, port} ->
|
{:ok, port} ->
|
||||||
|
@ -33,7 +46,7 @@ defmodule RabbitMQ.CLI.Diagnostics.Helpers do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def check_listener_connectivity(%{port: port}, node_name, timeout) do
|
def check_listener_connectivity(%{port: port}, node_name, target_ip, timeout) do
|
||||||
check_port_connectivity(port, node_name, timeout)
|
check_port_connectivity(port, node_name, target_ip, timeout)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -20,12 +20,13 @@ defmodule CheckPortConnectivityCommandTest do
|
||||||
{:ok,
|
{:ok,
|
||||||
opts: %{
|
opts: %{
|
||||||
node: get_rabbit_hostname(),
|
node: get_rabbit_hostname(),
|
||||||
|
address: nil,
|
||||||
timeout: context[:test_timeout] || 30000
|
timeout: context[:test_timeout] || 30000
|
||||||
}}
|
}}
|
||||||
end
|
end
|
||||||
|
|
||||||
test "merge_defaults: provides a default timeout" do
|
test "merge_defaults: provides a default timeout" do
|
||||||
assert @command.merge_defaults([], %{}) == {[], %{timeout: 30000}}
|
assert @command.merge_defaults([], %{}) == {[], %{address: nil, timeout: 30000}}
|
||||||
end
|
end
|
||||||
|
|
||||||
test "validate: treats positional arguments as a failure" do
|
test "validate: treats positional arguments as a failure" do
|
||||||
|
@ -40,11 +41,12 @@ defmodule CheckPortConnectivityCommandTest do
|
||||||
test "run: targeting an unreachable node throws a badrpc", context do
|
test "run: targeting an unreachable node throws a badrpc", context do
|
||||||
assert match?(
|
assert match?(
|
||||||
{:badrpc, _},
|
{:badrpc, _},
|
||||||
@command.run([], Map.merge(context[:opts], %{node: :jake@thedog}))
|
@command.run([], Map.merge(context[:opts], %{node: :jake@thedog, address: nil}))
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "run: tries to connect to every inferred active listener", context do
|
test "run: without --address tries to connect to every inferred active listener using hostname resolution",
|
||||||
|
context do
|
||||||
assert match?({true, _}, @command.run([], context[:opts]))
|
assert match?({true, _}, @command.run([], context[:opts]))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -53,7 +55,7 @@ defmodule CheckPortConnectivityCommandTest do
|
||||||
end
|
end
|
||||||
|
|
||||||
# note: it's run/2 that filters out non-local alarms
|
# note: it's run/2 that filters out non-local alarms
|
||||||
test "output: when target node has a local alarm in effect, returns a failure", context do
|
test "output: when check failed to connect to a port, returns a failure", context do
|
||||||
failure =
|
failure =
|
||||||
{:listener, :rabbit@mercurio, :lolz, 'mercurio', {0, 0, 0, 0, 0, 0, 0, 0}, 7_761_613,
|
{:listener, :rabbit@mercurio, :lolz, 'mercurio', {0, 0, 0, 0, 0, 0, 0, 0}, 7_761_613,
|
||||||
[backlog: 128, nodelay: true]}
|
[backlog: 128, nodelay: true]}
|
||||||
|
|
Loading…
Reference in New Issue