mix format rabbitmq_cli
This commit is contained in:
parent
56ca86a48e
commit
4cbbaad2df
|
|
@ -4,7 +4,6 @@
|
|||
##
|
||||
## Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved.
|
||||
|
||||
|
||||
# This file is responsible for configuring your application
|
||||
# and its dependencies with the aid of the Mix.Config module.
|
||||
use Mix.Config
|
||||
|
|
@ -25,7 +24,7 @@ use Mix.Config
|
|||
#
|
||||
# Or configure a 3rd-party app:
|
||||
#
|
||||
config :logger, [level: :warn, console: [device: :standard_error]]
|
||||
config :logger, level: :warn, console: [device: :standard_error]
|
||||
#
|
||||
|
||||
# It is also possible to import configuration files, relative to this
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ defmodule RabbitMQ.CLI.AutoComplete do
|
|||
def suggest_command(_cmd_name, empty) when empty == %{} do
|
||||
nil
|
||||
end
|
||||
|
||||
def suggest_command(typed, module_map) do
|
||||
suggestion =
|
||||
module_map
|
||||
|
|
@ -40,6 +41,7 @@ defmodule RabbitMQ.CLI.AutoComplete do
|
|||
case suggestion do
|
||||
{cmd, distance} when distance >= @jaro_distance_limit ->
|
||||
{:suggest, cmd}
|
||||
|
||||
_ ->
|
||||
nil
|
||||
end
|
||||
|
|
|
|||
|
|
@ -48,7 +48,11 @@ defmodule RabbitMQ.CLI.CommandBehaviour do
|
|||
@callback scopes() :: [atom()] | nil
|
||||
@callback description() :: String.t()
|
||||
@callback help_section() :: String.t()
|
||||
@callback usage_additional() :: String.t() | [String.t()] | nonempty_list(pair_of_strings()) | [{String.t(), String.t()}]
|
||||
@callback usage_additional() ::
|
||||
String.t()
|
||||
| [String.t()]
|
||||
| nonempty_list(pair_of_strings())
|
||||
| [{String.t(), String.t()}]
|
||||
@callback usage_doc_guides() :: String.t() | [String.t()]
|
||||
## Erlang distribution control
|
||||
## :cli - default rabbitmqctl generated node name
|
||||
|
|
@ -57,46 +61,93 @@ defmodule RabbitMQ.CLI.CommandBehaviour do
|
|||
@callback distribution(map()) :: :cli | :none | {:fun, (map() -> :ok | {:error, any()})}
|
||||
|
||||
defmacro defcmd(map) do
|
||||
usage_q = case map[:usage] do
|
||||
nil -> :ok
|
||||
usage ->
|
||||
quote do def usage(), do: unquote(usage) end
|
||||
end
|
||||
scopes_q = case map[:scopes] do
|
||||
nil -> :ok
|
||||
scopes ->
|
||||
quote do def scopes(), do: unquote(scopes) end
|
||||
end
|
||||
description_q = case map[:description] do
|
||||
nil -> :ok
|
||||
description ->
|
||||
quote do def description(), do: unquote(description) end
|
||||
end
|
||||
help_section_q = case map[:help_section] do
|
||||
nil -> :ok
|
||||
help_section ->
|
||||
quote do def help_section(), do: unquote(help_section) end
|
||||
end
|
||||
usage_additional_q = case map[:usage_additional] do
|
||||
nil -> :ok
|
||||
usage_additional ->
|
||||
quote do def usage_additional(), do: unquote(usage_additional) end
|
||||
end
|
||||
formatter_q = case map[:formatter] do
|
||||
nil -> :ok
|
||||
formatter ->
|
||||
quote do def formatter(), do: unquote(formatter) end
|
||||
end
|
||||
switches_q = case map[:switches] do
|
||||
nil -> :ok
|
||||
switches ->
|
||||
quote do def switches(), do: unquote(switches) end
|
||||
end
|
||||
aliases_q = case map[:aliases] do
|
||||
nil -> :ok
|
||||
aliases ->
|
||||
quote do def aliases(), do: unquote(aliases) end
|
||||
end
|
||||
usage_q =
|
||||
case map[:usage] do
|
||||
nil ->
|
||||
:ok
|
||||
|
||||
usage ->
|
||||
quote do
|
||||
def usage(), do: unquote(usage)
|
||||
end
|
||||
end
|
||||
|
||||
scopes_q =
|
||||
case map[:scopes] do
|
||||
nil ->
|
||||
:ok
|
||||
|
||||
scopes ->
|
||||
quote do
|
||||
def scopes(), do: unquote(scopes)
|
||||
end
|
||||
end
|
||||
|
||||
description_q =
|
||||
case map[:description] do
|
||||
nil ->
|
||||
:ok
|
||||
|
||||
description ->
|
||||
quote do
|
||||
def description(), do: unquote(description)
|
||||
end
|
||||
end
|
||||
|
||||
help_section_q =
|
||||
case map[:help_section] do
|
||||
nil ->
|
||||
:ok
|
||||
|
||||
help_section ->
|
||||
quote do
|
||||
def help_section(), do: unquote(help_section)
|
||||
end
|
||||
end
|
||||
|
||||
usage_additional_q =
|
||||
case map[:usage_additional] do
|
||||
nil ->
|
||||
:ok
|
||||
|
||||
usage_additional ->
|
||||
quote do
|
||||
def usage_additional(), do: unquote(usage_additional)
|
||||
end
|
||||
end
|
||||
|
||||
formatter_q =
|
||||
case map[:formatter] do
|
||||
nil ->
|
||||
:ok
|
||||
|
||||
formatter ->
|
||||
quote do
|
||||
def formatter(), do: unquote(formatter)
|
||||
end
|
||||
end
|
||||
|
||||
switches_q =
|
||||
case map[:switches] do
|
||||
nil ->
|
||||
:ok
|
||||
|
||||
switches ->
|
||||
quote do
|
||||
def switches(), do: unquote(switches)
|
||||
end
|
||||
end
|
||||
|
||||
aliases_q =
|
||||
case map[:aliases] do
|
||||
nil ->
|
||||
:ok
|
||||
|
||||
aliases ->
|
||||
quote do
|
||||
def aliases(), do: unquote(aliases)
|
||||
end
|
||||
end
|
||||
|
||||
quote do
|
||||
unquote(usage_q)
|
||||
|
|
@ -129,6 +180,7 @@ defmodule RabbitMQ.CLI.CommandBehaviour do
|
|||
:rabbitmqctl -> :other
|
||||
plugin -> {:plugin, plugin}
|
||||
end
|
||||
|
||||
section ->
|
||||
section
|
||||
end
|
||||
|
|
@ -159,9 +211,7 @@ defmodule RabbitMQ.CLI.CommandBehaviour do
|
|||
end
|
||||
|
||||
def validate_execution_environment(cmd, args, options) do
|
||||
Helpers.apply_if_exported(cmd,
|
||||
:validate_execution_environment, [args, options],
|
||||
:ok)
|
||||
Helpers.apply_if_exported(cmd, :validate_execution_environment, [args, options], :ok)
|
||||
end
|
||||
|
||||
def distribution(cmd, options) do
|
||||
|
|
|
|||
|
|
@ -54,9 +54,11 @@ defmodule RabbitMQ.CLI.Core.Alarms do
|
|||
def alarm_type(val) when is_atom(val) do
|
||||
val
|
||||
end
|
||||
|
||||
def alarm_type({:resource_limit, val, _node}) do
|
||||
val
|
||||
end
|
||||
|
||||
def alarm_type({{:resource_limit, val, _node}, []}) do
|
||||
val
|
||||
end
|
||||
|
|
@ -64,6 +66,7 @@ defmodule RabbitMQ.CLI.Core.Alarms do
|
|||
def alarm_maps(xs) do
|
||||
Enum.map(xs, &alarm_map/1)
|
||||
end
|
||||
|
||||
def alarm_map(:file_descriptor_limit) do
|
||||
%{
|
||||
type: :resource_limit,
|
||||
|
|
@ -71,6 +74,7 @@ defmodule RabbitMQ.CLI.Core.Alarms do
|
|||
node: node()
|
||||
}
|
||||
end
|
||||
|
||||
def alarm_map({{:resource_limit, resource, node}, _}) do
|
||||
%{
|
||||
type: :resource_limit,
|
||||
|
|
@ -78,6 +82,7 @@ defmodule RabbitMQ.CLI.Core.Alarms do
|
|||
node: node
|
||||
}
|
||||
end
|
||||
|
||||
def alarm_map({:resource_limit, resource, node}) do
|
||||
%{
|
||||
type: :resource_limit,
|
||||
|
|
|
|||
|
|
@ -189,6 +189,7 @@ defmodule RabbitMQ.CLI.Core.CommandModules do
|
|||
|> to_snake_case
|
||||
|> String.to_atom()
|
||||
|> List.wrap()
|
||||
|
||||
scopes ->
|
||||
scopes
|
||||
end
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ defmodule RabbitMQ.CLI.Core.FeatureFlags do
|
|||
|
||||
def is_enabled_remotely(node_name, feature_flag) do
|
||||
case :rabbit_misc.rpc_call(node_name, :rabbit_feature_flags, :is_enabled, [feature_flag]) do
|
||||
true -> true
|
||||
true -> true
|
||||
false -> false
|
||||
{:error, _} = error -> error
|
||||
end
|
||||
|
|
@ -21,10 +21,13 @@ defmodule RabbitMQ.CLI.Core.FeatureFlags do
|
|||
|
||||
def assert_feature_flag_enabled(node_name, feature_flag, success_fun) do
|
||||
case is_enabled_remotely(node_name, feature_flag) do
|
||||
true ->
|
||||
true ->
|
||||
success_fun.()
|
||||
|
||||
false ->
|
||||
{:error, ExitCodes.exit_dataerr(), "The #{feature_flag} feature flag is not enabled on the target node"}
|
||||
{:error, ExitCodes.exit_dataerr(),
|
||||
"The #{feature_flag} feature flag is not enabled on the target node"}
|
||||
|
||||
{:error, _} = error ->
|
||||
error
|
||||
end
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ defmodule RabbitMQ.CLI.Core.Helpers do
|
|||
def normalise_node(name, node_name_type) do
|
||||
case NodeName.create(name, node_name_type) do
|
||||
{:ok, node_name} -> node_name
|
||||
other -> other
|
||||
other -> other
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -27,9 +27,11 @@ defmodule RabbitMQ.CLI.Core.Helpers do
|
|||
def normalise_node_option(options) do
|
||||
node_opt = Config.get_option(:node, options)
|
||||
longnames_opt = Config.get_option(:longnames, options)
|
||||
|
||||
case NodeName.create(node_opt, longnames_opt) do
|
||||
{:error, _} = err ->
|
||||
err
|
||||
|
||||
{:ok, val} ->
|
||||
{:ok, Map.put(options, :node, val)}
|
||||
end
|
||||
|
|
@ -38,10 +40,12 @@ defmodule RabbitMQ.CLI.Core.Helpers do
|
|||
def normalise_node_option(nil, _, _) do
|
||||
nil
|
||||
end
|
||||
|
||||
def normalise_node_option(node_opt, longnames_opt, options) do
|
||||
case NodeName.create(node_opt, longnames_opt) do
|
||||
{:error, _} = err ->
|
||||
err
|
||||
|
||||
{:ok, val} ->
|
||||
{:ok, Map.put(options, :node, val)}
|
||||
end
|
||||
|
|
@ -50,6 +54,7 @@ defmodule RabbitMQ.CLI.Core.Helpers do
|
|||
def case_insensitive_format(%{format: format} = opts) do
|
||||
%{opts | format: String.downcase(format)}
|
||||
end
|
||||
|
||||
def case_insensitive_format(opts), do: opts
|
||||
|
||||
def nodes_in_cluster(node, timeout \\ :infinity) do
|
||||
|
|
@ -116,17 +121,17 @@ defmodule RabbitMQ.CLI.Core.Helpers do
|
|||
end
|
||||
|
||||
def atomize_values(map, keys) do
|
||||
Enum.reduce(map, %{},
|
||||
fn({k, v}, acc) ->
|
||||
case Enum.member?(keys, k) do
|
||||
false -> Map.put(acc, k, v)
|
||||
true -> Map.put(acc, k, DataCoercion.to_atom(v))
|
||||
end
|
||||
end)
|
||||
Enum.reduce(map, %{}, fn {k, v}, acc ->
|
||||
case Enum.member?(keys, k) do
|
||||
false -> Map.put(acc, k, v)
|
||||
true -> Map.put(acc, k, DataCoercion.to_atom(v))
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
def apply_if_exported(mod, fun, args, default) do
|
||||
Code.ensure_loaded(mod)
|
||||
|
||||
case function_exported?(mod, fun, length(args)) do
|
||||
true -> apply(mod, fun, args)
|
||||
false -> default
|
||||
|
|
|
|||
|
|
@ -8,18 +8,20 @@ defmodule RabbitMQ.CLI.Core.Input do
|
|||
alias RabbitMQ.CLI.Core.Config
|
||||
|
||||
def consume_single_line_string_with_prompt(prompt, opts) do
|
||||
val = case Config.output_less?(opts) do
|
||||
true ->
|
||||
IO.read(:stdio, :line)
|
||||
false ->
|
||||
IO.puts(prompt)
|
||||
IO.read(:stdio, :line)
|
||||
end
|
||||
val =
|
||||
case Config.output_less?(opts) do
|
||||
true ->
|
||||
IO.read(:stdio, :line)
|
||||
|
||||
false ->
|
||||
IO.puts(prompt)
|
||||
IO.read(:stdio, :line)
|
||||
end
|
||||
|
||||
case val do
|
||||
:eof -> :eof
|
||||
"" -> :eof
|
||||
s -> String.trim(s)
|
||||
"" -> :eof
|
||||
s -> String.trim(s)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -28,8 +30,8 @@ defmodule RabbitMQ.CLI.Core.Input do
|
|||
|
||||
case val do
|
||||
:eof -> :eof
|
||||
"" -> :eof
|
||||
s -> String.trim(s)
|
||||
"" -> :eof
|
||||
s -> String.trim(s)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -13,9 +13,17 @@ defmodule RabbitMQ.CLI.Core.Listeners do
|
|||
# API
|
||||
#
|
||||
|
||||
defrecord :certificate, :Certificate, extract(:Certificate, from_lib: "public_key/include/public_key.hrl")
|
||||
defrecord :tbscertificate, :TBSCertificate, extract(:TBSCertificate, from_lib: "public_key/include/public_key.hrl")
|
||||
defrecord :validity, :Validity, extract(:Validity, from_lib: "public_key/include/public_key.hrl")
|
||||
defrecord :certificate,
|
||||
:Certificate,
|
||||
extract(:Certificate, from_lib: "public_key/include/public_key.hrl")
|
||||
|
||||
defrecord :tbscertificate,
|
||||
:TBSCertificate,
|
||||
extract(:TBSCertificate, from_lib: "public_key/include/public_key.hrl")
|
||||
|
||||
defrecord :validity,
|
||||
:Validity,
|
||||
extract(:Validity, from_lib: "public_key/include/public_key.hrl")
|
||||
|
||||
def listeners_on(listeners, target_node) do
|
||||
Enum.filter(listeners, fn listener(node: node) ->
|
||||
|
|
@ -33,24 +41,22 @@ defmodule RabbitMQ.CLI.Core.Listeners do
|
|||
listeners
|
||||
|> listener_maps
|
||||
|> Enum.map(fn %{interface: interface, port: port, protocol: protocol} ->
|
||||
"Interface: #{interface}, port: #{port}, protocol: #{protocol}, purpose: #{
|
||||
protocol_label(to_atom(protocol))
|
||||
}"
|
||||
"Interface: #{interface}, port: #{port}, protocol: #{protocol}, purpose: #{protocol_label(to_atom(protocol))}"
|
||||
end)
|
||||
end
|
||||
|
||||
def listener_lines(listeners, node) do
|
||||
listeners
|
||||
|> listener_maps
|
||||
|> Enum.map(fn %{interface: interface, port: port, protocol: protocol} ->
|
||||
"Node: #{node}, interface: #{interface}, port: #{port}, protocol: #{protocol}, purpose: #{
|
||||
protocol_label(to_atom(protocol))
|
||||
}"
|
||||
"Node: #{node}, interface: #{interface}, port: #{port}, protocol: #{protocol}, purpose: #{protocol_label(to_atom(protocol))}"
|
||||
end)
|
||||
end
|
||||
|
||||
def listener_map(listener) when is_map(listener) do
|
||||
listener
|
||||
end
|
||||
|
||||
def listener_map(listener) do
|
||||
# Listener options are left out intentionally: they can contain deeply nested values
|
||||
# that are impossible to serialise to JSON.
|
||||
|
|
@ -74,7 +80,8 @@ defmodule RabbitMQ.CLI.Core.Listeners do
|
|||
end
|
||||
|
||||
def listener_certs(listener) do
|
||||
listener(node: node, protocol: protocol, ip_address: interface, port: port, opts: opts) = listener
|
||||
listener(node: node, protocol: protocol, ip_address: interface, port: port, opts: opts) =
|
||||
listener
|
||||
|
||||
%{
|
||||
node: node,
|
||||
|
|
@ -90,29 +97,36 @@ defmodule RabbitMQ.CLI.Core.Listeners do
|
|||
def read_cert(nil) do
|
||||
nil
|
||||
end
|
||||
|
||||
def read_cert({:pem, pem}) do
|
||||
pem
|
||||
end
|
||||
|
||||
def read_cert(path) do
|
||||
case File.read(path) do
|
||||
{:ok, bin} ->
|
||||
bin
|
||||
|
||||
{:error, _} = err ->
|
||||
err
|
||||
end
|
||||
end
|
||||
|
||||
def listener_expiring_within(listener, seconds) do
|
||||
listener(node: node, protocol: protocol, ip_address: interface, port: port, opts: opts) = listener
|
||||
listener(node: node, protocol: protocol, ip_address: interface, port: port, opts: opts) =
|
||||
listener
|
||||
|
||||
certfile = Keyword.get(opts, :certfile)
|
||||
cacertfile = Keyword.get(opts, :cacertfile)
|
||||
now = :calendar.datetime_to_gregorian_seconds(:calendar.universal_time())
|
||||
expiry_date = now + seconds
|
||||
certfile_expires_on = expired(cert_validity(read_cert(certfile)), expiry_date)
|
||||
cacertfile_expires_on = expired(cert_validity(read_cert(cacertfile)), expiry_date)
|
||||
|
||||
case {certfile_expires_on, cacertfile_expires_on} do
|
||||
{[], []} ->
|
||||
false
|
||||
|
||||
_ ->
|
||||
%{
|
||||
node: node,
|
||||
|
|
@ -123,11 +137,20 @@ defmodule RabbitMQ.CLI.Core.Listeners do
|
|||
cacertfile: cacertfile,
|
||||
certfile_expires_on: certfile_expires_on,
|
||||
cacertfile_expires_on: cacertfile_expires_on
|
||||
}
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def expired_listener_map(%{node: node, protocol: protocol, interface: interface, port: port, certfile_expires_on: certfile_expires_on, cacertfile_expires_on: cacertfile_expires_on, certfile: certfile, cacertfile: cacertfile}) do
|
||||
def expired_listener_map(%{
|
||||
node: node,
|
||||
protocol: protocol,
|
||||
interface: interface,
|
||||
port: port,
|
||||
certfile_expires_on: certfile_expires_on,
|
||||
cacertfile_expires_on: cacertfile_expires_on,
|
||||
certfile: certfile,
|
||||
cacertfile: cacertfile
|
||||
}) do
|
||||
%{
|
||||
node: node,
|
||||
protocol: protocol,
|
||||
|
|
@ -144,6 +167,7 @@ defmodule RabbitMQ.CLI.Core.Listeners do
|
|||
def expires_on_list({:error, _} = error) do
|
||||
[error]
|
||||
end
|
||||
|
||||
def expires_on_list(expires) do
|
||||
Enum.map(expires, &expires_on/1)
|
||||
end
|
||||
|
|
@ -151,6 +175,7 @@ defmodule RabbitMQ.CLI.Core.Listeners do
|
|||
def expires_on({:error, _} = error) do
|
||||
error
|
||||
end
|
||||
|
||||
def expires_on(seconds) do
|
||||
{:ok, naive} = NaiveDateTime.from_erl(:calendar.gregorian_seconds_to_datetime(seconds))
|
||||
NaiveDateTime.to_string(naive)
|
||||
|
|
@ -159,38 +184,52 @@ defmodule RabbitMQ.CLI.Core.Listeners do
|
|||
def expired(nil, _) do
|
||||
[]
|
||||
end
|
||||
|
||||
def expired({:error, _} = error, _) do
|
||||
error
|
||||
end
|
||||
|
||||
def expired(expires, expiry_date) do
|
||||
Enum.filter(expires, fn ({:error, _}) -> true
|
||||
(seconds) -> seconds < expiry_date end)
|
||||
Enum.filter(expires, fn
|
||||
{:error, _} -> true
|
||||
seconds -> seconds < expiry_date
|
||||
end)
|
||||
end
|
||||
|
||||
def cert_validity(nil) do
|
||||
nil
|
||||
end
|
||||
|
||||
def cert_validity(cert) do
|
||||
dsa_entries = :public_key.pem_decode(cert)
|
||||
|
||||
case dsa_entries do
|
||||
[] ->
|
||||
{:error, "The certificate file provided does not contain any PEM entry."}
|
||||
|
||||
_ ->
|
||||
now = :calendar.datetime_to_gregorian_seconds(:calendar.universal_time())
|
||||
Enum.map(dsa_entries, fn ({:Certificate, _, _} = dsa_entry) ->
|
||||
certificate(tbsCertificate: tbs_certificate) = :public_key.pem_entry_decode(dsa_entry)
|
||||
tbscertificate(validity: validity) = tbs_certificate
|
||||
validity(notAfter: not_after, notBefore: not_before) = validity
|
||||
start = :pubkey_cert.time_str_2_gregorian_sec(not_before)
|
||||
case start > now do
|
||||
true ->
|
||||
{:ok, naive} = NaiveDateTime.from_erl(:calendar.gregorian_seconds_to_datetime(start))
|
||||
startdate = NaiveDateTime.to_string(naive)
|
||||
{:error, "Certificate is not yet valid. It starts on #{startdate}"}
|
||||
false ->
|
||||
:pubkey_cert.time_str_2_gregorian_sec(not_after)
|
||||
end
|
||||
({type, _, _}) ->
|
||||
|
||||
Enum.map(dsa_entries, fn
|
||||
{:Certificate, _, _} = dsa_entry ->
|
||||
certificate(tbsCertificate: tbs_certificate) = :public_key.pem_entry_decode(dsa_entry)
|
||||
tbscertificate(validity: validity) = tbs_certificate
|
||||
validity(notAfter: not_after, notBefore: not_before) = validity
|
||||
start = :pubkey_cert.time_str_2_gregorian_sec(not_before)
|
||||
|
||||
case start > now do
|
||||
true ->
|
||||
{:ok, naive} =
|
||||
NaiveDateTime.from_erl(:calendar.gregorian_seconds_to_datetime(start))
|
||||
|
||||
startdate = NaiveDateTime.to_string(naive)
|
||||
{:error, "Certificate is not yet valid. It starts on #{startdate}"}
|
||||
|
||||
false ->
|
||||
:pubkey_cert.time_str_2_gregorian_sec(not_after)
|
||||
end
|
||||
|
||||
{type, _, _} ->
|
||||
{:error, "The certificate file provided contains a #{type} entry."}
|
||||
end)
|
||||
end
|
||||
|
|
@ -209,13 +248,13 @@ defmodule RabbitMQ.CLI.Core.Listeners do
|
|||
end
|
||||
end
|
||||
|
||||
def protocol_label(:amqp), do: "AMQP 0-9-1 and AMQP 1.0"
|
||||
def protocol_label(:'amqp/ssl'), do: "AMQP 0-9-1 and AMQP 1.0 over TLS"
|
||||
def protocol_label(:mqtt), do: "MQTT"
|
||||
def protocol_label(:'mqtt/ssl'), do: "MQTT over TLS"
|
||||
def protocol_label(:stomp), do: "STOMP"
|
||||
def protocol_label(:'stomp/ssl'), do: "STOMP over TLS"
|
||||
def protocol_label(:http), do: "HTTP API"
|
||||
def protocol_label(:amqp), do: "AMQP 0-9-1 and AMQP 1.0"
|
||||
def protocol_label(:"amqp/ssl"), do: "AMQP 0-9-1 and AMQP 1.0 over TLS"
|
||||
def protocol_label(:mqtt), do: "MQTT"
|
||||
def protocol_label(:"mqtt/ssl"), do: "MQTT over TLS"
|
||||
def protocol_label(:stomp), do: "STOMP"
|
||||
def protocol_label(:"stomp/ssl"), do: "STOMP over TLS"
|
||||
def protocol_label(:http), do: "HTTP API"
|
||||
def protocol_label(:https), do: "HTTP API over TLS (HTTPS)"
|
||||
def protocol_label(:"http/web-mqtt"), do: "MQTT over WebSockets"
|
||||
def protocol_label(:"https/web-mqtt"), do: "MQTT over WebSockets and TLS (HTTPS)"
|
||||
|
|
@ -245,9 +284,9 @@ defmodule RabbitMQ.CLI.Core.Listeners do
|
|||
"mqtt311" -> "mqtt"
|
||||
"mqtt3_1" -> "mqtt"
|
||||
"mqtt3_1_1" -> "mqtt"
|
||||
"mqtts" -> "mqtt/ssl"
|
||||
"mqtt+tls" -> "mqtt/ssl"
|
||||
"mqtt+ssl" -> "mqtt/ssl"
|
||||
"mqtts" -> "mqtt/ssl"
|
||||
"mqtt+tls" -> "mqtt/ssl"
|
||||
"mqtt+ssl" -> "mqtt/ssl"
|
||||
"stomp1.0" -> "stomp"
|
||||
"stomp1.1" -> "stomp"
|
||||
"stomp1.2" -> "stomp"
|
||||
|
|
@ -257,7 +296,7 @@ defmodule RabbitMQ.CLI.Core.Listeners do
|
|||
"stomp1_0" -> "stomp"
|
||||
"stomp1_1" -> "stomp"
|
||||
"stomp1_2" -> "stomp"
|
||||
"stomps" -> "stomp/ssl"
|
||||
"stomps" -> "stomp/ssl"
|
||||
"stomp+tls" -> "stomp/ssl"
|
||||
"stomp+ssl" -> "stomp/ssl"
|
||||
"https" -> "https"
|
||||
|
|
@ -304,6 +343,7 @@ defmodule RabbitMQ.CLI.Core.Listeners do
|
|||
# networks address ranges, etc actually works better
|
||||
# for the kind of values we can get here than :inet functions. MK.
|
||||
regex = Regex.recompile!(~r/:/)
|
||||
|
||||
case value =~ regex do
|
||||
true -> "[#{value}]"
|
||||
false -> value
|
||||
|
|
|
|||
|
|
@ -5,29 +5,35 @@
|
|||
## Copyright (c) 2019-2022 VMware, Inc. or its affiliates. All rights reserved.
|
||||
|
||||
defmodule RabbitMQ.CLI.Core.LogFiles do
|
||||
@spec get_log_locations(atom, integer | :infinity) :: [String.t] | {:badrpc, term}
|
||||
@spec get_log_locations(atom, integer | :infinity) :: [String.t()] | {:badrpc, term}
|
||||
def get_log_locations(node_name, timeout) do
|
||||
case :rabbit_misc.rpc_call(node_name,
|
||||
:rabbit, :log_locations, [],
|
||||
timeout) do
|
||||
{:badrpc, _} = error -> error;
|
||||
case :rabbit_misc.rpc_call(node_name, :rabbit, :log_locations, [], timeout) do
|
||||
{:badrpc, _} = error -> error
|
||||
list -> Enum.map(list, &to_string/1)
|
||||
end
|
||||
end
|
||||
|
||||
@spec get_default_log_location(atom, integer | :infinity) ::
|
||||
{:ok, String.t} | {:badrpc, term} | {:error, term}
|
||||
{:ok, String.t()} | {:badrpc, term} | {:error, term}
|
||||
def get_default_log_location(node_name, timeout) do
|
||||
case get_log_locations(node_name, timeout) do
|
||||
{:badrpc, _} = error -> error;
|
||||
[] -> {:error, "No log files configured on the node"};
|
||||
{:badrpc, _} = error ->
|
||||
error
|
||||
|
||||
[] ->
|
||||
{:error, "No log files configured on the node"}
|
||||
|
||||
[first_log | _] = log_locations ->
|
||||
case get_log_config_file_location(node_name, timeout) do
|
||||
{:badrpc, _} = error -> error;
|
||||
nil -> {:ok, first_log};
|
||||
{:badrpc, _} = error ->
|
||||
error
|
||||
|
||||
nil ->
|
||||
{:ok, first_log}
|
||||
|
||||
location ->
|
||||
case Enum.member?(log_locations, location) do
|
||||
true -> {:ok, to_string(location)};
|
||||
true -> {:ok, to_string(location)}
|
||||
false -> {:ok, first_log}
|
||||
end
|
||||
end
|
||||
|
|
@ -35,14 +41,18 @@ defmodule RabbitMQ.CLI.Core.LogFiles do
|
|||
end
|
||||
|
||||
defp get_log_config_file_location(node_name, timeout) do
|
||||
case :rabbit_misc.rpc_call(node_name,
|
||||
:application, :get_env, [:rabbit, :log, :none],
|
||||
timeout) do
|
||||
{:badrpc, _} = error -> error;
|
||||
:none -> nil;
|
||||
case :rabbit_misc.rpc_call(node_name, :application, :get_env, [:rabbit, :log, :none], timeout) do
|
||||
{:badrpc, _} = error ->
|
||||
error
|
||||
|
||||
:none ->
|
||||
nil
|
||||
|
||||
log_config ->
|
||||
case log_config[:file] do
|
||||
nil -> nil;
|
||||
nil ->
|
||||
nil
|
||||
|
||||
file_config ->
|
||||
file_config[:file]
|
||||
end
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ defmodule RabbitMQ.CLI.Core.Memory do
|
|||
def compute_relative_values(all_pairs) when is_map(all_pairs) do
|
||||
compute_relative_values(Enum.into(all_pairs, []))
|
||||
end
|
||||
|
||||
def compute_relative_values(all_pairs) do
|
||||
num_pairs = Keyword.delete(all_pairs, :strategy)
|
||||
# Includes RSS, allocated and runtime-used ("erlang") values.
|
||||
|
|
@ -56,21 +57,27 @@ defmodule RabbitMQ.CLI.Core.Memory do
|
|||
def formatted_watermark(val) when is_float(val) do
|
||||
%{relative: val}
|
||||
end
|
||||
|
||||
def formatted_watermark({:relative, val}) when is_float(val) do
|
||||
%{relative: val}
|
||||
end
|
||||
|
||||
def formatted_watermark(:infinity) do
|
||||
%{relative: 1.0}
|
||||
end
|
||||
|
||||
def formatted_watermark({:absolute, val}) do
|
||||
%{absolute: parse_watermark(val)}
|
||||
end
|
||||
|
||||
def formatted_watermark(val) when is_integer(val) do
|
||||
%{absolute: parse_watermark(val)}
|
||||
end
|
||||
|
||||
def formatted_watermark(val) when is_bitstring(val) do
|
||||
%{absolute: parse_watermark(val)}
|
||||
end
|
||||
|
||||
def formatted_watermark(val) when is_list(val) do
|
||||
%{absolute: parse_watermark(val)}
|
||||
end
|
||||
|
|
@ -78,21 +85,24 @@ defmodule RabbitMQ.CLI.Core.Memory do
|
|||
def parse_watermark({:absolute, n}) do
|
||||
case IU.parse(n) do
|
||||
{:ok, parsed} -> parsed
|
||||
err -> err
|
||||
err -> err
|
||||
end
|
||||
end
|
||||
|
||||
def parse_watermark(n) when is_bitstring(n) do
|
||||
case IU.parse(n) do
|
||||
{:ok, parsed} -> parsed
|
||||
err -> err
|
||||
err -> err
|
||||
end
|
||||
end
|
||||
|
||||
def parse_watermark(n) when is_list(n) do
|
||||
case IU.parse(n) do
|
||||
{:ok, parsed} -> parsed
|
||||
err -> err
|
||||
err -> err
|
||||
end
|
||||
end
|
||||
|
||||
def parse_watermark(n) when is_float(n) or is_integer(n) do
|
||||
n
|
||||
end
|
||||
|
|
|
|||
|
|
@ -10,14 +10,15 @@ defmodule RabbitMQ.CLI.Core.Networking do
|
|||
@spec address_family(String.t() | atom() | charlist() | binary()) :: address_family()
|
||||
def address_family(value) do
|
||||
val = RabbitMQ.CLI.Core.DataCoercion.to_atom(value)
|
||||
|
||||
case val do
|
||||
:inet -> :inet
|
||||
:inet -> :inet
|
||||
:inet4 -> :inet
|
||||
:inet6 -> :inet6
|
||||
:ipv4 -> :inet
|
||||
:ipv6 -> :inet6
|
||||
:IPv4 -> :inet
|
||||
:IPv6 -> :inet6
|
||||
:ipv4 -> :inet
|
||||
:ipv6 -> :inet6
|
||||
:IPv4 -> :inet
|
||||
:IPv6 -> :inet6
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -25,14 +26,15 @@ defmodule RabbitMQ.CLI.Core.Networking do
|
|||
def valid_address_family?(value) when is_atom(value) do
|
||||
valid_address_family?(to_string(value))
|
||||
end
|
||||
def valid_address_family?("inet"), do: true
|
||||
|
||||
def valid_address_family?("inet"), do: true
|
||||
def valid_address_family?("inet4"), do: true
|
||||
def valid_address_family?("inet6"), do: true
|
||||
def valid_address_family?("ipv4"), do: true
|
||||
def valid_address_family?("ipv6"), do: true
|
||||
def valid_address_family?("IPv4"), do: true
|
||||
def valid_address_family?("IPv6"), do: true
|
||||
def valid_address_family?(_other), do: false
|
||||
def valid_address_family?("ipv4"), do: true
|
||||
def valid_address_family?("ipv6"), do: true
|
||||
def valid_address_family?("IPv4"), do: true
|
||||
def valid_address_family?("IPv6"), do: true
|
||||
def valid_address_family?(_other), do: false
|
||||
|
||||
@spec format_address(:inet.ip_address()) :: String.t()
|
||||
def format_address(addr) do
|
||||
|
|
@ -46,28 +48,37 @@ defmodule RabbitMQ.CLI.Core.Networking do
|
|||
|
||||
@spec inetrc_map(nonempty_list()) :: map()
|
||||
def inetrc_map(list) do
|
||||
Enum.reduce(list, %{},
|
||||
fn hosts, acc when is_list(hosts) ->
|
||||
Map.put(acc, "hosts", host_resolution_map(hosts))
|
||||
{k, v}, acc when k == :domain or k == :resolv_conf or k == :hosts_file ->
|
||||
Map.put(acc, to_string(k), to_string(v))
|
||||
{k, v}, acc when is_list(v) when k == :search or k == :lookup ->
|
||||
Map.put(acc, to_string(k), Enum.join(Enum.map(v, &to_string/1), ", "))
|
||||
{k, v}, acc when is_integer(v) ->
|
||||
Map.put(acc, to_string(k), v)
|
||||
{k, v, v2}, acc when is_tuple(v) when k == :nameserver or k == :nameservers or k == :alt_nameserver ->
|
||||
Map.put(acc, to_string(k), "#{:inet.ntoa(v)}:#{v2}")
|
||||
{k, v}, acc when is_tuple(v) when k == :nameserver or k == :nameservers or k == :alt_nameserver ->
|
||||
Map.put(acc, to_string(k), to_string(:inet.ntoa(v)))
|
||||
{k, v}, acc ->
|
||||
Map.put(acc, to_string(k), to_string(v))
|
||||
end)
|
||||
Enum.reduce(list, %{}, fn
|
||||
hosts, acc when is_list(hosts) ->
|
||||
Map.put(acc, "hosts", host_resolution_map(hosts))
|
||||
|
||||
{k, v}, acc when k == :domain or k == :resolv_conf or k == :hosts_file ->
|
||||
Map.put(acc, to_string(k), to_string(v))
|
||||
|
||||
{k, v}, acc when is_list(v) when k == :search or k == :lookup ->
|
||||
Map.put(acc, to_string(k), Enum.join(Enum.map(v, &to_string/1), ", "))
|
||||
|
||||
{k, v}, acc when is_integer(v) ->
|
||||
Map.put(acc, to_string(k), v)
|
||||
|
||||
{k, v, v2}, acc
|
||||
when is_tuple(v)
|
||||
when k == :nameserver or k == :nameservers or k == :alt_nameserver ->
|
||||
Map.put(acc, to_string(k), "#{:inet.ntoa(v)}:#{v2}")
|
||||
|
||||
{k, v}, acc
|
||||
when is_tuple(v)
|
||||
when k == :nameserver or k == :nameservers or k == :alt_nameserver ->
|
||||
Map.put(acc, to_string(k), to_string(:inet.ntoa(v)))
|
||||
|
||||
{k, v}, acc ->
|
||||
Map.put(acc, to_string(k), to_string(v))
|
||||
end)
|
||||
end
|
||||
|
||||
def host_resolution_map(hosts) do
|
||||
Enum.reduce(hosts, %{},
|
||||
fn {:host, address, hosts}, acc ->
|
||||
Map.put(acc, to_string(:inet.ntoa(address)), Enum.map(hosts, &to_string/1))
|
||||
end)
|
||||
Enum.reduce(hosts, %{}, fn {:host, address, hosts}, acc ->
|
||||
Map.put(acc, to_string(:inet.ntoa(address)), Enum.map(hosts, &to_string/1))
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -70,19 +70,22 @@ defmodule RabbitMQ.CLI.Core.Parser do
|
|||
{[_alias_command_name | cmd_arguments], cmd_options, cmd_invalid} =
|
||||
parse_alias(input, command_name, alias_module, alias_content, options)
|
||||
|
||||
{alias_module, command_name, cmd_arguments, Helpers.atomize_values(cmd_options, @atomized_options), cmd_invalid}
|
||||
{alias_module, command_name, cmd_arguments,
|
||||
Helpers.atomize_values(cmd_options, @atomized_options), cmd_invalid}
|
||||
|
||||
command_module when is_atom(command_module) ->
|
||||
{[^command_name | cmd_arguments], cmd_options, cmd_invalid} =
|
||||
parse_command_specific(input, command_module, options)
|
||||
|
||||
{command_module, command_name, cmd_arguments, Helpers.atomize_values(cmd_options, @atomized_options), cmd_invalid}
|
||||
{command_module, command_name, cmd_arguments,
|
||||
Helpers.atomize_values(cmd_options, @atomized_options), cmd_invalid}
|
||||
end
|
||||
end
|
||||
|
||||
def command_suggestion(_cmd_name, empty) when empty == %{} do
|
||||
nil
|
||||
end
|
||||
|
||||
def command_suggestion(typed, module_map) do
|
||||
RabbitMQ.CLI.AutoComplete.suggest_command(typed, module_map)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ defmodule RabbitMQ.CLI.Core.Platform do
|
|||
|
||||
def line_separator() do
|
||||
case :os.type() do
|
||||
{:unix, _} -> "\n"
|
||||
{:unix, _} -> "\n"
|
||||
{:win32, _} -> "\r\n"
|
||||
end
|
||||
end
|
||||
|
|
@ -22,15 +22,19 @@ defmodule RabbitMQ.CLI.Core.Platform do
|
|||
def os_name({:unix, :linux}) do
|
||||
"Linux"
|
||||
end
|
||||
|
||||
def os_name({:unix, :darwin}) do
|
||||
"macOS"
|
||||
end
|
||||
|
||||
def os_name({:unix, :freebsd}) do
|
||||
"FreeBSD"
|
||||
end
|
||||
|
||||
def os_name({:unix, name}) do
|
||||
name |> to_string |> String.capitalize
|
||||
name |> to_string |> String.capitalize()
|
||||
end
|
||||
|
||||
def os_name({:win32, _}) do
|
||||
"Windows"
|
||||
end
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ defmodule RabbitMQ.CLI.Core.Validators do
|
|||
alias RabbitMQ.CLI.Core.Helpers
|
||||
import RabbitMQ.CLI.Core.{CodePath, Paths}
|
||||
|
||||
|
||||
def chain([validator | rest], args) do
|
||||
case apply(validator, args) do
|
||||
:ok -> chain(rest, args)
|
||||
|
|
|
|||
|
|
@ -11,14 +11,14 @@ defmodule RabbitMQ.CLI.Core.Version do
|
|||
to_string(:rabbit_misc.version())
|
||||
end
|
||||
|
||||
|
||||
def remote_version(node_name) do
|
||||
remote_version(node_name, @default_timeout)
|
||||
end
|
||||
|
||||
def remote_version(node_name, timeout) do
|
||||
case :rabbit_misc.rpc_call(node_name, :rabbit_misc, :version, [], timeout) do
|
||||
{:badrpc, _} = err -> err
|
||||
val -> val
|
||||
val -> val
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -15,9 +15,11 @@ defmodule RabbitMQ.CLI.Ctl.Commands.AddUserCommand do
|
|||
def validate(args, _) when length(args) < 1, do: {:validation_failure, :not_enough_args}
|
||||
def validate(args, _) when length(args) > 2, do: {:validation_failure, :too_many_args}
|
||||
def validate([_], _), do: :ok
|
||||
|
||||
def validate(["", _], _) do
|
||||
{:validation_failure, {:bad_argument, "user cannot be an empty string"}}
|
||||
end
|
||||
|
||||
def validate([_, _], _), do: :ok
|
||||
|
||||
use RabbitMQ.CLI.Core.RequiresRabbitAppRunning
|
||||
|
|
@ -28,15 +30,19 @@ defmodule RabbitMQ.CLI.Ctl.Commands.AddUserCommand do
|
|||
# Credential validators can be used to require passwords of a certain length
|
||||
# or following a certain pattern. This is a core server responsibility. MK.
|
||||
case Input.infer_password("Password: ", opts) do
|
||||
:eof -> {:error, :not_enough_args}
|
||||
password -> :rabbit_misc.rpc_call(
|
||||
node_name,
|
||||
:rabbit_auth_backend_internal,
|
||||
:add_user,
|
||||
[username, password, Helpers.cli_acting_user()]
|
||||
)
|
||||
:eof ->
|
||||
{:error, :not_enough_args}
|
||||
|
||||
password ->
|
||||
:rabbit_misc.rpc_call(
|
||||
node_name,
|
||||
:rabbit_auth_backend_internal,
|
||||
:add_user,
|
||||
[username, password, Helpers.cli_acting_user()]
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
def run([username, password], %{node: node_name}) do
|
||||
:rabbit_misc.rpc_call(
|
||||
node_name,
|
||||
|
|
@ -49,28 +55,38 @@ defmodule RabbitMQ.CLI.Ctl.Commands.AddUserCommand do
|
|||
def output({:error, :not_enough_args}, _) do
|
||||
{:error, ExitCodes.exit_dataerr(), "Password is not provided via argument or stdin"}
|
||||
end
|
||||
|
||||
def output({:error, {:user_already_exists, username}}, %{node: node_name, formatter: "json"}) do
|
||||
{:error, %{"result" => "error", "node" => node_name, "message" => "User #{username} already exists"}}
|
||||
{:error,
|
||||
%{"result" => "error", "node" => node_name, "message" => "User #{username} already exists"}}
|
||||
end
|
||||
|
||||
def output({:error, {:user_already_exists, username}}, _) do
|
||||
{:error, ExitCodes.exit_software(), "User \"#{username}\" already exists"}
|
||||
end
|
||||
|
||||
def output(:ok, %{formatter: "json", node: node_name}) do
|
||||
m = %{
|
||||
"status" => "ok",
|
||||
"node" => node_name,
|
||||
"message" => "Done. Don't forget to grant the user permissions to some virtual hosts! See 'rabbitmqctl help set_permissions' to learn more."
|
||||
"status" => "ok",
|
||||
"node" => node_name,
|
||||
"message" =>
|
||||
"Done. Don't forget to grant the user permissions to some virtual hosts! See 'rabbitmqctl help set_permissions' to learn more."
|
||||
}
|
||||
|
||||
{:ok, m}
|
||||
end
|
||||
|
||||
def output(:ok, opts) do
|
||||
case output_less?(opts) do
|
||||
true ->
|
||||
:ok
|
||||
|
||||
false ->
|
||||
{:ok, "Done. Don't forget to grant the user permissions to some virtual hosts! See 'rabbitmqctl help set_permissions' to learn more."}
|
||||
{:ok,
|
||||
"Done. Don't forget to grant the user permissions to some virtual hosts! See 'rabbitmqctl help set_permissions' to learn more."}
|
||||
end
|
||||
end
|
||||
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
|
||||
def usage, do: "add_user <username> <password>"
|
||||
|
|
@ -78,7 +94,10 @@ defmodule RabbitMQ.CLI.Ctl.Commands.AddUserCommand do
|
|||
def usage_additional() do
|
||||
[
|
||||
["<username>", "Self-explanatory"],
|
||||
["<password>", "Password this user will authenticate with. Use a blank string to disable password-based authentication."]
|
||||
[
|
||||
"<password>",
|
||||
"Password this user will authenticate with. Use a blank string to disable password-based authentication."
|
||||
]
|
||||
]
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -9,54 +9,84 @@ defmodule RabbitMQ.CLI.Ctl.Commands.AddVhostCommand do
|
|||
|
||||
@behaviour RabbitMQ.CLI.CommandBehaviour
|
||||
|
||||
def switches(), do: [description: :string,
|
||||
tags: :string,
|
||||
default_queue_type: :string]
|
||||
def switches(), do: [description: :string, tags: :string, default_queue_type: :string]
|
||||
def aliases(), do: [d: :description]
|
||||
|
||||
def merge_defaults(args, opts) do
|
||||
{args, Map.merge(%{description: "", tags: ""}, opts)}
|
||||
end
|
||||
|
||||
use RabbitMQ.CLI.Core.AcceptsOnePositionalArgument
|
||||
use RabbitMQ.CLI.Core.RequiresRabbitAppRunning
|
||||
|
||||
def run([vhost], %{node: node_name, description: desc, tags: tags, default_queue_type: default_qt}) do
|
||||
meta = %{description: desc,
|
||||
tags: parse_tags(tags),
|
||||
default_queue_type: default_qt}
|
||||
def run([vhost], %{
|
||||
node: node_name,
|
||||
description: desc,
|
||||
tags: tags,
|
||||
default_queue_type: default_qt
|
||||
}) do
|
||||
meta = %{description: desc, tags: parse_tags(tags), default_queue_type: default_qt}
|
||||
# check if the respective feature flag is enabled
|
||||
case default_qt do
|
||||
"quorum" ->
|
||||
FeatureFlags.assert_feature_flag_enabled(node_name, :quorum_queue, fn () ->
|
||||
:rabbit_misc.rpc_call(node_name, :rabbit_vhost, :add, [vhost, meta, Helpers.cli_acting_user()])
|
||||
FeatureFlags.assert_feature_flag_enabled(node_name, :quorum_queue, fn ->
|
||||
:rabbit_misc.rpc_call(node_name, :rabbit_vhost, :add, [
|
||||
vhost,
|
||||
meta,
|
||||
Helpers.cli_acting_user()
|
||||
])
|
||||
end)
|
||||
|
||||
"stream" ->
|
||||
FeatureFlags.assert_feature_flag_enabled(node_name, :stream_queue, fn () ->
|
||||
:rabbit_misc.rpc_call(node_name, :rabbit_vhost, :add, [vhost, meta, Helpers.cli_acting_user()])
|
||||
FeatureFlags.assert_feature_flag_enabled(node_name, :stream_queue, fn ->
|
||||
:rabbit_misc.rpc_call(node_name, :rabbit_vhost, :add, [
|
||||
vhost,
|
||||
meta,
|
||||
Helpers.cli_acting_user()
|
||||
])
|
||||
end)
|
||||
|
||||
_ ->
|
||||
:rabbit_misc.rpc_call(node_name, :rabbit_vhost, :add, [vhost, meta, Helpers.cli_acting_user()])
|
||||
:rabbit_misc.rpc_call(node_name, :rabbit_vhost, :add, [
|
||||
vhost,
|
||||
meta,
|
||||
Helpers.cli_acting_user()
|
||||
])
|
||||
end
|
||||
end
|
||||
|
||||
def run([vhost], %{node: node_name, description: desc, tags: tags}) do
|
||||
:rabbit_misc.rpc_call(node_name, :rabbit_vhost, :add, [vhost, desc, tags, Helpers.cli_acting_user()])
|
||||
:rabbit_misc.rpc_call(node_name, :rabbit_vhost, :add, [
|
||||
vhost,
|
||||
desc,
|
||||
tags,
|
||||
Helpers.cli_acting_user()
|
||||
])
|
||||
end
|
||||
|
||||
def run([vhost], %{node: node_name}) do
|
||||
:rabbit_misc.rpc_call(node_name, :rabbit_vhost, :add, [vhost, Helpers.cli_acting_user()])
|
||||
end
|
||||
|
||||
def output({:error, :invalid_queue_type}, _opts) do
|
||||
{:error, ExitCodes.exit_usage, "Unsupported default queue type"}
|
||||
{:error, ExitCodes.exit_usage(), "Unsupported default queue type"}
|
||||
end
|
||||
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
|
||||
def usage, do: "add_vhost <vhost> [--description <description> --tags \"<tag1>,<tag2>,<...>\" --default-queue-type <quorum|classic|stream>]"
|
||||
def usage,
|
||||
do:
|
||||
"add_vhost <vhost> [--description <description> --tags \"<tag1>,<tag2>,<...>\" --default-queue-type <quorum|classic|stream>]"
|
||||
|
||||
def usage_additional() do
|
||||
[
|
||||
["<vhost>", "Virtual host name"],
|
||||
["--description <description>", "Virtual host description"],
|
||||
["--tags <tags>", "Command separated list of tags"],
|
||||
["--default-queue-type <quorum|classic|stream>", "Queue type to use if no type is explicitly provided by the client"]
|
||||
[
|
||||
"--default-queue-type <quorum|classic|stream>",
|
||||
"Queue type to use if no type is explicitly provided by the client"
|
||||
]
|
||||
]
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -24,15 +24,19 @@ defmodule RabbitMQ.CLI.Ctl.Commands.AuthenticateUserCommand do
|
|||
# Credential validators can be used to require passwords of a certain length
|
||||
# or following a certain pattern. This is a core server responsibility. MK.
|
||||
case Input.infer_password("Password: ", opts) do
|
||||
:eof -> {:error, :not_enough_args}
|
||||
password -> :rabbit_misc.rpc_call(
|
||||
node_name,
|
||||
:rabbit_access_control,
|
||||
:check_user_pass_login,
|
||||
[user, password]
|
||||
)
|
||||
:eof ->
|
||||
{:error, :not_enough_args}
|
||||
|
||||
password ->
|
||||
:rabbit_misc.rpc_call(
|
||||
node_name,
|
||||
:rabbit_access_control,
|
||||
:check_user_pass_login,
|
||||
[user, password]
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
def run([user, password], %{node: node_name}) do
|
||||
:rabbit_misc.rpc_call(
|
||||
node_name,
|
||||
|
|
@ -59,18 +63,21 @@ defmodule RabbitMQ.CLI.Ctl.Commands.AuthenticateUserCommand do
|
|||
|
||||
def help_section(), do: :user_management
|
||||
|
||||
def description(), do: "Attempts to authenticate a user. Exits with a non-zero code if authentication fails."
|
||||
def description(),
|
||||
do: "Attempts to authenticate a user. Exits with a non-zero code if authentication fails."
|
||||
|
||||
def banner([username | _], _), do: "Authenticating user \"#{username}\" ..."
|
||||
|
||||
def output({:error, :not_enough_args}, _) do
|
||||
{:error, ExitCodes.exit_software(), "Password is not provided via argument or stdin"}
|
||||
end
|
||||
|
||||
def output({:refused, user, msg, args}, _) do
|
||||
{:error, RabbitMQ.CLI.Core.ExitCodes.exit_dataerr(),
|
||||
"Error: failed to authenticate user \"#{user}\"\n" <>
|
||||
to_string(:io_lib.format(msg, args))}
|
||||
end
|
||||
|
||||
def output({:ok, _user}, _) do
|
||||
{:ok, "Success"}
|
||||
end
|
||||
|
|
|
|||
|
|
@ -26,11 +26,13 @@ defmodule RabbitMQ.CLI.Ctl.Commands.AutocompleteCommand do
|
|||
def run(args, %{script_name: script_name}) do
|
||||
{:stream, RabbitMQ.CLI.AutoComplete.complete(script_name, args)}
|
||||
end
|
||||
|
||||
def run(args, opts) do
|
||||
script_name = Config.get_system_option(:script_name, opts)
|
||||
|
||||
{:stream, RabbitMQ.CLI.AutoComplete.complete(script_name, args)}
|
||||
end
|
||||
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
|
||||
def usage() do
|
||||
|
|
|
|||
|
|
@ -37,9 +37,7 @@ defmodule RabbitMQ.CLI.Ctl.Commands.AwaitOnlineNodesCommand do
|
|||
use RabbitMQ.CLI.DefaultOutput
|
||||
|
||||
def banner([count], %{node: node_name, timeout: timeout}) when is_number(timeout) do
|
||||
"Will wait for at least #{count} nodes to join the cluster of #{node_name}. Timeout: #{
|
||||
trunc(timeout / 1000)
|
||||
} seconds."
|
||||
"Will wait for at least #{count} nodes to join the cluster of #{node_name}. Timeout: #{trunc(timeout / 1000)} seconds."
|
||||
end
|
||||
|
||||
def banner([count], %{node: node_name, timeout: _timeout}) do
|
||||
|
|
@ -52,7 +50,10 @@ defmodule RabbitMQ.CLI.Ctl.Commands.AwaitOnlineNodesCommand do
|
|||
|
||||
def usage_additional() do
|
||||
[
|
||||
["<count>", "how many cluster members must be up in order for this command to exit. When <count> is 1, always exits immediately."]
|
||||
[
|
||||
"<count>",
|
||||
"how many cluster members must be up in order for this command to exit. When <count> is 1, always exits immediately."
|
||||
]
|
||||
]
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -24,15 +24,19 @@ defmodule RabbitMQ.CLI.Ctl.Commands.ChangePasswordCommand do
|
|||
# Credential validators can be used to require passwords of a certain length
|
||||
# or following a certain pattern. This is a core server responsibility. MK.
|
||||
case Input.infer_password("Password: ", opts) do
|
||||
:eof -> {:error, :not_enough_args}
|
||||
password -> :rabbit_misc.rpc_call(
|
||||
node_name,
|
||||
:rabbit_auth_backend_internal,
|
||||
:change_password,
|
||||
[username, password, Helpers.cli_acting_user()]
|
||||
)
|
||||
:eof ->
|
||||
{:error, :not_enough_args}
|
||||
|
||||
password ->
|
||||
:rabbit_misc.rpc_call(
|
||||
node_name,
|
||||
:rabbit_auth_backend_internal,
|
||||
:change_password,
|
||||
[username, password, Helpers.cli_acting_user()]
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
def run([username, password], %{node: node_name}) do
|
||||
:rabbit_misc.rpc_call(
|
||||
node_name,
|
||||
|
|
@ -45,12 +49,16 @@ defmodule RabbitMQ.CLI.Ctl.Commands.ChangePasswordCommand do
|
|||
def output({:error, :not_enough_args}, _) do
|
||||
{:error, ExitCodes.exit_software(), "Password is not provided via argument or stdin"}
|
||||
end
|
||||
|
||||
def output({:error, {:no_such_user, username}}, %{node: node_name, formatter: "json"}) do
|
||||
{:error, %{"result" => "error", "node" => node_name, "message" => "User #{username} does not exists"}}
|
||||
{:error,
|
||||
%{"result" => "error", "node" => node_name, "message" => "User #{username} does not exists"}}
|
||||
end
|
||||
|
||||
def output({:error, {:no_such_user, username}}, _) do
|
||||
{:error, ExitCodes.exit_nouser(), "User \"#{username}\" does not exist"}
|
||||
end
|
||||
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
|
||||
def usage, do: "change_password <username> <password>"
|
||||
|
|
|
|||
|
|
@ -25,17 +25,27 @@ defmodule RabbitMQ.CLI.Ctl.Commands.ClearPermissionsCommand do
|
|||
end
|
||||
|
||||
def output({:error, {:no_such_user, username}}, %{node: node_name, formatter: "json"}) do
|
||||
{:error, %{"result" => "error", "node" => node_name, "message" => "User #{username} does not exist"}}
|
||||
{:error,
|
||||
%{"result" => "error", "node" => node_name, "message" => "User #{username} does not exist"}}
|
||||
end
|
||||
|
||||
def output({:error, {:no_such_vhost, vhost}}, %{node: node_name, formatter: "json"}) do
|
||||
{:error, %{"result" => "error", "node" => node_name, "message" => "Virtual host #{vhost} does not exist"}}
|
||||
{:error,
|
||||
%{
|
||||
"result" => "error",
|
||||
"node" => node_name,
|
||||
"message" => "Virtual host #{vhost} does not exist"
|
||||
}}
|
||||
end
|
||||
|
||||
def output({:error, {:no_such_user, username}}, _) do
|
||||
{:error, ExitCodes.exit_nouser(), "User #{username} does not exist"}
|
||||
end
|
||||
|
||||
def output({:error, {:no_such_vhost, vhost}}, _) do
|
||||
{:error, "Virtual host #{vhost} does not exist"}
|
||||
end
|
||||
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
|
||||
def usage, do: "clear_permissions [--vhost <vhost>] <username>"
|
||||
|
|
|
|||
|
|
@ -44,17 +44,27 @@ defmodule RabbitMQ.CLI.Ctl.Commands.ClearTopicPermissionsCommand do
|
|||
end
|
||||
|
||||
def output({:error, {:no_such_user, username}}, %{node: node_name, formatter: "json"}) do
|
||||
{:error, %{"result" => "error", "node" => node_name, "message" => "User #{username} does not exist"}}
|
||||
{:error,
|
||||
%{"result" => "error", "node" => node_name, "message" => "User #{username} does not exist"}}
|
||||
end
|
||||
|
||||
def output({:error, {:no_such_vhost, vhost}}, %{node: node_name, formatter: "json"}) do
|
||||
{:error, %{"result" => "error", "node" => node_name, "message" => "Virtual host #{vhost} does not exist"}}
|
||||
{:error,
|
||||
%{
|
||||
"result" => "error",
|
||||
"node" => node_name,
|
||||
"message" => "Virtual host #{vhost} does not exist"
|
||||
}}
|
||||
end
|
||||
|
||||
def output({:error, {:no_such_user, username}}, _) do
|
||||
{:error, ExitCodes.exit_nouser(), "User #{username} does not exist"}
|
||||
end
|
||||
|
||||
def output({:error, {:no_such_vhost, vhost}}, _) do
|
||||
{:error, "Virtual host #{vhost} does not exist"}
|
||||
end
|
||||
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
|
||||
def usage, do: "clear_topic_permissions [--vhost <vhost>] <username> [<exchange>]"
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@ defmodule RabbitMQ.CLI.Ctl.Commands.ClearUserLimitsCommand do
|
|||
def banner([username, "all"], %{}) do
|
||||
"Clearing all limits for user \"#{username}\" ..."
|
||||
end
|
||||
|
||||
def banner([username, limit_type], %{}) do
|
||||
"Clearing \"#{limit_type}\" limit for user \"#{username}\" ..."
|
||||
end
|
||||
|
|
|
|||
|
|
@ -62,18 +62,19 @@ defmodule RabbitMQ.CLI.Ctl.Commands.CloseAllConnectionsCommand do
|
|||
per_connection_delay: delay,
|
||||
limit: limit
|
||||
}) do
|
||||
run(args, %{
|
||||
node: node_name,
|
||||
vhost: nil,
|
||||
global: global_opt,
|
||||
per_connection_delay: delay,
|
||||
limit: limit
|
||||
})
|
||||
run(args, %{
|
||||
node: node_name,
|
||||
vhost: nil,
|
||||
global: global_opt,
|
||||
per_connection_delay: delay,
|
||||
limit: limit
|
||||
})
|
||||
end
|
||||
|
||||
def output({:stream, stream}, _opts) do
|
||||
{:stream, Stream.filter(stream, fn x -> x != :ok end)}
|
||||
end
|
||||
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
|
||||
def banner([explanation], %{node: node_name, global: true}) do
|
||||
|
|
@ -108,7 +109,9 @@ defmodule RabbitMQ.CLI.Ctl.Commands.CloseAllConnectionsCommand do
|
|||
|
||||
def help_section(), do: :operations
|
||||
|
||||
def description(), do: "Instructs the broker to close all connections for the specified vhost or entire RabbitMQ node"
|
||||
def description(),
|
||||
do:
|
||||
"Instructs the broker to close all connections for the specified vhost or entire RabbitMQ node"
|
||||
|
||||
#
|
||||
# Implementation
|
||||
|
|
|
|||
|
|
@ -40,7 +40,8 @@ defmodule RabbitMQ.CLI.Ctl.Commands.CloseConnectionCommand do
|
|||
|
||||
def help_section(), do: :operations
|
||||
|
||||
def description(), do: "Instructs the broker to close the connection associated with the Erlang process id"
|
||||
def description(),
|
||||
do: "Instructs the broker to close the connection associated with the Erlang process id"
|
||||
|
||||
def banner([pid, explanation], _), do: "Closing connection #{pid}, reason: #{explanation}..."
|
||||
end
|
||||
|
|
|
|||
|
|
@ -47,24 +47,35 @@ defmodule RabbitMQ.CLI.Ctl.Commands.ClusterStatusCommand do
|
|||
|
||||
nodes ->
|
||||
count = length(nodes)
|
||||
alarms_by_node = Enum.map(nodes, fn n -> alarms_by_node(n, per_node_timeout(timeout, count)) end)
|
||||
listeners_by_node = Enum.map(nodes, fn n -> listeners_of(n, per_node_timeout(timeout, count)) end)
|
||||
versions_by_node = Enum.map(nodes, fn n -> versions_by_node(n, per_node_timeout(timeout, count)) end)
|
||||
maintenance_status_by_node = Enum.map(nodes,
|
||||
fn n -> maintenance_status_by_node(n, per_node_timeout(timeout, count)) end)
|
||||
|
||||
feature_flags = case :rabbit_misc.rpc_call(node_name, :rabbit_ff_extra, :cli_info, [], timeout) do
|
||||
{:badrpc, {:EXIT, {:undef, _}}} -> []
|
||||
{:badrpc, _} = err -> err
|
||||
val -> val
|
||||
end
|
||||
alarms_by_node =
|
||||
Enum.map(nodes, fn n -> alarms_by_node(n, per_node_timeout(timeout, count)) end)
|
||||
|
||||
listeners_by_node =
|
||||
Enum.map(nodes, fn n -> listeners_of(n, per_node_timeout(timeout, count)) end)
|
||||
|
||||
versions_by_node =
|
||||
Enum.map(nodes, fn n -> versions_by_node(n, per_node_timeout(timeout, count)) end)
|
||||
|
||||
maintenance_status_by_node =
|
||||
Enum.map(
|
||||
nodes,
|
||||
fn n -> maintenance_status_by_node(n, per_node_timeout(timeout, count)) end
|
||||
)
|
||||
|
||||
feature_flags =
|
||||
case :rabbit_misc.rpc_call(node_name, :rabbit_ff_extra, :cli_info, [], timeout) do
|
||||
{:badrpc, {:EXIT, {:undef, _}}} -> []
|
||||
{:badrpc, _} = err -> err
|
||||
val -> val
|
||||
end
|
||||
|
||||
status ++
|
||||
[{:alarms, alarms_by_node}] ++
|
||||
[{:listeners, listeners_by_node}] ++
|
||||
[{:versions, versions_by_node}] ++
|
||||
[{:maintenance_status, maintenance_status_by_node}] ++
|
||||
[{:feature_flags, feature_flags}]
|
||||
[{:alarms, alarms_by_node}] ++
|
||||
[{:listeners, listeners_by_node}] ++
|
||||
[{:versions, versions_by_node}] ++
|
||||
[{:maintenance_status, maintenance_status_by_node}] ++
|
||||
[{:feature_flags, feature_flags}]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -80,11 +91,12 @@ defmodule RabbitMQ.CLI.Ctl.Commands.ClusterStatusCommand do
|
|||
|
||||
def output(result, %{formatter: "json"}) when is_list(result) do
|
||||
# format more data structures as map for sensible JSON output
|
||||
m = result_map(result)
|
||||
|> Map.update(:alarms, [], fn xs -> alarm_maps(xs) end)
|
||||
|> Map.update(:listeners, %{}, fn m ->
|
||||
Enum.map(m, fn {n, xs} -> {n, listener_maps(xs)} end) |> Enum.into(%{})
|
||||
end)
|
||||
m =
|
||||
result_map(result)
|
||||
|> Map.update(:alarms, [], fn xs -> alarm_maps(xs) end)
|
||||
|> Map.update(:listeners, %{}, fn m ->
|
||||
Enum.map(m, fn {n, xs} -> {n, listener_maps(xs)} end) |> Enum.into(%{})
|
||||
end)
|
||||
|
||||
{:ok, m}
|
||||
end
|
||||
|
|
@ -97,62 +109,88 @@ defmodule RabbitMQ.CLI.Ctl.Commands.ClusterStatusCommand do
|
|||
"Cluster name: #{m[:cluster_name]}"
|
||||
]
|
||||
|
||||
disk_nodes_section = [
|
||||
"\n#{bright("Disk Nodes")}\n",
|
||||
] ++ node_lines(m[:disk_nodes])
|
||||
disk_nodes_section =
|
||||
[
|
||||
"\n#{bright("Disk Nodes")}\n"
|
||||
] ++ node_lines(m[:disk_nodes])
|
||||
|
||||
ram_nodes_section = case m[:ram_nodes] do
|
||||
[] -> []
|
||||
xs -> [
|
||||
"\n#{bright("RAM Nodes")}\n",
|
||||
] ++ node_lines(xs)
|
||||
end
|
||||
ram_nodes_section =
|
||||
case m[:ram_nodes] do
|
||||
[] ->
|
||||
[]
|
||||
|
||||
running_nodes_section = [
|
||||
"\n#{bright("Running Nodes")}\n",
|
||||
] ++ node_lines(m[:running_nodes])
|
||||
xs ->
|
||||
[
|
||||
"\n#{bright("RAM Nodes")}\n"
|
||||
] ++ node_lines(xs)
|
||||
end
|
||||
|
||||
versions_section = [
|
||||
"\n#{bright("Versions")}\n",
|
||||
] ++ version_lines(m[:versions])
|
||||
running_nodes_section =
|
||||
[
|
||||
"\n#{bright("Running Nodes")}\n"
|
||||
] ++ node_lines(m[:running_nodes])
|
||||
|
||||
alarms_section = [
|
||||
"\n#{bright("Alarms")}\n",
|
||||
] ++ case m[:alarms] do
|
||||
[] -> ["(none)"]
|
||||
xs -> alarm_lines(xs, node_name)
|
||||
end
|
||||
versions_section =
|
||||
[
|
||||
"\n#{bright("Versions")}\n"
|
||||
] ++ version_lines(m[:versions])
|
||||
|
||||
partitions_section = [
|
||||
"\n#{bright("Network Partitions")}\n"
|
||||
] ++ case map_size(m[:partitions]) do
|
||||
0 -> ["(none)"]
|
||||
_ -> partition_lines(m[:partitions])
|
||||
end
|
||||
alarms_section =
|
||||
[
|
||||
"\n#{bright("Alarms")}\n"
|
||||
] ++
|
||||
case m[:alarms] do
|
||||
[] -> ["(none)"]
|
||||
xs -> alarm_lines(xs, node_name)
|
||||
end
|
||||
|
||||
listeners_section = [
|
||||
"\n#{bright("Listeners")}\n"
|
||||
] ++ case map_size(m[:listeners]) do
|
||||
0 -> ["(none)"]
|
||||
_ -> Enum.reduce(m[:listeners], [], fn {node, listeners}, acc ->
|
||||
acc ++ listener_lines(listeners, node)
|
||||
end)
|
||||
end
|
||||
partitions_section =
|
||||
[
|
||||
"\n#{bright("Network Partitions")}\n"
|
||||
] ++
|
||||
case map_size(m[:partitions]) do
|
||||
0 -> ["(none)"]
|
||||
_ -> partition_lines(m[:partitions])
|
||||
end
|
||||
|
||||
maintenance_section = [
|
||||
"\n#{bright("Maintenance status")}\n",
|
||||
] ++ maintenance_lines(m[:maintenance_status])
|
||||
listeners_section =
|
||||
[
|
||||
"\n#{bright("Listeners")}\n"
|
||||
] ++
|
||||
case map_size(m[:listeners]) do
|
||||
0 ->
|
||||
["(none)"]
|
||||
|
||||
feature_flags_section = [
|
||||
"\n#{bright("Feature flags")}\n"
|
||||
] ++ case Enum.count(m[:feature_flags]) do
|
||||
0 -> ["(none)"]
|
||||
_ -> feature_flag_lines(m[:feature_flags])
|
||||
end
|
||||
_ ->
|
||||
Enum.reduce(m[:listeners], [], fn {node, listeners}, acc ->
|
||||
acc ++ listener_lines(listeners, node)
|
||||
end)
|
||||
end
|
||||
|
||||
lines = cluster_name_section ++ disk_nodes_section ++ ram_nodes_section ++ running_nodes_section ++
|
||||
versions_section ++ maintenance_section ++ alarms_section ++ partitions_section ++
|
||||
listeners_section ++ feature_flags_section
|
||||
maintenance_section =
|
||||
[
|
||||
"\n#{bright("Maintenance status")}\n"
|
||||
] ++ maintenance_lines(m[:maintenance_status])
|
||||
|
||||
feature_flags_section =
|
||||
[
|
||||
"\n#{bright("Feature flags")}\n"
|
||||
] ++
|
||||
case Enum.count(m[:feature_flags]) do
|
||||
0 -> ["(none)"]
|
||||
_ -> feature_flag_lines(m[:feature_flags])
|
||||
end
|
||||
|
||||
lines =
|
||||
cluster_name_section ++
|
||||
disk_nodes_section ++
|
||||
ram_nodes_section ++
|
||||
running_nodes_section ++
|
||||
versions_section ++
|
||||
maintenance_section ++
|
||||
alarms_section ++
|
||||
partitions_section ++
|
||||
listeners_section ++ feature_flags_section
|
||||
|
||||
{:ok, Enum.join(lines, line_separator())}
|
||||
end
|
||||
|
|
@ -173,7 +211,9 @@ defmodule RabbitMQ.CLI.Ctl.Commands.ClusterStatusCommand do
|
|||
|
||||
def help_section(), do: :cluster_management
|
||||
|
||||
def description(), do: "Displays all the nodes in the cluster grouped by node type, together with the currently running nodes"
|
||||
def description(),
|
||||
do:
|
||||
"Displays all the nodes in the cluster grouped by node type, together with the currently running nodes"
|
||||
|
||||
def banner(_, %{node: node_name}), do: "Cluster status of node #{node_name} ..."
|
||||
|
||||
|
|
@ -195,20 +235,22 @@ defmodule RabbitMQ.CLI.Ctl.Commands.ClusterStatusCommand do
|
|||
disk_nodes: result |> Keyword.get(:nodes, []) |> Keyword.get(:disc, []),
|
||||
ram_nodes: result |> Keyword.get(:nodes, []) |> Keyword.get(:ram, []),
|
||||
running_nodes: result |> Keyword.get(:running_nodes, []) |> Enum.map(&to_string/1),
|
||||
alarms: Keyword.get(result, :alarms) |> Keyword.values |> Enum.concat |> Enum.uniq,
|
||||
alarms: Keyword.get(result, :alarms) |> Keyword.values() |> Enum.concat() |> Enum.uniq(),
|
||||
maintenance_status: Keyword.get(result, :maintenance_status, []) |> Enum.into(%{}),
|
||||
partitions: Keyword.get(result, :partitions, []) |> Enum.into(%{}),
|
||||
listeners: Keyword.get(result, :listeners, []) |> Enum.into(%{}),
|
||||
versions: Keyword.get(result, :versions, []) |> Enum.into(%{}),
|
||||
feature_flags: Keyword.get(result, :feature_flags, []) |> Enum.map(fn ff -> Enum.into(ff, %{}) end)
|
||||
feature_flags:
|
||||
Keyword.get(result, :feature_flags, []) |> Enum.map(fn ff -> Enum.into(ff, %{}) end)
|
||||
}
|
||||
end
|
||||
|
||||
defp alarms_by_node(node, timeout) do
|
||||
alarms = case :rabbit_misc.rpc_call(to_atom(node), :rabbit, :alarms, [], timeout) do
|
||||
{:badrpc, _} -> []
|
||||
xs -> xs
|
||||
end
|
||||
alarms =
|
||||
case :rabbit_misc.rpc_call(to_atom(node), :rabbit, :alarms, [], timeout) do
|
||||
{:badrpc, _} -> []
|
||||
xs -> xs
|
||||
end
|
||||
|
||||
{node, alarms}
|
||||
end
|
||||
|
|
@ -218,65 +260,103 @@ defmodule RabbitMQ.CLI.Ctl.Commands.ClusterStatusCommand do
|
|||
# in the cluster, so why do we run it on every node? See the badrpc clause,
|
||||
# some nodes may be inavailable or partitioned from other nodes. This way we
|
||||
# gather as complete a picture as possible. MK.
|
||||
listeners = case :rabbit_misc.rpc_call(to_atom(node), :rabbit_networking, :active_listeners, [], timeout) do
|
||||
{:badrpc, _} -> []
|
||||
xs -> xs
|
||||
end
|
||||
listeners =
|
||||
case :rabbit_misc.rpc_call(
|
||||
to_atom(node),
|
||||
:rabbit_networking,
|
||||
:active_listeners,
|
||||
[],
|
||||
timeout
|
||||
) do
|
||||
{:badrpc, _} -> []
|
||||
xs -> xs
|
||||
end
|
||||
|
||||
{node, listeners_on(listeners, node)}
|
||||
end
|
||||
|
||||
defp versions_by_node(node, timeout) do
|
||||
{rmq_name, rmq_vsn, otp_vsn} = case :rabbit_misc.rpc_call(
|
||||
to_atom(node), :rabbit, :product_info, [], timeout) do
|
||||
{:badrpc, _} ->
|
||||
{nil, nil, nil}
|
||||
map ->
|
||||
%{:otp_release => otp} = map
|
||||
name = case map do
|
||||
%{:product_name => v} -> v
|
||||
%{:product_base_name => v} -> v
|
||||
end
|
||||
vsn = case map do
|
||||
%{:product_version => v} -> v
|
||||
%{:product_base_version => v} -> v
|
||||
end
|
||||
{name, vsn, otp}
|
||||
end
|
||||
{rmq_name, rmq_vsn, otp_vsn} =
|
||||
case :rabbit_misc.rpc_call(
|
||||
to_atom(node),
|
||||
:rabbit,
|
||||
:product_info,
|
||||
[],
|
||||
timeout
|
||||
) do
|
||||
{:badrpc, _} ->
|
||||
{nil, nil, nil}
|
||||
|
||||
{node, %{rabbitmq_name: to_string(rmq_name), rabbitmq_version: to_string(rmq_vsn), erlang_version: to_string(otp_vsn)}}
|
||||
map ->
|
||||
%{:otp_release => otp} = map
|
||||
|
||||
name =
|
||||
case map do
|
||||
%{:product_name => v} -> v
|
||||
%{:product_base_name => v} -> v
|
||||
end
|
||||
|
||||
vsn =
|
||||
case map do
|
||||
%{:product_version => v} -> v
|
||||
%{:product_base_version => v} -> v
|
||||
end
|
||||
|
||||
{name, vsn, otp}
|
||||
end
|
||||
|
||||
{node,
|
||||
%{
|
||||
rabbitmq_name: to_string(rmq_name),
|
||||
rabbitmq_version: to_string(rmq_vsn),
|
||||
erlang_version: to_string(otp_vsn)
|
||||
}}
|
||||
end
|
||||
|
||||
defp maintenance_status_by_node(node, timeout) do
|
||||
target = to_atom(node)
|
||||
result = case :rabbit_misc.rpc_call(target,
|
||||
:rabbit_maintenance, :status_local_read, [target], timeout) do
|
||||
{:badrpc, _} -> "unknown"
|
||||
:regular -> "not under maintenance"
|
||||
:draining -> magenta("marked for maintenance")
|
||||
# forward compatibility: should we figure out a way to know when
|
||||
# draining completes (it involves inherently asynchronous cluster
|
||||
# operations such as quorum queue leader re-election), we'd introduce
|
||||
# a new state
|
||||
:drained -> magenta("marked for maintenance")
|
||||
value -> to_string(value)
|
||||
end
|
||||
|
||||
result =
|
||||
case :rabbit_misc.rpc_call(
|
||||
target,
|
||||
:rabbit_maintenance,
|
||||
:status_local_read,
|
||||
[target],
|
||||
timeout
|
||||
) do
|
||||
{:badrpc, _} -> "unknown"
|
||||
:regular -> "not under maintenance"
|
||||
:draining -> magenta("marked for maintenance")
|
||||
# forward compatibility: should we figure out a way to know when
|
||||
# draining completes (it involves inherently asynchronous cluster
|
||||
# operations such as quorum queue leader re-election), we'd introduce
|
||||
# a new state
|
||||
:drained -> magenta("marked for maintenance")
|
||||
value -> to_string(value)
|
||||
end
|
||||
|
||||
{node, result}
|
||||
end
|
||||
|
||||
defp node_lines(nodes) do
|
||||
Enum.map(nodes, &to_string/1) |> Enum.sort
|
||||
Enum.map(nodes, &to_string/1) |> Enum.sort()
|
||||
end
|
||||
|
||||
defp version_lines(mapping) do
|
||||
Enum.map(mapping, fn {node, %{rabbitmq_name: rmq_name, rabbitmq_version: rmq_vsn, erlang_version: otp_vsn}} ->
|
||||
"#{node}: #{rmq_name} #{rmq_vsn} on Erlang #{otp_vsn}"
|
||||
end)
|
||||
Enum.map(mapping, fn {node,
|
||||
%{
|
||||
rabbitmq_name: rmq_name,
|
||||
rabbitmq_version: rmq_vsn,
|
||||
erlang_version: otp_vsn
|
||||
}} ->
|
||||
"#{node}: #{rmq_name} #{rmq_vsn} on Erlang #{otp_vsn}"
|
||||
end)
|
||||
end
|
||||
|
||||
defp partition_lines(mapping) do
|
||||
Enum.map(mapping, fn {node, unreachable_peers} -> "Node #{node} cannot communicate with #{Enum.join(unreachable_peers, ", ")}" end)
|
||||
Enum.map(mapping, fn {node, unreachable_peers} ->
|
||||
"Node #{node} cannot communicate with #{Enum.join(unreachable_peers, ", ")}"
|
||||
end)
|
||||
end
|
||||
|
||||
defp maintenance_lines(mapping) do
|
||||
|
|
|
|||
|
|
@ -19,16 +19,22 @@ defmodule RabbitMQ.CLI.Ctl.Commands.DecodeCommand do
|
|||
iterations: :integer
|
||||
]
|
||||
end
|
||||
|
||||
@atomized_keys [:cipher, :hash]
|
||||
|
||||
def distribution(_), do: :none
|
||||
|
||||
def merge_defaults(args, opts) do
|
||||
with_defaults = Map.merge(%{
|
||||
cipher: :rabbit_pbe.default_cipher(),
|
||||
hash: :rabbit_pbe.default_hash(),
|
||||
iterations: :rabbit_pbe.default_iterations()
|
||||
}, opts)
|
||||
with_defaults =
|
||||
Map.merge(
|
||||
%{
|
||||
cipher: :rabbit_pbe.default_cipher(),
|
||||
hash: :rabbit_pbe.default_hash(),
|
||||
iterations: :rabbit_pbe.default_iterations()
|
||||
},
|
||||
opts
|
||||
)
|
||||
|
||||
{args, Helpers.atomize_values(with_defaults, @atomized_keys)}
|
||||
end
|
||||
|
||||
|
|
@ -60,7 +66,9 @@ defmodule RabbitMQ.CLI.Ctl.Commands.DecodeCommand do
|
|||
|
||||
def run([value], %{cipher: cipher, hash: hash, iterations: iterations} = opts) do
|
||||
case Input.consume_single_line_string_with_prompt("Passphrase: ", opts) do
|
||||
:eof -> {:error, :not_enough_args}
|
||||
:eof ->
|
||||
{:error, :not_enough_args}
|
||||
|
||||
passphrase ->
|
||||
try do
|
||||
term_value = Helpers.evaluate_input_as_term(value)
|
||||
|
|
@ -69,6 +77,7 @@ defmodule RabbitMQ.CLI.Ctl.Commands.DecodeCommand do
|
|||
case term_value do
|
||||
{:encrypted, _} = encrypted ->
|
||||
encrypted
|
||||
|
||||
_ ->
|
||||
{:encrypted, term_value}
|
||||
end
|
||||
|
|
@ -91,6 +100,7 @@ defmodule RabbitMQ.CLI.Ctl.Commands.DecodeCommand do
|
|||
case term_value do
|
||||
{:encrypted, _} = encrypted ->
|
||||
encrypted
|
||||
|
||||
_ ->
|
||||
{:encrypted, term_value}
|
||||
end
|
||||
|
|
@ -110,7 +120,8 @@ defmodule RabbitMQ.CLI.Ctl.Commands.DecodeCommand do
|
|||
"Decrypting value..."
|
||||
end
|
||||
|
||||
def usage, do: "decode value passphrase [--cipher <cipher>] [--hash <hash>] [--iterations <iterations>]"
|
||||
def usage,
|
||||
do: "decode value passphrase [--cipher <cipher>] [--hash <hash>] [--iterations <iterations>]"
|
||||
|
||||
def usage_additional() do
|
||||
[
|
||||
|
|
|
|||
|
|
@ -23,11 +23,14 @@ defmodule RabbitMQ.CLI.Ctl.Commands.DeleteUserCommand do
|
|||
end
|
||||
|
||||
def output({:error, {:no_such_user, username}}, %{node: node_name, formatter: "json"}) do
|
||||
{:error, %{"result" => "error", "node" => node_name, "message" => "User #{username} does not exists"}}
|
||||
{:error,
|
||||
%{"result" => "error", "node" => node_name, "message" => "User #{username} does not exists"}}
|
||||
end
|
||||
|
||||
def output({:error, {:no_such_user, username}}, _) do
|
||||
{:error, ExitCodes.exit_nouser(), "User \"#{username}\" does not exist"}
|
||||
end
|
||||
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
|
||||
def usage, do: "delete_user <username>"
|
||||
|
|
@ -46,7 +49,9 @@ defmodule RabbitMQ.CLI.Ctl.Commands.DeleteUserCommand do
|
|||
|
||||
def help_section(), do: :user_management
|
||||
|
||||
def description(), do: "Removes a user from the internal database. Has no effect on users provided by external backends such as LDAP"
|
||||
def description(),
|
||||
do:
|
||||
"Removes a user from the internal database. Has no effect on users provided by external backends such as LDAP"
|
||||
|
||||
def banner([arg], _), do: "Deleting user \"#{arg}\" ..."
|
||||
end
|
||||
|
|
|
|||
|
|
@ -4,15 +4,17 @@
|
|||
##
|
||||
## Copyright (c) 2018-2022 VMware, Inc. or its affiliates. All rights reserved.
|
||||
|
||||
|
||||
defmodule RabbitMQ.CLI.Ctl.Commands.EnableFeatureFlagCommand do
|
||||
@behaviour RabbitMQ.CLI.CommandBehaviour
|
||||
|
||||
def merge_defaults(args, opts), do: {args, opts}
|
||||
|
||||
def validate([], _), do: {:validation_failure, :not_enough_args}
|
||||
def validate([_|_] = args, _) when length(args) > 1, do: {:validation_failure, :too_many_args}
|
||||
def validate([""], _), do: {:validation_failure, {:bad_argument, "feature_flag cannot be an empty string."}}
|
||||
def validate([_ | _] = args, _) when length(args) > 1, do: {:validation_failure, :too_many_args}
|
||||
|
||||
def validate([""], _),
|
||||
do: {:validation_failure, {:bad_argument, "feature_flag cannot be an empty string."}}
|
||||
|
||||
def validate([_], _), do: :ok
|
||||
|
||||
use RabbitMQ.CLI.Core.RequiresRabbitAppRunning
|
||||
|
|
@ -22,37 +24,45 @@ defmodule RabbitMQ.CLI.Ctl.Commands.EnableFeatureFlagCommand do
|
|||
# Server does not support feature flags, consider none are available.
|
||||
# See rabbitmq/rabbitmq-cli#344 for context. MK.
|
||||
{:badrpc, {:EXIT, {:undef, _}}} -> {:error, :unsupported}
|
||||
{:badrpc, _} = err -> err
|
||||
other -> other
|
||||
{:badrpc, _} = err -> err
|
||||
other -> other
|
||||
end
|
||||
end
|
||||
|
||||
def run([feature_flag], %{node: node_name}) do
|
||||
case :rabbit_misc.rpc_call(node_name, :rabbit_feature_flags, :enable, [String.to_atom(feature_flag)]) do
|
||||
case :rabbit_misc.rpc_call(node_name, :rabbit_feature_flags, :enable, [
|
||||
String.to_atom(feature_flag)
|
||||
]) do
|
||||
# Server does not support feature flags, consider none are available.
|
||||
# See rabbitmq/rabbitmq-cli#344 for context. MK.
|
||||
{:badrpc, {:EXIT, {:undef, _}}} -> {:error, :unsupported}
|
||||
{:badrpc, _} = err -> err
|
||||
other -> other
|
||||
{:badrpc, _} = err -> err
|
||||
other -> other
|
||||
end
|
||||
end
|
||||
|
||||
def output({:error, :unsupported}, %{node: node_name}) do
|
||||
{:error, RabbitMQ.CLI.Core.ExitCodes.exit_usage, "This feature flag is not supported by node #{node_name}"}
|
||||
{:error, RabbitMQ.CLI.Core.ExitCodes.exit_usage(),
|
||||
"This feature flag is not supported by node #{node_name}"}
|
||||
end
|
||||
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
|
||||
def usage, do: "enable_feature_flag <all | feature_flag>"
|
||||
|
||||
def usage_additional() do
|
||||
[
|
||||
["<feature_flag>", "name of the feature flag to enable, or \"all\" to enable all supported flags"]
|
||||
]
|
||||
end
|
||||
[
|
||||
[
|
||||
"<feature_flag>",
|
||||
"name of the feature flag to enable, or \"all\" to enable all supported flags"
|
||||
]
|
||||
]
|
||||
end
|
||||
|
||||
def help_section(), do: :feature_flags
|
||||
|
||||
def description(), do: "Enables a feature flag or all supported feature flags on the target node"
|
||||
def description(),
|
||||
do: "Enables a feature flag or all supported feature flags on the target node"
|
||||
|
||||
def banner(["all"], _), do: "Enabling all feature flags ..."
|
||||
|
||||
|
|
|
|||
|
|
@ -17,16 +17,22 @@ defmodule RabbitMQ.CLI.Ctl.Commands.EncodeCommand do
|
|||
iterations: :integer
|
||||
]
|
||||
end
|
||||
|
||||
@atomized_keys [:cipher, :hash]
|
||||
|
||||
def distribution(_), do: :none
|
||||
|
||||
def merge_defaults(args, opts) do
|
||||
with_defaults = Map.merge(%{
|
||||
cipher: :rabbit_pbe.default_cipher(),
|
||||
hash: :rabbit_pbe.default_hash(),
|
||||
iterations: :rabbit_pbe.default_iterations()
|
||||
}, opts)
|
||||
with_defaults =
|
||||
Map.merge(
|
||||
%{
|
||||
cipher: :rabbit_pbe.default_cipher(),
|
||||
hash: :rabbit_pbe.default_hash(),
|
||||
iterations: :rabbit_pbe.default_iterations()
|
||||
},
|
||||
opts
|
||||
)
|
||||
|
||||
{args, Helpers.atomize_values(with_defaults, @atomized_keys)}
|
||||
end
|
||||
|
||||
|
|
@ -52,14 +58,22 @@ defmodule RabbitMQ.CLI.Ctl.Commands.EncodeCommand do
|
|||
|
||||
def run([], %{cipher: cipher, hash: hash, iterations: iterations} = opts) do
|
||||
case Input.consume_single_line_string_with_prompt("Value to encode: ", opts) do
|
||||
:eof -> {:error, :not_enough_args}
|
||||
:eof ->
|
||||
{:error, :not_enough_args}
|
||||
|
||||
value ->
|
||||
case Input.consume_single_line_string_with_prompt("Passphrase: ", opts) do
|
||||
:eof -> {:error, :not_enough_args}
|
||||
:eof ->
|
||||
{:error, :not_enough_args}
|
||||
|
||||
passphrase ->
|
||||
try do
|
||||
term_value = Helpers.evaluate_input_as_term(value)
|
||||
result = {:encrypted, _} = :rabbit_pbe.encrypt_term(cipher, hash, iterations, passphrase, term_value)
|
||||
|
||||
result =
|
||||
{:encrypted, _} =
|
||||
:rabbit_pbe.encrypt_term(cipher, hash, iterations, passphrase, term_value)
|
||||
|
||||
{:ok, result}
|
||||
catch
|
||||
_, _ ->
|
||||
|
|
@ -71,11 +85,17 @@ defmodule RabbitMQ.CLI.Ctl.Commands.EncodeCommand do
|
|||
|
||||
def run([value], %{cipher: cipher, hash: hash, iterations: iterations} = opts) do
|
||||
case Input.consume_single_line_string_with_prompt("Passphrase: ", opts) do
|
||||
:eof -> {:error, :not_enough_args}
|
||||
:eof ->
|
||||
{:error, :not_enough_args}
|
||||
|
||||
passphrase ->
|
||||
try do
|
||||
term_value = Helpers.evaluate_input_as_term(value)
|
||||
result = {:encrypted, _} = :rabbit_pbe.encrypt_term(cipher, hash, iterations, passphrase, term_value)
|
||||
|
||||
result =
|
||||
{:encrypted, _} =
|
||||
:rabbit_pbe.encrypt_term(cipher, hash, iterations, passphrase, term_value)
|
||||
|
||||
{:ok, result}
|
||||
catch
|
||||
_, _ ->
|
||||
|
|
@ -87,7 +107,11 @@ defmodule RabbitMQ.CLI.Ctl.Commands.EncodeCommand do
|
|||
def run([value, passphrase], %{cipher: cipher, hash: hash, iterations: iterations}) do
|
||||
try do
|
||||
term_value = Helpers.evaluate_input_as_term(value)
|
||||
result = {:encrypted, _} = :rabbit_pbe.encrypt_term(cipher, hash, iterations, passphrase, term_value)
|
||||
|
||||
result =
|
||||
{:encrypted, _} =
|
||||
:rabbit_pbe.encrypt_term(cipher, hash, iterations, passphrase, term_value)
|
||||
|
||||
{:ok, result}
|
||||
catch
|
||||
_, _ ->
|
||||
|
|
@ -101,7 +125,8 @@ defmodule RabbitMQ.CLI.Ctl.Commands.EncodeCommand do
|
|||
"Encrypting value ..."
|
||||
end
|
||||
|
||||
def usage, do: "encode value passphrase [--cipher <cipher>] [--hash <hash>] [--iterations <iterations>]"
|
||||
def usage,
|
||||
do: "encode value passphrase [--cipher <cipher>] [--hash <hash>] [--iterations <iterations>]"
|
||||
|
||||
def usage_additional() do
|
||||
[
|
||||
|
|
|
|||
|
|
@ -32,7 +32,9 @@ defmodule RabbitMQ.CLI.Ctl.Commands.EnvironmentCommand do
|
|||
|
||||
def help_section(), do: :configuration
|
||||
|
||||
def description(), do: "Displays the name and value of each variable in the application environment for each running application"
|
||||
def description(),
|
||||
do:
|
||||
"Displays the name and value of each variable in the application environment for each running application"
|
||||
|
||||
def banner(_, %{node: node_name}), do: "Application environment of node #{node_name} ..."
|
||||
end
|
||||
|
|
|
|||
|
|
@ -29,7 +29,9 @@ defmodule RabbitMQ.CLI.Ctl.Commands.EvalCommand do
|
|||
|
||||
def run([], %{node: node_name} = opts) do
|
||||
case Input.consume_multiline_string() do
|
||||
:eof -> {:error, :not_enough_args}
|
||||
:eof ->
|
||||
{:error, :not_enough_args}
|
||||
|
||||
expr ->
|
||||
case ErlEval.parse_expr(expr) do
|
||||
{:ok, parsed} ->
|
||||
|
|
@ -37,13 +39,15 @@ defmodule RabbitMQ.CLI.Ctl.Commands.EvalCommand do
|
|||
|
||||
case :rabbit_misc.rpc_call(node_name, :erl_eval, :exprs, [parsed, bindings]) do
|
||||
{:value, value, _} -> {:ok, value}
|
||||
err -> err
|
||||
err -> err
|
||||
end
|
||||
|
||||
{:error, msg} -> {:error, msg}
|
||||
{:error, msg} ->
|
||||
{:error, msg}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def run([expr | arguments], %{node: node_name} = opts) do
|
||||
case ErlEval.parse_expr(expr) do
|
||||
{:ok, parsed} ->
|
||||
|
|
@ -51,19 +55,23 @@ defmodule RabbitMQ.CLI.Ctl.Commands.EvalCommand do
|
|||
|
||||
case :rabbit_misc.rpc_call(node_name, :erl_eval, :exprs, [parsed, bindings]) do
|
||||
{:value, value, _} -> {:ok, value}
|
||||
err -> err
|
||||
err -> err
|
||||
end
|
||||
|
||||
{:error, msg} -> {:error, msg}
|
||||
{:error, msg} ->
|
||||
{:error, msg}
|
||||
end
|
||||
end
|
||||
|
||||
def output({:error, :not_enough_args}, _) do
|
||||
{:error, ExitCodes.exit_dataerr(), "Expression to evaluate is not provided via argument or stdin"}
|
||||
{:error, ExitCodes.exit_dataerr(),
|
||||
"Expression to evaluate is not provided via argument or stdin"}
|
||||
end
|
||||
|
||||
def output({:error, msg}, _) do
|
||||
{:error, ExitCodes.exit_dataerr(), "Evaluation failed: #{msg}"}
|
||||
end
|
||||
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
|
||||
def formatter(), do: RabbitMQ.CLI.Formatters.Erlang
|
||||
|
|
|
|||
|
|
@ -24,13 +24,16 @@ defmodule RabbitMQ.CLI.Ctl.Commands.EvalFileCommand do
|
|||
case File.read(file_path) do
|
||||
{:ok, expr} ->
|
||||
case ErlEval.parse_expr(expr) do
|
||||
{:ok, _} -> :ok
|
||||
{:ok, _} -> :ok
|
||||
{:error, err} -> {:validation_failure, err}
|
||||
end
|
||||
|
||||
{:error, :enoent} ->
|
||||
{:validation_failure, "File #{file_path} does not exist"}
|
||||
|
||||
{:error, :eacces} ->
|
||||
{:validation_failure, "Insufficient permissions to read file #{file_path}"}
|
||||
|
||||
{:error, err} ->
|
||||
{:validation_failure, err}
|
||||
end
|
||||
|
|
@ -38,15 +41,15 @@ defmodule RabbitMQ.CLI.Ctl.Commands.EvalFileCommand do
|
|||
|
||||
def run([file_path], opts) do
|
||||
case File.read(file_path) do
|
||||
{:ok, expr} -> EvalCommand.run([expr], opts)
|
||||
{:ok, expr} -> EvalCommand.run([expr], opts)
|
||||
{:error, err} -> {:error, err}
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def output({:error, msg}, _) do
|
||||
{:error, ExitCodes.exit_dataerr(), "Evaluation failed: #{msg}"}
|
||||
end
|
||||
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
|
||||
def formatter(), do: RabbitMQ.CLI.Formatters.Erlang
|
||||
|
|
@ -69,7 +72,8 @@ defmodule RabbitMQ.CLI.Ctl.Commands.EvalFileCommand do
|
|||
|
||||
def help_section(), do: :operations
|
||||
|
||||
def description(), do: "Evaluates a file that contains a snippet of Erlang code on the target node"
|
||||
def description(),
|
||||
do: "Evaluates a file that contains a snippet of Erlang code on the target node"
|
||||
|
||||
def banner(_, _), do: nil
|
||||
end
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ defmodule RabbitMQ.CLI.Ctl.Commands.ExportDefinitionsCommand do
|
|||
def merge_defaults(["-"] = args, opts) do
|
||||
{args, Map.merge(%{format: "json", silent: true}, Helpers.case_insensitive_format(opts))}
|
||||
end
|
||||
|
||||
def merge_defaults(args, opts) do
|
||||
{args, Map.merge(%{format: "json"}, Helpers.case_insensitive_format(opts))}
|
||||
end
|
||||
|
|
@ -23,23 +24,29 @@ defmodule RabbitMQ.CLI.Ctl.Commands.ExportDefinitionsCommand do
|
|||
when format != "json" and format != "JSON" and format != "erlang" do
|
||||
{:validation_failure, {:bad_argument, "Format should be either json or erlang"}}
|
||||
end
|
||||
|
||||
def validate([], _) do
|
||||
{:validation_failure, :not_enough_args}
|
||||
end
|
||||
|
||||
def validate(args, _) when length(args) > 1 do
|
||||
{:validation_failure, :too_many_args}
|
||||
end
|
||||
|
||||
# output to stdout
|
||||
def validate(["-"], _) do
|
||||
:ok
|
||||
end
|
||||
|
||||
def validate([path], _) do
|
||||
dir = Path.dirname(path)
|
||||
case File.exists?(dir, [raw: true]) do
|
||||
true -> :ok
|
||||
|
||||
case File.exists?(dir, raw: true) do
|
||||
true -> :ok
|
||||
false -> {:validation_failure, {:bad_argument, "Directory #{dir} does not exist"}}
|
||||
end
|
||||
end
|
||||
|
||||
def validate(_, _), do: :ok
|
||||
|
||||
use RabbitMQ.CLI.Core.RequiresRabbitAppRunning
|
||||
|
|
@ -51,46 +58,66 @@ defmodule RabbitMQ.CLI.Ctl.Commands.ExportDefinitionsCommand do
|
|||
result -> {:ok, result}
|
||||
end
|
||||
end
|
||||
|
||||
def run([path], %{node: node_name, timeout: timeout, format: format}) do
|
||||
case :rabbit_misc.rpc_call(node_name, :rabbit_definitions, :all_definitions, [], timeout) do
|
||||
{:badrpc, _} = err -> err
|
||||
{:error, _} = err -> err
|
||||
{:error, _, _} = err -> err
|
||||
result ->
|
||||
# write to the file in run/2 because output/2 is not meant to
|
||||
# produce side effects
|
||||
body = serialise(result, format)
|
||||
abs_path = Path.absname(path)
|
||||
{:badrpc, _} = err ->
|
||||
err
|
||||
|
||||
File.rm(abs_path)
|
||||
case File.write(abs_path, body) do
|
||||
# no output
|
||||
:ok -> {:ok, nil}
|
||||
{:error, :enoent} ->
|
||||
{:error, ExitCodes.exit_dataerr(), "Parent directory or file #{path} does not exist"}
|
||||
{:error, :enotdir} ->
|
||||
{:error, ExitCodes.exit_dataerr(), "Parent directory of file #{path} is not a directory"}
|
||||
{:error, :enospc} ->
|
||||
{:error, ExitCodes.exit_dataerr(), "No space left on device hosting #{path}"}
|
||||
{:error, :eacces} ->
|
||||
{:error, ExitCodes.exit_dataerr(), "No permissions to write to file #{path} or its parent directory"}
|
||||
{:error, :eisdir} ->
|
||||
{:error, ExitCodes.exit_dataerr(), "Path #{path} is a directory"}
|
||||
{:error, err} ->
|
||||
{:error, ExitCodes.exit_dataerr(), "Could not write to file #{path}: #{err}"}
|
||||
end
|
||||
{:error, _} = err ->
|
||||
err
|
||||
|
||||
{:error, _, _} = err ->
|
||||
err
|
||||
|
||||
result ->
|
||||
# write to the file in run/2 because output/2 is not meant to
|
||||
# produce side effects
|
||||
body = serialise(result, format)
|
||||
abs_path = Path.absname(path)
|
||||
|
||||
File.rm(abs_path)
|
||||
|
||||
case File.write(abs_path, body) do
|
||||
# no output
|
||||
:ok ->
|
||||
{:ok, nil}
|
||||
|
||||
{:error, :enoent} ->
|
||||
{:error, ExitCodes.exit_dataerr(), "Parent directory or file #{path} does not exist"}
|
||||
|
||||
{:error, :enotdir} ->
|
||||
{:error, ExitCodes.exit_dataerr(),
|
||||
"Parent directory of file #{path} is not a directory"}
|
||||
|
||||
{:error, :enospc} ->
|
||||
{:error, ExitCodes.exit_dataerr(), "No space left on device hosting #{path}"}
|
||||
|
||||
{:error, :eacces} ->
|
||||
{:error, ExitCodes.exit_dataerr(),
|
||||
"No permissions to write to file #{path} or its parent directory"}
|
||||
|
||||
{:error, :eisdir} ->
|
||||
{:error, ExitCodes.exit_dataerr(), "Path #{path} is a directory"}
|
||||
|
||||
{:error, err} ->
|
||||
{:error, ExitCodes.exit_dataerr(), "Could not write to file #{path}: #{err}"}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def output({:ok, nil}, _) do
|
||||
{:ok, nil}
|
||||
end
|
||||
|
||||
def output({:ok, result}, %{format: "json"}) when is_map(result) do
|
||||
{:ok, serialise(result, "json")}
|
||||
end
|
||||
|
||||
def output({:ok, result}, %{format: "erlang"}) when is_map(result) do
|
||||
{:ok, serialise(result, "erlang")}
|
||||
end
|
||||
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
|
||||
def printer(), do: RabbitMQ.CLI.Printers.StdIORaw
|
||||
|
|
@ -114,7 +141,8 @@ defmodule RabbitMQ.CLI.Ctl.Commands.ExportDefinitionsCommand do
|
|||
|
||||
def description(), do: "Exports definitions in JSON or compressed Erlang Term Format."
|
||||
|
||||
def banner([path], %{format: fmt}), do: "Exporting definitions in #{human_friendly_format(fmt)} to a file at \"#{path}\" ..."
|
||||
def banner([path], %{format: fmt}),
|
||||
do: "Exporting definitions in #{human_friendly_format(fmt)} to a file at \"#{path}\" ..."
|
||||
|
||||
#
|
||||
# Implementation
|
||||
|
|
@ -124,11 +152,13 @@ defmodule RabbitMQ.CLI.Ctl.Commands.ExportDefinitionsCommand do
|
|||
# make sure all runtime parameter values are maps, otherwise
|
||||
# they will end up being a list of pairs (a keyword list/proplist)
|
||||
# in the resulting JSON document
|
||||
map = Map.update!(raw_map, :parameters, fn(params) ->
|
||||
Enum.map(params, fn(param) ->
|
||||
Map.update!(param, "value", &:rabbit_data_coercion.to_map/1)
|
||||
map =
|
||||
Map.update!(raw_map, :parameters, fn params ->
|
||||
Enum.map(params, fn param ->
|
||||
Map.update!(param, "value", &:rabbit_data_coercion.to_map/1)
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
|
||||
{:ok, json} = JSON.encode(map)
|
||||
json
|
||||
end
|
||||
|
|
|
|||
|
|
@ -51,7 +51,9 @@ defmodule RabbitMQ.CLI.Ctl.Commands.ForceBootCommand do
|
|||
|
||||
def help_section(), do: :cluster_management
|
||||
|
||||
def description(), do: "Forces node to start even if it cannot contact or rejoin any of its previously known peers"
|
||||
def description(),
|
||||
do:
|
||||
"Forces node to start even if it cannot contact or rejoin any of its previously known peers"
|
||||
|
||||
def banner(_, _), do: nil
|
||||
end
|
||||
|
|
|
|||
|
|
@ -29,7 +29,10 @@ defmodule RabbitMQ.CLI.Ctl.Commands.ForceGcCommand do
|
|||
|
||||
def help_section(), do: :operations
|
||||
|
||||
def description, do: "Makes all Erlang processes on the target node perform/schedule a full sweep garbage collection"
|
||||
def description,
|
||||
do:
|
||||
"Makes all Erlang processes on the target node perform/schedule a full sweep garbage collection"
|
||||
|
||||
def banner([], %{node: node_name}), do: "Will ask all processes on node #{node_name} to schedule a full sweep GC"
|
||||
def banner([], %{node: node_name}),
|
||||
do: "Will ask all processes on node #{node_name} to schedule a full sweep GC"
|
||||
end
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ defmodule RabbitMQ.CLI.Ctl.Commands.ForceResetCommand do
|
|||
|
||||
def output({:error, :mnesia_unexpectedly_running}, %{node: node_name}) do
|
||||
{:error, ExitCodes.exit_software(),
|
||||
RabbitMQ.CLI.DefaultOutput.mnesia_running_error(node_name)}
|
||||
RabbitMQ.CLI.DefaultOutput.mnesia_running_error(node_name)}
|
||||
end
|
||||
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
|
|
|
|||
|
|
@ -46,19 +46,31 @@ defmodule RabbitMQ.CLI.Ctl.Commands.ForgetClusterNodeCommand do
|
|||
|
||||
def run([node_to_remove], %{node: node_name, offline: false}) do
|
||||
atom_name = to_atom(node_to_remove)
|
||||
args = [atom_name, false]
|
||||
args = [atom_name, false]
|
||||
|
||||
case :rabbit_misc.rpc_call(node_name, :rabbit_mnesia, :forget_cluster_node, args) do
|
||||
{:error, {:failed_to_remove_node, ^atom_name, {:active, _, _}}} ->
|
||||
{:error, "RabbitMQ on node #{node_to_remove} must be stopped with 'rabbitmqctl -n #{node_to_remove} stop_app' before it can be removed"};
|
||||
{:error, _} = error -> error;
|
||||
{:badrpc, _} = error -> error;
|
||||
{:error,
|
||||
"RabbitMQ on node #{node_to_remove} must be stopped with 'rabbitmqctl -n #{node_to_remove} stop_app' before it can be removed"}
|
||||
|
||||
{:error, _} = error ->
|
||||
error
|
||||
|
||||
{:badrpc, _} = error ->
|
||||
error
|
||||
|
||||
:ok ->
|
||||
case :rabbit_misc.rpc_call(node_name, :rabbit_quorum_queue, :shrink_all, [atom_name]) do
|
||||
{:error, _} ->
|
||||
{:error, "RabbitMQ failed to shrink some of the quorum queues on node #{node_to_remove}"};
|
||||
_ -> :ok
|
||||
{:error,
|
||||
"RabbitMQ failed to shrink some of the quorum queues on node #{node_to_remove}"}
|
||||
|
||||
_ ->
|
||||
:ok
|
||||
end
|
||||
other -> other
|
||||
|
||||
other ->
|
||||
other
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -70,7 +82,10 @@ defmodule RabbitMQ.CLI.Ctl.Commands.ForgetClusterNodeCommand do
|
|||
|
||||
def usage_additional() do
|
||||
[
|
||||
["--offline", "try to update cluster membership state directly. Use when target node is stopped. Only works for local nodes."]
|
||||
[
|
||||
"--offline",
|
||||
"try to update cluster membership state directly. Use when target node is stopped. Only works for local nodes."
|
||||
]
|
||||
]
|
||||
end
|
||||
|
||||
|
|
@ -88,6 +103,7 @@ defmodule RabbitMQ.CLI.Ctl.Commands.ForgetClusterNodeCommand do
|
|||
def banner([node_to_remove], %{offline: true}) do
|
||||
"Removing node #{node_to_remove} from the cluster. Warning: quorum queues cannot be shrunk in offline mode"
|
||||
end
|
||||
|
||||
def banner([node_to_remove], _) do
|
||||
"Removing node #{node_to_remove} from the cluster"
|
||||
end
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ defmodule RabbitMQ.CLI.Ctl.Commands.HelpCommand do
|
|||
|
||||
def validate([], _), do: :ok
|
||||
def validate([_command], _), do: :ok
|
||||
|
||||
def validate(args, _) when length(args) > 1 do
|
||||
{:validation_failure, :too_many_args}
|
||||
end
|
||||
|
|
@ -30,15 +31,19 @@ defmodule RabbitMQ.CLI.Ctl.Commands.HelpCommand do
|
|||
CommandModules.load(opts)
|
||||
|
||||
module_map = CommandModules.module_map()
|
||||
|
||||
case module_map[command_name] do
|
||||
nil ->
|
||||
# command not found
|
||||
# {:error, all_usage(opts)}
|
||||
case RabbitMQ.CLI.AutoComplete.suggest_command(command_name, module_map) do
|
||||
{:suggest, suggested} ->
|
||||
suggest_message = "\nCommand '#{command_name}' not found. \n" <>
|
||||
"Did you mean '#{suggested}'? \n"
|
||||
suggest_message =
|
||||
"\nCommand '#{command_name}' not found. \n" <>
|
||||
"Did you mean '#{suggested}'? \n"
|
||||
|
||||
{:error, ExitCodes.exit_usage(), suggest_message}
|
||||
|
||||
nil ->
|
||||
{:error, ExitCodes.exit_usage(), "\nCommand '#{command_name}' not found."}
|
||||
end
|
||||
|
|
@ -54,6 +59,7 @@ defmodule RabbitMQ.CLI.Ctl.Commands.HelpCommand do
|
|||
case opts[:list_commands] do
|
||||
true ->
|
||||
{:ok, commands_description()}
|
||||
|
||||
_ ->
|
||||
{:ok, all_usage(opts)}
|
||||
end
|
||||
|
|
@ -62,9 +68,11 @@ defmodule RabbitMQ.CLI.Ctl.Commands.HelpCommand do
|
|||
def output({:ok, result}, _) do
|
||||
{:ok, result}
|
||||
end
|
||||
|
||||
def output({:error, result}, _) do
|
||||
{:error, ExitCodes.exit_usage(), result}
|
||||
end
|
||||
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
|
||||
def banner(_, _), do: nil
|
||||
|
|
@ -81,25 +89,28 @@ defmodule RabbitMQ.CLI.Ctl.Commands.HelpCommand do
|
|||
]
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# Implementation
|
||||
#
|
||||
|
||||
def all_usage(opts) do
|
||||
tool_name = program_name(opts)
|
||||
|
||||
tool_usage(tool_name) ++
|
||||
["\n\nAvailable commands:\n"] ++ commands_description() ++
|
||||
help_footer(tool_name)
|
||||
["\n\nAvailable commands:\n"] ++
|
||||
commands_description() ++
|
||||
help_footer(tool_name)
|
||||
end
|
||||
|
||||
def command_usage(command, opts) do
|
||||
Enum.join([base_usage(command, opts)] ++
|
||||
command_description(command) ++
|
||||
additional_usage(command) ++
|
||||
relevant_doc_guides(command) ++
|
||||
general_options_usage(),
|
||||
"\n\n") <> "\n"
|
||||
Enum.join(
|
||||
[base_usage(command, opts)] ++
|
||||
command_description(command) ++
|
||||
additional_usage(command) ++
|
||||
relevant_doc_guides(command) ++
|
||||
general_options_usage(),
|
||||
"\n\n"
|
||||
) <> "\n"
|
||||
end
|
||||
|
||||
defp tool_usage(tool_name) do
|
||||
|
|
@ -137,7 +148,7 @@ defmodule RabbitMQ.CLI.Ctl.Commands.HelpCommand do
|
|||
|
||||
defp general_options_usage() do
|
||||
[
|
||||
"#{bright("General Options")}
|
||||
"#{bright("General Options")}
|
||||
|
||||
The following options are accepted by most or all commands.
|
||||
|
||||
|
|
@ -154,12 +165,13 @@ short | long | description
|
|||
| | virtual host to use
|
||||
| --formatter | alternative result formatter to use
|
||||
| if supported: json, pretty_table, table, csv, erlang
|
||||
not all commands support all (or any) alternative formatters."]
|
||||
not all commands support all (or any) alternative formatters."
|
||||
]
|
||||
end
|
||||
|
||||
defp command_description(command) do
|
||||
case CommandBehaviour.description(command) do
|
||||
"" -> []
|
||||
"" -> []
|
||||
other -> [other <> ".\n"]
|
||||
end
|
||||
end
|
||||
|
|
@ -167,9 +179,11 @@ short | long | description
|
|||
defp list_item_formatter([option, description]) do
|
||||
"#{option}\n\t#{description}\n"
|
||||
end
|
||||
|
||||
defp list_item_formatter({option, description}) do
|
||||
"#{option}\n\t#{description}\n"
|
||||
end
|
||||
|
||||
defp list_item_formatter(line) do
|
||||
"#{line}\n"
|
||||
end
|
||||
|
|
@ -180,8 +194,11 @@ short | long | description
|
|||
list when is_list(list) -> list |> Enum.map(&list_item_formatter/1)
|
||||
bin when is_binary(bin) -> ["#{bin}\n"]
|
||||
end
|
||||
|
||||
case command_usage do
|
||||
[] -> []
|
||||
[] ->
|
||||
[]
|
||||
|
||||
usage ->
|
||||
[flatten_string(["#{bright("Arguments and Options")}\n" | usage], "")]
|
||||
end
|
||||
|
|
@ -193,8 +210,11 @@ short | long | description
|
|||
list when is_list(list) -> list |> Enum.map(fn ln -> " * #{ln}\n" end)
|
||||
bin when is_binary(bin) -> [" * #{bin}\n"]
|
||||
end
|
||||
|
||||
case guide_list do
|
||||
[] -> []
|
||||
[] ->
|
||||
[]
|
||||
|
||||
usage ->
|
||||
[flatten_string(["#{bright("Relevant Doc Guides")}\n" | usage], "")]
|
||||
end
|
||||
|
|
@ -211,25 +231,25 @@ short | long | description
|
|||
defp commands_description() do
|
||||
module_map = CommandModules.module_map()
|
||||
|
||||
pad_commands_to = Enum.reduce(module_map, 0,
|
||||
fn({name, _}, longest) ->
|
||||
pad_commands_to =
|
||||
Enum.reduce(module_map, 0, fn {name, _}, longest ->
|
||||
name_length = String.length(name)
|
||||
|
||||
case name_length > longest do
|
||||
true -> name_length
|
||||
true -> name_length
|
||||
false -> longest
|
||||
end
|
||||
end)
|
||||
|
||||
lines = module_map
|
||||
|> Enum.map(
|
||||
fn({name, cmd}) ->
|
||||
lines =
|
||||
module_map
|
||||
|> Enum.map(fn {name, cmd} ->
|
||||
description = CommandBehaviour.description(cmd)
|
||||
help_section = CommandBehaviour.help_section(cmd)
|
||||
{name, {description, help_section}}
|
||||
end)
|
||||
|> Enum.group_by(fn({_, {_, help_section}}) -> help_section end)
|
||||
|> Enum.sort_by(
|
||||
fn({help_section, _}) ->
|
||||
|> Enum.group_by(fn {_, {_, help_section}} -> help_section end)
|
||||
|> Enum.sort_by(fn {help_section, _} ->
|
||||
case help_section do
|
||||
:deprecated -> 999
|
||||
:other -> 100
|
||||
|
|
@ -247,19 +267,16 @@ short | long | description
|
|||
_ -> 98
|
||||
end
|
||||
end)
|
||||
|> Enum.map(
|
||||
fn({help_section, section_helps}) ->
|
||||
|> Enum.map(fn {help_section, section_helps} ->
|
||||
[
|
||||
"\n" <> bright(section_head(help_section)) <> ":\n\n" |
|
||||
Enum.sort(section_helps)
|
||||
|> Enum.map(
|
||||
fn({name, {description, _}}) ->
|
||||
"\n" <> bright(section_head(help_section)) <> ":\n\n"
|
||||
| Enum.sort(section_helps)
|
||||
|> Enum.map(fn {name, {description, _}} ->
|
||||
" #{String.pad_trailing(name, pad_commands_to)} #{description}\n"
|
||||
end)
|
||||
]
|
||||
|
||||
end)
|
||||
|> Enum.concat()
|
||||
|> Enum.concat()
|
||||
|
||||
lines ++ ["\n"]
|
||||
end
|
||||
|
|
@ -268,34 +285,49 @@ short | long | description
|
|||
case help_section do
|
||||
:help ->
|
||||
"Help"
|
||||
|
||||
:user_management ->
|
||||
"Users"
|
||||
|
||||
:cluster_management ->
|
||||
"Cluster"
|
||||
|
||||
:replication ->
|
||||
"Replication"
|
||||
|
||||
:node_management ->
|
||||
"Nodes"
|
||||
|
||||
:queues ->
|
||||
"Queues"
|
||||
|
||||
:observability_and_health_checks ->
|
||||
"Monitoring, observability and health checks"
|
||||
|
||||
:virtual_hosts ->
|
||||
"Virtual hosts"
|
||||
"Virtual hosts"
|
||||
|
||||
:access_control ->
|
||||
"Access Control"
|
||||
|
||||
:parameters ->
|
||||
"Parameters"
|
||||
|
||||
:policies ->
|
||||
"Policies"
|
||||
|
||||
:configuration ->
|
||||
"Configuration and Environment"
|
||||
|
||||
:feature_flags ->
|
||||
"Feature flags"
|
||||
|
||||
:other ->
|
||||
"Other"
|
||||
|
||||
{:plugin, plugin} ->
|
||||
plugin_section(plugin) <> " plugin"
|
||||
|
||||
custom ->
|
||||
snake_case_to_capitalized_string(custom)
|
||||
end
|
||||
|
|
@ -307,16 +339,16 @@ short | long | description
|
|||
|
||||
defp format_known_plugin_name_fragments(value) do
|
||||
case value do
|
||||
["amqp1.0"] -> "AMQP 1.0"
|
||||
["amqp1.0"] -> "AMQP 1.0"
|
||||
["amqp1", "0"] -> "AMQP 1.0"
|
||||
["management"] -> "Management"
|
||||
["management", "agent"] -> "Management"
|
||||
["mqtt"] -> "MQTT"
|
||||
["stomp"] -> "STOMP"
|
||||
["web", "mqtt"] -> "Web MQTT"
|
||||
["management"] -> "Management"
|
||||
["management", "agent"] -> "Management"
|
||||
["mqtt"] -> "MQTT"
|
||||
["stomp"] -> "STOMP"
|
||||
["web", "mqtt"] -> "Web MQTT"
|
||||
["web", "stomp"] -> "Web STOMP"
|
||||
[other] -> snake_case_to_capitalized_string(other)
|
||||
fragments -> snake_case_to_capitalized_string(Enum.join(fragments, "_"))
|
||||
[other] -> snake_case_to_capitalized_string(other)
|
||||
fragments -> snake_case_to_capitalized_string(Enum.join(fragments, "_"))
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@ defmodule RabbitMQ.CLI.Ctl.Commands.HipeCompileCommand do
|
|||
def run([_target_dir], _opts) do
|
||||
:ok
|
||||
end
|
||||
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
|
||||
def usage, do: "hipe_compile <directory>"
|
||||
|
|
|
|||
|
|
@ -12,8 +12,10 @@ defmodule RabbitMQ.CLI.Ctl.Commands.ImportDefinitionsCommand do
|
|||
def merge_defaults(["-"] = args, opts) do
|
||||
{args, Map.merge(%{format: "json", silent: true}, Helpers.case_insensitive_format(opts))}
|
||||
end
|
||||
|
||||
def merge_defaults(args, opts) do
|
||||
{args, Map.merge(%{format: "json", skip_if_unchanged: false}, Helpers.case_insensitive_format(opts))}
|
||||
{args,
|
||||
Map.merge(%{format: "json", skip_if_unchanged: false}, Helpers.case_insensitive_format(opts))}
|
||||
end
|
||||
|
||||
def switches(), do: [timeout: :integer, format: :string, skip_if_unchanged: :boolean]
|
||||
|
|
@ -23,63 +25,86 @@ defmodule RabbitMQ.CLI.Ctl.Commands.ImportDefinitionsCommand do
|
|||
when format != "json" and format != "JSON" and format != "erlang" do
|
||||
{:validation_failure, {:bad_argument, "Format should be either json or erlang"}}
|
||||
end
|
||||
|
||||
def validate(args, _) when length(args) > 1 do
|
||||
{:validation_failure, :too_many_args}
|
||||
end
|
||||
|
||||
def validate([path], _) do
|
||||
case File.exists?(path, [raw: true]) do
|
||||
true -> :ok
|
||||
case File.exists?(path, raw: true) do
|
||||
true -> :ok
|
||||
false -> {:validation_failure, {:bad_argument, "File #{path} does not exist"}}
|
||||
end
|
||||
end
|
||||
|
||||
def validate(_, _), do: :ok
|
||||
|
||||
use RabbitMQ.CLI.Core.RequiresRabbitAppRunning
|
||||
|
||||
def run([], %{node: node_name, format: format, timeout: timeout}) do
|
||||
case IO.read(:stdio, :all) do
|
||||
:eof -> {:error, :not_enough_args}
|
||||
bin ->
|
||||
:eof ->
|
||||
{:error, :not_enough_args}
|
||||
|
||||
bin ->
|
||||
case deserialise(bin, format) do
|
||||
{:error, error} ->
|
||||
{:error, ExitCodes.exit_dataerr(), "Failed to deserialise input (format: #{human_friendly_format(format)}) (error: #{inspect(error)})"}
|
||||
{:error, ExitCodes.exit_dataerr(),
|
||||
"Failed to deserialise input (format: #{human_friendly_format(format)}) (error: #{inspect(error)})"}
|
||||
|
||||
{:ok, map} ->
|
||||
skip? = Map.get(map, :skip_if_unchanged, false)
|
||||
fun = case skip? do
|
||||
true -> :import_parsed_with_hashing
|
||||
false -> :import_parsed
|
||||
end
|
||||
|
||||
fun =
|
||||
case skip? do
|
||||
true -> :import_parsed_with_hashing
|
||||
false -> :import_parsed
|
||||
end
|
||||
|
||||
:rabbit_misc.rpc_call(node_name, :rabbit_definitions, fun, [map], timeout)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def run([path], %{node: node_name, format: format, timeout: timeout}) do
|
||||
abs_path = Path.absname(path)
|
||||
|
||||
case File.read(abs_path) do
|
||||
{:ok, ""} ->
|
||||
{:error, ExitCodes.exit_dataerr(), "File #{path} is zero-sized"}
|
||||
|
||||
{:ok, bin} ->
|
||||
case deserialise(bin, format) do
|
||||
{:error, error} ->
|
||||
{:error, ExitCodes.exit_dataerr(), "Failed to deserialise input (format: #{human_friendly_format(format)}) (error: #{inspect(error)})"}
|
||||
{:error, ExitCodes.exit_dataerr(),
|
||||
"Failed to deserialise input (format: #{human_friendly_format(format)}) (error: #{inspect(error)})"}
|
||||
|
||||
{:ok, map} ->
|
||||
skip? = Map.get(map, :skip_if_unchanged, false)
|
||||
fun = case skip? do
|
||||
true -> :import_parsed_with_hashing
|
||||
false -> :import_parsed
|
||||
end
|
||||
|
||||
fun =
|
||||
case skip? do
|
||||
true -> :import_parsed_with_hashing
|
||||
false -> :import_parsed
|
||||
end
|
||||
|
||||
:rabbit_misc.rpc_call(node_name, :rabbit_definitions, fun, [map], timeout)
|
||||
end
|
||||
{:error, :enoent} ->
|
||||
|
||||
{:error, :enoent} ->
|
||||
{:error, ExitCodes.exit_dataerr(), "Parent directory or file #{path} does not exist"}
|
||||
|
||||
{:error, :enotdir} ->
|
||||
{:error, ExitCodes.exit_dataerr(), "Parent directory of file #{path} is not a directory"}
|
||||
|
||||
{:error, :eacces} ->
|
||||
{:error, ExitCodes.exit_dataerr(), "No permissions to read from file #{path} or its parent directory"}
|
||||
{:error, ExitCodes.exit_dataerr(),
|
||||
"No permissions to read from file #{path} or its parent directory"}
|
||||
|
||||
{:error, :eisdir} ->
|
||||
{:error, ExitCodes.exit_dataerr(), "Path #{path} is a directory"}
|
||||
{:error, err} ->
|
||||
|
||||
{:error, err} ->
|
||||
{:error, ExitCodes.exit_dataerr(), "Could not read from file #{path}: #{err}"}
|
||||
end
|
||||
end
|
||||
|
|
@ -87,24 +112,34 @@ defmodule RabbitMQ.CLI.Ctl.Commands.ImportDefinitionsCommand do
|
|||
def output(:ok, %{node: node_name, formatter: "json"}) do
|
||||
{:ok, %{"result" => "ok", "node" => node_name}}
|
||||
end
|
||||
|
||||
def output(:ok, opts) do
|
||||
case Config.output_less?(opts) do
|
||||
true -> :ok
|
||||
false -> {:ok, "Successfully started definition import. " <>
|
||||
"This process is asynchronous and can take some time.\n"}
|
||||
true ->
|
||||
:ok
|
||||
|
||||
false ->
|
||||
{:ok,
|
||||
"Successfully started definition import. " <>
|
||||
"This process is asynchronous and can take some time.\n"}
|
||||
end
|
||||
end
|
||||
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
|
||||
def printer(), do: RabbitMQ.CLI.Printers.StdIORaw
|
||||
|
||||
def usage, do: "import_definitions <file_path | \"-\"> [--format <json | erlang>] [--skip-if-unchanged]"
|
||||
def usage,
|
||||
do: "import_definitions <file_path | \"-\"> [--format <json | erlang>] [--skip-if-unchanged]"
|
||||
|
||||
def usage_additional() do
|
||||
[
|
||||
["[file]", "Local file path to import from. If omitted will be read from standard input"],
|
||||
["--format", "input format to use: json or erlang"],
|
||||
["--skip-if-unchanged", "Avoids repetitive definition imports when file contents are unchanged. Target node must be configured accordingly"]
|
||||
[
|
||||
"--skip-if-unchanged",
|
||||
"Avoids repetitive definition imports when file contents are unchanged. Target node must be configured accordingly"
|
||||
]
|
||||
]
|
||||
end
|
||||
|
||||
|
|
@ -121,6 +156,7 @@ defmodule RabbitMQ.CLI.Ctl.Commands.ImportDefinitionsCommand do
|
|||
def banner([], %{format: fmt}) do
|
||||
"Importing definitions in #{human_friendly_format(fmt)} from standard input ..."
|
||||
end
|
||||
|
||||
def banner([path], %{format: fmt}) do
|
||||
"Importing definitions in #{human_friendly_format(fmt)} from a file at \"#{path}\" ..."
|
||||
end
|
||||
|
|
@ -136,8 +172,9 @@ defmodule RabbitMQ.CLI.Ctl.Commands.ImportDefinitionsCommand do
|
|||
defp deserialise(bin, "erlang") do
|
||||
try do
|
||||
{:ok, :erlang.binary_to_term(bin)}
|
||||
rescue e in ArgumentError ->
|
||||
{:error, e.message}
|
||||
rescue
|
||||
e in ArgumentError ->
|
||||
{:error, e.message}
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -77,8 +77,14 @@ defmodule RabbitMQ.CLI.Ctl.Commands.JoinClusterCommand do
|
|||
def usage_additional() do
|
||||
[
|
||||
["<existing_cluster_member>", "Existing cluster member (node) to join"],
|
||||
["--disc", "new node should be a disk one (stores its schema on disk). Highly recommended, used by default."],
|
||||
["--ram", "new node should be a RAM one (stores schema in RAM). Not recommended. Consult clustering doc guides first."]
|
||||
[
|
||||
"--disc",
|
||||
"new node should be a disk one (stores its schema on disk). Highly recommended, used by default."
|
||||
],
|
||||
[
|
||||
"--ram",
|
||||
"new node should be a RAM one (stores schema in RAM). Not recommended. Consult clustering doc guides first."
|
||||
]
|
||||
]
|
||||
end
|
||||
|
||||
|
|
@ -91,5 +97,6 @@ defmodule RabbitMQ.CLI.Ctl.Commands.JoinClusterCommand do
|
|||
|
||||
def help_section(), do: :cluster_management
|
||||
|
||||
def description(), do: "Instructs the node to become a member of the cluster that the specified node is in"
|
||||
def description(),
|
||||
do: "Instructs the node to become a member of the cluster that the specified node is in"
|
||||
end
|
||||
|
|
|
|||
|
|
@ -43,9 +43,7 @@ defmodule RabbitMQ.CLI.Ctl.Commands.ListConsumersCommand do
|
|||
Helpers.with_nodes_in_cluster(node_name, fn nodes ->
|
||||
RpcStream.receive_list_items_with_fun(
|
||||
node_name,
|
||||
[{:rabbit_amqqueue,
|
||||
:emit_consumers_all,
|
||||
[nodes, vhost]}],
|
||||
[{:rabbit_amqqueue, :emit_consumers_all, [nodes, vhost]}],
|
||||
timeout,
|
||||
info_keys,
|
||||
Kernel.length(nodes),
|
||||
|
|
@ -91,15 +89,16 @@ defmodule RabbitMQ.CLI.Ctl.Commands.ListConsumersCommand do
|
|||
|
||||
def fill_consumer_active_fields({items, {chunk, :continue}}) do
|
||||
{Enum.map(items, fn item ->
|
||||
case Keyword.has_key?(item, :active) do
|
||||
true ->
|
||||
item
|
||||
false ->
|
||||
Keyword.drop(item, [:arguments])
|
||||
++ [active: true, activity_status: :up]
|
||||
++ [arguments: Keyword.get(item, :arguments, [])]
|
||||
end
|
||||
end), {chunk, :continue}}
|
||||
case Keyword.has_key?(item, :active) do
|
||||
true ->
|
||||
item
|
||||
|
||||
false ->
|
||||
Keyword.drop(item, [:arguments]) ++
|
||||
[active: true, activity_status: :up] ++
|
||||
[arguments: Keyword.get(item, :arguments, [])]
|
||||
end
|
||||
end), {chunk, :continue}}
|
||||
end
|
||||
|
||||
def fill_consumer_active_fields(v) do
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@
|
|||
##
|
||||
## Copyright (c) 2018-2022 VMware, Inc. or its affiliates. All rights reserved.
|
||||
|
||||
|
||||
defmodule RabbitMQ.CLI.Ctl.Commands.ListFeatureFlagsCommand do
|
||||
alias RabbitMQ.CLI.Core.{DocGuide, Validators}
|
||||
alias RabbitMQ.CLI.Ctl.InfoKeys
|
||||
|
|
@ -41,15 +40,14 @@ defmodule RabbitMQ.CLI.Ctl.Commands.ListFeatureFlagsCommand do
|
|||
)
|
||||
end
|
||||
|
||||
def run([_|_] = args, %{node: node_name, timeout: timeout}) do
|
||||
def run([_ | _] = args, %{node: node_name, timeout: timeout}) do
|
||||
case :rabbit_misc.rpc_call(node_name, :rabbit_ff_extra, :cli_info, [], timeout) do
|
||||
# Server does not support feature flags, consider none are available.
|
||||
# See rabbitmq/rabbitmq-cli#344 for context. MK.
|
||||
{:badrpc, {:EXIT, {:undef, _}}} -> []
|
||||
{:badrpc, _} = err -> err
|
||||
val -> filter_by_arg(val, args)
|
||||
{:badrpc, _} = err -> err
|
||||
val -> filter_by_arg(val, args)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def banner(_, _), do: "Listing feature flags ..."
|
||||
|
|
@ -81,13 +79,15 @@ defmodule RabbitMQ.CLI.Ctl.Commands.ListFeatureFlagsCommand do
|
|||
ff_info
|
||||
end
|
||||
|
||||
defp filter_by_arg(ff_info, [_|_] = args) when is_list(ff_info) do
|
||||
defp filter_by_arg(ff_info, [_ | _] = args) when is_list(ff_info) do
|
||||
symbol_args = InfoKeys.prepare_info_keys(args)
|
||||
Enum.map(ff_info,
|
||||
fn(ff) ->
|
||||
|
||||
Enum.map(
|
||||
ff_info,
|
||||
fn ff ->
|
||||
symbol_args
|
||||
|> Enum.filter(fn(arg) -> ff[arg] != nil end)
|
||||
|> Enum.map(fn(arg) -> {arg, ff[arg]} end)
|
||||
|> Enum.filter(fn arg -> ff[arg] != nil end)
|
||||
|> Enum.map(fn arg -> {arg, ff[arg]} end)
|
||||
end
|
||||
)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -26,12 +26,13 @@ defmodule RabbitMQ.CLI.Ctl.Commands.ListQueuesCommand do
|
|||
leader members online)a
|
||||
|
||||
def description(), do: "Lists queues and their properties"
|
||||
def usage(), do: "list_queues [--vhost <vhost>] [--online] [--offline] [--local] [--no-table-headers] [<column>, ...]"
|
||||
|
||||
def usage(),
|
||||
do:
|
||||
"list_queues [--vhost <vhost>] [--online] [--offline] [--local] [--no-table-headers] [<column>, ...]"
|
||||
|
||||
def scopes(), do: [:ctl, :diagnostics]
|
||||
def switches(), do: [offline: :boolean,
|
||||
online: :boolean,
|
||||
local: :boolean,
|
||||
timeout: :integer]
|
||||
def switches(), do: [offline: :boolean, online: :boolean, local: :boolean, timeout: :integer]
|
||||
def aliases(), do: [t: :timeout]
|
||||
|
||||
def info_keys(), do: @info_keys
|
||||
|
|
|
|||
|
|
@ -85,11 +85,15 @@ defmodule RabbitMQ.CLI.Ctl.Commands.ListUnresponsiveQueuesCommand do
|
|||
[
|
||||
["<column>", "must be one of " <> Enum.join(Enum.sort(@info_keys), ", ")],
|
||||
["--local", "only return queues hosted on the target node"],
|
||||
["--queue-timeout <milliseconds>", "per-queue timeout to use when checking for responsiveness"]
|
||||
[
|
||||
"--queue-timeout <milliseconds>",
|
||||
"per-queue timeout to use when checking for responsiveness"
|
||||
]
|
||||
]
|
||||
end
|
||||
|
||||
def help_section(), do: :observability_and_health_checks
|
||||
|
||||
def description(), do: "Tests queues to respond within timeout. Lists those which did not respond"
|
||||
def description(),
|
||||
do: "Tests queues to respond within timeout. Lists those which did not respond"
|
||||
end
|
||||
|
|
|
|||
|
|
@ -43,7 +43,9 @@ defmodule RabbitMQ.CLI.Ctl.Commands.ListUserLimitsCommand do
|
|||
end
|
||||
|
||||
def run([], %{node: node_name, user: username}) do
|
||||
case :rabbit_misc.rpc_call(node_name, :rabbit_auth_backend_internal, :get_user_limits, [username]) do
|
||||
case :rabbit_misc.rpc_call(node_name, :rabbit_auth_backend_internal, :get_user_limits, [
|
||||
username
|
||||
]) do
|
||||
:undefined ->
|
||||
{:error, {:no_such_user, username}}
|
||||
|
||||
|
|
|
|||
|
|
@ -70,9 +70,10 @@ defmodule RabbitMQ.CLI.Ctl.Commands.NodeHealthCheckCommand do
|
|||
end
|
||||
|
||||
def help_section(), do: :deprecated
|
||||
|
||||
def description() do
|
||||
"DEPRECATED. Performs intrusive, opinionated health checks on a fully booted node. " <>
|
||||
"See https://www.rabbitmq.com/monitoring.html#health-checks instead"
|
||||
"See https://www.rabbitmq.com/monitoring.html#health-checks instead"
|
||||
end
|
||||
|
||||
def banner(_, %{node: node_name, timeout: timeout}) do
|
||||
|
|
|
|||
|
|
@ -62,6 +62,7 @@ defmodule RabbitMQ.CLI.Ctl.Commands.PingCommand do
|
|||
{:error, RabbitMQ.CLI.Core.ExitCodes.exit_software(),
|
||||
"Error: timed out while waiting for a response from #{node_name}."}
|
||||
end
|
||||
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
|
||||
def usage() do
|
||||
|
|
@ -76,12 +77,12 @@ defmodule RabbitMQ.CLI.Ctl.Commands.PingCommand do
|
|||
|
||||
def help_section(), do: :observability_and_health_checks
|
||||
|
||||
def description(), do: "Checks that the node OS process is up, registered with EPMD and CLI tools can authenticate with it"
|
||||
def description(),
|
||||
do:
|
||||
"Checks that the node OS process is up, registered with EPMD and CLI tools can authenticate with it"
|
||||
|
||||
def banner([], %{node: node_name, timeout: timeout}) when is_number(timeout) do
|
||||
"Will ping #{node_name}. This only checks if the OS process is running and registered with epmd. Timeout: #{
|
||||
timeout
|
||||
} ms."
|
||||
"Will ping #{node_name}. This only checks if the OS process is running and registered with epmd. Timeout: #{timeout} ms."
|
||||
end
|
||||
|
||||
def banner([], %{node: node_name, timeout: _timeout}) do
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
defmodule RabbitMQ.CLI.Ctl.Commands.ReportCommand do
|
||||
alias RabbitMQ.CLI.Core.DocGuide
|
||||
|
||||
alias RabbitMQ.CLI.Ctl.Commands.{
|
||||
ClusterStatusCommand,
|
||||
EnvironmentCommand,
|
||||
|
|
@ -20,6 +21,7 @@ defmodule RabbitMQ.CLI.Ctl.Commands.ReportCommand do
|
|||
ListQueuesCommand,
|
||||
StatusCommand
|
||||
}
|
||||
|
||||
alias RabbitMQ.CLI.Diagnostics.Commands.{
|
||||
CommandLineArgumentsCommand,
|
||||
OsEnvCommand
|
||||
|
|
@ -73,8 +75,7 @@ defmodule RabbitMQ.CLI.Ctl.Commands.ReportCommand do
|
|||
run_command(ListPermissionsCommand, [], opts),
|
||||
run_command(ListPoliciesCommand, [], opts),
|
||||
run_command(ListGlobalParametersCommand, [], opts),
|
||||
run_command(ListParametersCommand, [], opts),
|
||||
|
||||
run_command(ListParametersCommand, [], opts)
|
||||
]
|
||||
end)
|
||||
|
||||
|
|
@ -96,7 +97,9 @@ defmodule RabbitMQ.CLI.Ctl.Commands.ReportCommand do
|
|||
|
||||
def help_section(), do: :observability_and_health_checks
|
||||
|
||||
def description(), do: "Generate a server status report containing a concatenation of all server status information for support purposes"
|
||||
def description(),
|
||||
do:
|
||||
"Generate a server status report containing a concatenation of all server status information for support purposes"
|
||||
|
||||
def banner(_, %{node: node_name}), do: "Reporting server status of node #{node_name} ..."
|
||||
|
||||
|
|
|
|||
|
|
@ -34,7 +34,8 @@ defmodule RabbitMQ.CLI.Ctl.Commands.ResetCommand do
|
|||
|
||||
def help_section(), do: :node_management
|
||||
|
||||
def description(), do: "Instructs a RabbitMQ node to leave the cluster and return to its virgin state"
|
||||
def description(),
|
||||
do: "Instructs a RabbitMQ node to leave the cluster and return to its virgin state"
|
||||
|
||||
def banner(_, %{node: node_name}), do: "Resetting node #{node_name} ..."
|
||||
end
|
||||
|
|
|
|||
|
|
@ -21,7 +21,13 @@ defmodule RabbitMQ.CLI.Ctl.Commands.ResumeListenersCommand do
|
|||
use RabbitMQ.CLI.Core.AcceptsNoPositionalArguments
|
||||
|
||||
def run([], %{node: node_name, timeout: timeout}) do
|
||||
:rabbit_misc.rpc_call(node_name, :rabbit_maintenance, :resume_all_client_listeners, [], timeout)
|
||||
:rabbit_misc.rpc_call(
|
||||
node_name,
|
||||
:rabbit_maintenance,
|
||||
:resume_all_client_listeners,
|
||||
[],
|
||||
timeout
|
||||
)
|
||||
end
|
||||
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
|
|
@ -36,10 +42,11 @@ defmodule RabbitMQ.CLI.Ctl.Commands.ResumeListenersCommand do
|
|||
|
||||
def help_section(), do: :operations
|
||||
|
||||
def description(), do: "Resumes client connection listeners making them accept client connections again"
|
||||
def description(),
|
||||
do: "Resumes client connection listeners making them accept client connections again"
|
||||
|
||||
def banner(_, %{node: node_name}) do
|
||||
"Will resume client connection listeners on node #{node_name}. "
|
||||
<> "The node will now accept client connections"
|
||||
"Will resume client connection listeners on node #{node_name}. " <>
|
||||
"The node will now accept client connections"
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ defmodule RabbitMQ.CLI.Ctl.Commands.RotateLogsCommand do
|
|||
"This command does not rotate logs anymore [deprecated]"
|
||||
}
|
||||
end
|
||||
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
|
||||
def usage, do: "rotate_logs"
|
||||
|
|
|
|||
|
|
@ -14,9 +14,11 @@ defmodule RabbitMQ.CLI.Ctl.Commands.SetGlobalParameterCommand do
|
|||
def validate(args, _) when length(args) < 2 do
|
||||
{:validation_failure, :not_enough_args}
|
||||
end
|
||||
|
||||
def validate(args, _) when length(args) > 2 do
|
||||
{:validation_failure, :too_many_args}
|
||||
end
|
||||
|
||||
def validate(_, _), do: :ok
|
||||
|
||||
use RabbitMQ.CLI.Core.RequiresRabbitAppRunning
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ defmodule RabbitMQ.CLI.Ctl.Commands.SetLogLevelCommand do
|
|||
def validate([], _) do
|
||||
{:validation_failure, :not_enough_args}
|
||||
end
|
||||
|
||||
def validate(args, _) when length(args) > 1 do
|
||||
{:validation_failure, :too_many_args}
|
||||
end
|
||||
|
|
@ -36,7 +37,8 @@ defmodule RabbitMQ.CLI.Ctl.Commands.SetLogLevelCommand do
|
|||
:ok
|
||||
|
||||
false ->
|
||||
{:error, "level #{level} is not supported. Try one of debug, info, warning, error, critical, none"}
|
||||
{:error,
|
||||
"level #{level} is not supported. Try one of debug, info, warning, error, critical, none"}
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -54,10 +54,16 @@ defmodule RabbitMQ.CLI.Ctl.Commands.SetOperatorPolicyCommand do
|
|||
def usage_additional() do
|
||||
[
|
||||
["<name>", "policy name (identifier)"],
|
||||
["<pattern>", "a regular expression pattern that will be used to match queue, exchanges, etc"],
|
||||
[
|
||||
"<pattern>",
|
||||
"a regular expression pattern that will be used to match queue, exchanges, etc"
|
||||
],
|
||||
["<definition>", "policy definition (arguments). Must be a valid JSON document"],
|
||||
["--priority <priority>", "policy priority"],
|
||||
["--apply-to <queues | exchanges | all>", "policy should only apply to queues, exchanges, or all entities (both of the above)"]
|
||||
[
|
||||
"--apply-to <queues | exchanges | all>",
|
||||
"policy should only apply to queues, exchanges, or all entities (both of the above)"
|
||||
]
|
||||
]
|
||||
end
|
||||
|
||||
|
|
@ -69,11 +75,10 @@ defmodule RabbitMQ.CLI.Ctl.Commands.SetOperatorPolicyCommand do
|
|||
|
||||
def help_section(), do: :policies
|
||||
|
||||
def description(), do: "Sets an operator policy that overrides a subset of arguments in user policies"
|
||||
def description(),
|
||||
do: "Sets an operator policy that overrides a subset of arguments in user policies"
|
||||
|
||||
def banner([name, pattern, definition], %{vhost: vhost, priority: priority}) do
|
||||
"Setting operator policy override \"#{name}\" for pattern \"#{pattern}\" to \"#{definition}\" with priority \"#{
|
||||
priority
|
||||
}\" for vhost \"#{vhost}\" ..."
|
||||
"Setting operator policy override \"#{name}\" for pattern \"#{pattern}\" to \"#{definition}\" with priority \"#{priority}\" for vhost \"#{vhost}\" ..."
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -61,8 +61,6 @@ defmodule RabbitMQ.CLI.Ctl.Commands.SetParameterCommand do
|
|||
def description(), do: "Sets a runtime parameter."
|
||||
|
||||
def banner([component_name, name, value], %{vhost: vhost}) do
|
||||
"Setting runtime parameter \"#{name}\" for component \"#{component_name}\" to \"#{value}\" in vhost \"#{
|
||||
vhost
|
||||
}\" ..."
|
||||
"Setting runtime parameter \"#{name}\" for component \"#{component_name}\" to \"#{value}\" in vhost \"#{vhost}\" ..."
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -39,17 +39,27 @@ defmodule RabbitMQ.CLI.Ctl.Commands.SetPermissionsCommand do
|
|||
end
|
||||
|
||||
def output({:error, {:no_such_user, username}}, %{node: node_name, formatter: "json"}) do
|
||||
{:error, %{"result" => "error", "node" => node_name, "message" => "User #{username} does not exist"}}
|
||||
{:error,
|
||||
%{"result" => "error", "node" => node_name, "message" => "User #{username} does not exist"}}
|
||||
end
|
||||
|
||||
def output({:error, {:no_such_vhost, vhost}}, %{node: node_name, formatter: "json"}) do
|
||||
{:error, %{"result" => "error", "node" => node_name, "message" => "Virtual host #{vhost} does not exist"}}
|
||||
{:error,
|
||||
%{
|
||||
"result" => "error",
|
||||
"node" => node_name,
|
||||
"message" => "Virtual host #{vhost} does not exist"
|
||||
}}
|
||||
end
|
||||
|
||||
def output({:error, {:no_such_user, username}}, _) do
|
||||
{:error, ExitCodes.exit_nouser(), "User #{username} does not exist"}
|
||||
end
|
||||
|
||||
def output({:error, {:no_such_vhost, vhost}}, _) do
|
||||
{:error, "Virtual host #{vhost} does not exist"}
|
||||
end
|
||||
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
|
||||
def usage, do: "set_permissions [--vhost <vhost>] <username> <conf> <write> <read>"
|
||||
|
|
|
|||
|
|
@ -18,12 +18,15 @@ defmodule RabbitMQ.CLI.Ctl.Commands.SetPolicyCommand do
|
|||
def validate([], _) do
|
||||
{:validation_failure, :not_enough_args}
|
||||
end
|
||||
|
||||
def validate(args, _) when length(args) < 3 do
|
||||
{:validation_failure, :not_enough_args}
|
||||
end
|
||||
|
||||
def validate(args, _) when length(args) > 3 do
|
||||
{:validation_failure, :too_many_args}
|
||||
end
|
||||
|
||||
def validate(_, _), do: :ok
|
||||
|
||||
use RabbitMQ.CLI.Core.RequiresRabbitAppRunning
|
||||
|
|
@ -51,10 +54,16 @@ defmodule RabbitMQ.CLI.Ctl.Commands.SetPolicyCommand do
|
|||
def usage_additional() do
|
||||
[
|
||||
["<name>", "policy name (identifier)"],
|
||||
["<pattern>", "regular expression pattern that will be used to match queues, exchanges, etc"],
|
||||
[
|
||||
"<pattern>",
|
||||
"regular expression pattern that will be used to match queues, exchanges, etc"
|
||||
],
|
||||
["<definition>", "policy definition (arguments). Must be a valid JSON document"],
|
||||
["--priority <priority>", "policy priority"],
|
||||
["--apply-to <queues | exchanges | all>", "policy should only apply to queues, exchanges, or all entities (both of the above)"]
|
||||
[
|
||||
"--apply-to <queues | exchanges | all>",
|
||||
"policy should only apply to queues, exchanges, or all entities (both of the above)"
|
||||
]
|
||||
]
|
||||
end
|
||||
|
||||
|
|
@ -69,8 +78,6 @@ defmodule RabbitMQ.CLI.Ctl.Commands.SetPolicyCommand do
|
|||
def description(), do: "Sets or updates a policy"
|
||||
|
||||
def banner([name, pattern, definition], %{vhost: vhost, priority: priority}) do
|
||||
"Setting policy \"#{name}\" for pattern \"#{pattern}\" to \"#{definition}\" with priority \"#{
|
||||
priority
|
||||
}\" for vhost \"#{vhost}\" ..."
|
||||
"Setting policy \"#{name}\" for pattern \"#{pattern}\" to \"#{definition}\" with priority \"#{priority}\" for vhost \"#{vhost}\" ..."
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -16,9 +16,11 @@ defmodule RabbitMQ.CLI.Ctl.Commands.SetTopicPermissionsCommand do
|
|||
def validate(args, _) when length(args) < 4 do
|
||||
{:validation_failure, :not_enough_args}
|
||||
end
|
||||
|
||||
def validate(args, _) when length(args) > 4 do
|
||||
{:validation_failure, :too_many_args}
|
||||
end
|
||||
|
||||
def validate(_, _), do: :ok
|
||||
|
||||
use RabbitMQ.CLI.Core.RequiresRabbitAppRunning
|
||||
|
|
@ -33,17 +35,27 @@ defmodule RabbitMQ.CLI.Ctl.Commands.SetTopicPermissionsCommand do
|
|||
end
|
||||
|
||||
def output({:error, {:no_such_user, username}}, %{node: node_name, formatter: "json"}) do
|
||||
{:error, %{"result" => "error", "node" => node_name, "message" => "User #{username} does not exist"}}
|
||||
{:error,
|
||||
%{"result" => "error", "node" => node_name, "message" => "User #{username} does not exist"}}
|
||||
end
|
||||
|
||||
def output({:error, {:no_such_vhost, vhost}}, %{node: node_name, formatter: "json"}) do
|
||||
{:error, %{"result" => "error", "node" => node_name, "message" => "Virtual host #{vhost} does not exist"}}
|
||||
{:error,
|
||||
%{
|
||||
"result" => "error",
|
||||
"node" => node_name,
|
||||
"message" => "Virtual host #{vhost} does not exist"
|
||||
}}
|
||||
end
|
||||
|
||||
def output({:error, {:no_such_user, username}}, _) do
|
||||
{:error, ExitCodes.exit_nouser(), "User #{username} does not exist"}
|
||||
end
|
||||
|
||||
def output({:error, {:no_such_vhost, vhost}}, _) do
|
||||
{:error, "Virtual host #{vhost} does not exist"}
|
||||
end
|
||||
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
|
||||
def usage do
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ defmodule RabbitMQ.CLI.Ctl.Commands.SetUserTagsCommand do
|
|||
def validate([], _) do
|
||||
{:validation_failure, :not_enough_args}
|
||||
end
|
||||
|
||||
def validate(_, _), do: :ok
|
||||
|
||||
use RabbitMQ.CLI.Core.RequiresRabbitAppRunning
|
||||
|
|
@ -28,11 +29,14 @@ defmodule RabbitMQ.CLI.Ctl.Commands.SetUserTagsCommand do
|
|||
end
|
||||
|
||||
def output({:error, {:no_such_user, username}}, %{node: node_name, formatter: "json"}) do
|
||||
{:error, %{"result" => "error", "node" => node_name, "message" => "User #{username} does not exists"}}
|
||||
{:error,
|
||||
%{"result" => "error", "node" => node_name, "message" => "User #{username} does not exists"}}
|
||||
end
|
||||
|
||||
def output({:error, {:no_such_user, username}}, _) do
|
||||
{:error, ExitCodes.exit_nouser(), "User \"#{username}\" does not exist"}
|
||||
end
|
||||
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
|
||||
def usage, do: "set_user_tags <username> <tag> [...]"
|
||||
|
|
|
|||
|
|
@ -14,25 +14,37 @@ defmodule RabbitMQ.CLI.Ctl.Commands.SetVhostTagsCommand do
|
|||
def validate([], _) do
|
||||
{:validation_failure, :not_enough_args}
|
||||
end
|
||||
|
||||
def validate(_, _), do: :ok
|
||||
|
||||
use RabbitMQ.CLI.Core.RequiresRabbitAppRunning
|
||||
|
||||
def run([vhost | tags], %{node: node_name}) do
|
||||
case :rabbit_misc.rpc_call(
|
||||
node_name, :rabbit_vhost, :update_tags, [vhost, tags, Helpers.cli_acting_user()]) do
|
||||
{:error, _} = err -> err
|
||||
node_name,
|
||||
:rabbit_vhost,
|
||||
:update_tags,
|
||||
[vhost, tags, Helpers.cli_acting_user()]
|
||||
) do
|
||||
{:error, _} = err -> err
|
||||
{:badrpc, _} = err -> err
|
||||
_ -> :ok
|
||||
_ -> :ok
|
||||
end
|
||||
end
|
||||
|
||||
def output({:error, {:no_such_vhost, vhost}}, %{node: node_name, formatter: "json"}) do
|
||||
{:error, %{"result" => "error", "node" => node_name, "message" => "Virtual host \"#{vhost}\" does not exists"}}
|
||||
{:error,
|
||||
%{
|
||||
"result" => "error",
|
||||
"node" => node_name,
|
||||
"message" => "Virtual host \"#{vhost}\" does not exists"
|
||||
}}
|
||||
end
|
||||
|
||||
def output({:error, {:no_such_vhost, vhost}}, _) do
|
||||
{:error, ExitCodes.exit_dataerr(), "Virtual host \"#{vhost}\" does not exist"}
|
||||
end
|
||||
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
|
||||
def usage, do: "set_vhost_tags <vhost> <tag> [...]"
|
||||
|
|
|
|||
|
|
@ -112,7 +112,7 @@ defmodule RabbitMQ.CLI.Ctl.Commands.SetVmMemoryHighWatermarkCommand do
|
|||
def usage_doc_guides() do
|
||||
[
|
||||
DocGuide.alarms(),
|
||||
DocGuide.memory_use(),
|
||||
DocGuide.memory_use()
|
||||
]
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -9,8 +9,7 @@ defmodule RabbitMQ.CLI.Ctl.Commands.ShutdownCommand do
|
|||
alias RabbitMQ.CLI.Core.{OsPid, NodeName}
|
||||
|
||||
def switches() do
|
||||
[timeout: :integer,
|
||||
wait: :boolean]
|
||||
[timeout: :integer, wait: :boolean]
|
||||
end
|
||||
|
||||
def aliases(), do: [timeout: :t]
|
||||
|
|
@ -26,16 +25,22 @@ defmodule RabbitMQ.CLI.Ctl.Commands.ShutdownCommand do
|
|||
def validate([], %{node: node_name, wait: true}) do
|
||||
local_hostname = NodeName.hostname_from_node(Node.self())
|
||||
remote_hostname = NodeName.hostname_from_node(node_name)
|
||||
|
||||
case addressing_local_node?(local_hostname, remote_hostname) do
|
||||
true -> :ok;
|
||||
true ->
|
||||
:ok
|
||||
|
||||
false ->
|
||||
msg = "\nThis command can only --wait for shutdown of local nodes " <>
|
||||
"but node #{node_name} seems to be remote " <>
|
||||
"(local hostname: #{local_hostname}, remote: #{remote_hostname}).\n" <>
|
||||
"Pass --no-wait to shut node #{node_name} down without waiting.\n"
|
||||
msg =
|
||||
"\nThis command can only --wait for shutdown of local nodes " <>
|
||||
"but node #{node_name} seems to be remote " <>
|
||||
"(local hostname: #{local_hostname}, remote: #{remote_hostname}).\n" <>
|
||||
"Pass --no-wait to shut node #{node_name} down without waiting.\n"
|
||||
|
||||
{:validation_failure, {:unsupported_target, msg}}
|
||||
end
|
||||
end
|
||||
|
||||
use RabbitMQ.CLI.Core.AcceptsNoPositionalArguments
|
||||
|
||||
def run([], %{node: node_name, wait: false, timeout: timeout}) do
|
||||
|
|
@ -46,6 +51,7 @@ defmodule RabbitMQ.CLI.Ctl.Commands.ShutdownCommand do
|
|||
case :rabbit_misc.rpc_call(node_name, :os, :getpid, []) do
|
||||
pid when is_list(pid) ->
|
||||
shut_down_node_and_wait_pid_to_stop(node_name, pid, timeout)
|
||||
|
||||
other ->
|
||||
other
|
||||
end
|
||||
|
|
@ -57,25 +63,30 @@ defmodule RabbitMQ.CLI.Ctl.Commands.ShutdownCommand do
|
|||
|
||||
def usage_additional() do
|
||||
[
|
||||
["--wait", "if set, will wait for target node to terminate (by inferring and monitoring its PID file). Only works for local nodes."],
|
||||
[
|
||||
"--wait",
|
||||
"if set, will wait for target node to terminate (by inferring and monitoring its PID file). Only works for local nodes."
|
||||
],
|
||||
["--no-wait", "if set, will not wait for target node to terminate"]
|
||||
]
|
||||
end
|
||||
|
||||
def help_section(), do: :node_management
|
||||
|
||||
def description(), do: "Stops RabbitMQ and its runtime (Erlang VM). Monitors progress for local nodes. Does not require a PID file path."
|
||||
def description(),
|
||||
do:
|
||||
"Stops RabbitMQ and its runtime (Erlang VM). Monitors progress for local nodes. Does not require a PID file path."
|
||||
|
||||
def banner(_, _), do: nil
|
||||
|
||||
|
||||
#
|
||||
# Implementation
|
||||
#
|
||||
|
||||
def addressing_local_node?(_, remote_hostname) when remote_hostname == :localhost , do: :true
|
||||
def addressing_local_node?(_, remote_hostname) when remote_hostname == 'localhost', do: :true
|
||||
def addressing_local_node?(_, remote_hostname) when remote_hostname == "localhost", do: :true
|
||||
def addressing_local_node?(_, remote_hostname) when remote_hostname == :localhost, do: true
|
||||
def addressing_local_node?(_, remote_hostname) when remote_hostname == 'localhost', do: true
|
||||
def addressing_local_node?(_, remote_hostname) when remote_hostname == "localhost", do: true
|
||||
|
||||
def addressing_local_node?(local_hostname, remote_hostname) do
|
||||
local_hostname == remote_hostname
|
||||
end
|
||||
|
|
|
|||
|
|
@ -19,7 +19,8 @@ defmodule RabbitMQ.CLI.Ctl.Commands.StartAppCommand do
|
|||
|
||||
def help_section(), do: :node_management
|
||||
|
||||
def description(), do: "Starts the RabbitMQ application but leaves the runtime (Erlang VM) running"
|
||||
def description(),
|
||||
do: "Starts the RabbitMQ application but leaves the runtime (Erlang VM) running"
|
||||
|
||||
def banner(_, %{node: node_name}), do: "Starting node #{node_name} ..."
|
||||
end
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ defmodule RabbitMQ.CLI.Ctl.Commands.StatusCommand do
|
|||
def validate(args, _) when length(args) > 0 do
|
||||
{:validation_failure, :too_many_args}
|
||||
end
|
||||
|
||||
def validate(_, %{unit: unit}) do
|
||||
case IU.known_unit?(unit) do
|
||||
true ->
|
||||
|
|
@ -41,6 +42,7 @@ defmodule RabbitMQ.CLI.Ctl.Commands.StatusCommand do
|
|||
{:validation_failure, "unit '#{unit}' is not supported. Please use one of: bytes, mb, gb"}
|
||||
end
|
||||
end
|
||||
|
||||
def validate(_, _), do: :ok
|
||||
|
||||
use RabbitMQ.CLI.Core.RequiresRabbitAppRunning
|
||||
|
|
@ -67,45 +69,52 @@ defmodule RabbitMQ.CLI.Ctl.Commands.StatusCommand do
|
|||
def output(result, %{node: node_name, unit: unit}) when is_list(result) do
|
||||
m = result_map(result)
|
||||
|
||||
product_name_section = case m do
|
||||
%{:product_name => product_name} when product_name != "" ->
|
||||
["Product name: #{product_name}"]
|
||||
_ ->
|
||||
[]
|
||||
end
|
||||
product_version_section = case m do
|
||||
%{:product_version => product_version} when product_version != "" ->
|
||||
["Product version: #{product_version}"]
|
||||
_ ->
|
||||
[]
|
||||
end
|
||||
product_name_section =
|
||||
case m do
|
||||
%{:product_name => product_name} when product_name != "" ->
|
||||
["Product name: #{product_name}"]
|
||||
|
||||
runtime_section = [
|
||||
"#{bright("Runtime")}\n",
|
||||
"OS PID: #{m[:pid]}",
|
||||
"OS: #{m[:os]}",
|
||||
# TODO: format
|
||||
"Uptime (seconds): #{m[:uptime]}",
|
||||
"Is under maintenance?: #{m[:is_under_maintenance]}"
|
||||
] ++
|
||||
product_name_section ++
|
||||
product_version_section ++
|
||||
[
|
||||
"RabbitMQ version: #{m[:rabbitmq_version]}",
|
||||
"RabbitMQ release series support status: #{m[:release_series_support_status]}",
|
||||
"Node name: #{node_name}",
|
||||
"Erlang configuration: #{m[:erlang_version]}",
|
||||
"Crypto library: #{m[:crypto_lib_version]}",
|
||||
"Erlang processes: #{m[:processes][:used]} used, #{m[:processes][:limit]} limit",
|
||||
"Scheduler run queue: #{m[:run_queue]}",
|
||||
"Cluster heartbeat timeout (net_ticktime): #{m[:net_ticktime]}"
|
||||
]
|
||||
_ ->
|
||||
[]
|
||||
end
|
||||
|
||||
plugin_section = [
|
||||
"\n#{bright("Plugins")}\n",
|
||||
"Enabled plugin file: #{m[:enabled_plugin_file]}",
|
||||
"Enabled plugins:\n"
|
||||
] ++ Enum.map(m[:active_plugins], fn pl -> " * #{pl}" end)
|
||||
product_version_section =
|
||||
case m do
|
||||
%{:product_version => product_version} when product_version != "" ->
|
||||
["Product version: #{product_version}"]
|
||||
|
||||
_ ->
|
||||
[]
|
||||
end
|
||||
|
||||
runtime_section =
|
||||
[
|
||||
"#{bright("Runtime")}\n",
|
||||
"OS PID: #{m[:pid]}",
|
||||
"OS: #{m[:os]}",
|
||||
# TODO: format
|
||||
"Uptime (seconds): #{m[:uptime]}",
|
||||
"Is under maintenance?: #{m[:is_under_maintenance]}"
|
||||
] ++
|
||||
product_name_section ++
|
||||
product_version_section ++
|
||||
[
|
||||
"RabbitMQ version: #{m[:rabbitmq_version]}",
|
||||
"RabbitMQ release series support status: #{m[:release_series_support_status]}",
|
||||
"Node name: #{node_name}",
|
||||
"Erlang configuration: #{m[:erlang_version]}",
|
||||
"Crypto library: #{m[:crypto_lib_version]}",
|
||||
"Erlang processes: #{m[:processes][:used]} used, #{m[:processes][:limit]} limit",
|
||||
"Scheduler run queue: #{m[:run_queue]}",
|
||||
"Cluster heartbeat timeout (net_ticktime): #{m[:net_ticktime]}"
|
||||
]
|
||||
|
||||
plugin_section =
|
||||
[
|
||||
"\n#{bright("Plugins")}\n",
|
||||
"Enabled plugin file: #{m[:enabled_plugin_file]}",
|
||||
"Enabled plugins:\n"
|
||||
] ++ Enum.map(m[:active_plugins], fn pl -> " * #{pl}" end)
|
||||
|
||||
data_directory_section = [
|
||||
"\n#{bright("Data directory")}\n",
|
||||
|
|
@ -113,36 +122,46 @@ defmodule RabbitMQ.CLI.Ctl.Commands.StatusCommand do
|
|||
"Raft data directory: #{m[:raft_data_directory]}"
|
||||
]
|
||||
|
||||
config_section = [
|
||||
"\n#{bright("Config files")}\n"
|
||||
] ++ Enum.map(m[:config_files], fn path -> " * #{path}" end)
|
||||
config_section =
|
||||
[
|
||||
"\n#{bright("Config files")}\n"
|
||||
] ++ Enum.map(m[:config_files], fn path -> " * #{path}" end)
|
||||
|
||||
log_section = [
|
||||
"\n#{bright("Log file(s)")}\n"
|
||||
] ++ Enum.map(m[:log_files], fn path -> " * #{path}" end)
|
||||
log_section =
|
||||
[
|
||||
"\n#{bright("Log file(s)")}\n"
|
||||
] ++ Enum.map(m[:log_files], fn path -> " * #{path}" end)
|
||||
|
||||
alarms_section = [
|
||||
"\n#{bright("Alarms")}\n",
|
||||
] ++ case m[:alarms] do
|
||||
[] -> ["(none)"]
|
||||
xs -> alarm_lines(xs, node_name)
|
||||
end
|
||||
alarms_section =
|
||||
[
|
||||
"\n#{bright("Alarms")}\n"
|
||||
] ++
|
||||
case m[:alarms] do
|
||||
[] -> ["(none)"]
|
||||
xs -> alarm_lines(xs, node_name)
|
||||
end
|
||||
|
||||
breakdown = compute_relative_values(m[:memory])
|
||||
memory_calculation_strategy = to_atom(m[:vm_memory_calculation_strategy])
|
||||
total_memory = get_in(m[:memory], [:total, memory_calculation_strategy])
|
||||
|
||||
readable_watermark_setting = case m[:vm_memory_high_watermark_setting] do
|
||||
%{:relative => val} -> "#{val} of available memory"
|
||||
# absolute value
|
||||
%{:absolute => val} -> "#{IU.convert(val, unit)} #{unit}"
|
||||
end
|
||||
memory_section = [
|
||||
"\n#{bright("Memory")}\n",
|
||||
"Total memory used: #{IU.convert(total_memory, unit)} #{unit}",
|
||||
"Calculation strategy: #{memory_calculation_strategy}",
|
||||
"Memory high watermark setting: #{readable_watermark_setting}, computed to: #{IU.convert(m[:vm_memory_high_watermark_limit], unit)} #{unit}\n"
|
||||
] ++ Enum.map(breakdown, fn({category, val}) -> "#{category}: #{IU.convert(val[:bytes], unit)} #{unit} (#{val[:percentage]} %)" end)
|
||||
readable_watermark_setting =
|
||||
case m[:vm_memory_high_watermark_setting] do
|
||||
%{:relative => val} -> "#{val} of available memory"
|
||||
# absolute value
|
||||
%{:absolute => val} -> "#{IU.convert(val, unit)} #{unit}"
|
||||
end
|
||||
|
||||
memory_section =
|
||||
[
|
||||
"\n#{bright("Memory")}\n",
|
||||
"Total memory used: #{IU.convert(total_memory, unit)} #{unit}",
|
||||
"Calculation strategy: #{memory_calculation_strategy}",
|
||||
"Memory high watermark setting: #{readable_watermark_setting}, computed to: #{IU.convert(m[:vm_memory_high_watermark_limit], unit)} #{unit}\n"
|
||||
] ++
|
||||
Enum.map(breakdown, fn {category, val} ->
|
||||
"#{category}: #{IU.convert(val[:bytes], unit)} #{unit} (#{val[:percentage]} %)"
|
||||
end)
|
||||
|
||||
file_descriptors = [
|
||||
"\n#{bright("File Descriptors")}\n",
|
||||
|
|
@ -163,15 +182,24 @@ defmodule RabbitMQ.CLI.Ctl.Commands.StatusCommand do
|
|||
"Virtual host count: #{m[:totals][:virtual_host_count]}"
|
||||
]
|
||||
|
||||
listeners_section = [
|
||||
"\n#{bright("Listeners")}\n",
|
||||
] ++ case m[:listeners] do
|
||||
[] -> ["(none)"]
|
||||
xs -> listener_lines(xs)
|
||||
end
|
||||
lines = runtime_section ++ plugin_section ++ data_directory_section ++
|
||||
config_section ++ log_section ++ alarms_section ++ memory_section ++
|
||||
file_descriptors ++ disk_space_section ++ totals_section ++ listeners_section
|
||||
listeners_section =
|
||||
[
|
||||
"\n#{bright("Listeners")}\n"
|
||||
] ++
|
||||
case m[:listeners] do
|
||||
[] -> ["(none)"]
|
||||
xs -> listener_lines(xs)
|
||||
end
|
||||
|
||||
lines =
|
||||
runtime_section ++
|
||||
plugin_section ++
|
||||
data_directory_section ++
|
||||
config_section ++
|
||||
log_section ++
|
||||
alarms_section ++
|
||||
memory_section ++
|
||||
file_descriptors ++ disk_space_section ++ totals_section ++ listeners_section
|
||||
|
||||
{:ok, Enum.join(lines, line_separator())}
|
||||
end
|
||||
|
|
@ -206,10 +234,11 @@ defmodule RabbitMQ.CLI.Ctl.Commands.StatusCommand do
|
|||
#
|
||||
|
||||
defp result_map(result) do
|
||||
crypto_lib_version = case Keyword.get(result, :crypto_lib_info) do
|
||||
{_, _, version} -> version
|
||||
other -> other
|
||||
end
|
||||
crypto_lib_version =
|
||||
case Keyword.get(result, :crypto_lib_info) do
|
||||
{_, _, version} -> version
|
||||
other -> other
|
||||
end
|
||||
|
||||
%{
|
||||
os: os_name(Keyword.get(result, :os)),
|
||||
|
|
@ -217,7 +246,7 @@ defmodule RabbitMQ.CLI.Ctl.Commands.StatusCommand do
|
|||
product_name: Keyword.get(result, :product_name) |> to_string,
|
||||
product_version: Keyword.get(result, :product_version) |> to_string,
|
||||
rabbitmq_version: Keyword.get(result, :rabbitmq_version) |> to_string,
|
||||
erlang_version: Keyword.get(result, :erlang_version) |> to_string |> String.trim_trailing,
|
||||
erlang_version: Keyword.get(result, :erlang_version) |> to_string |> String.trim_trailing(),
|
||||
crypto_lib_version: crypto_lib_version,
|
||||
release_series_support_status: Keyword.get(result, :release_series_support_status, true),
|
||||
uptime: Keyword.get(result, :uptime),
|
||||
|
|
@ -225,38 +254,31 @@ defmodule RabbitMQ.CLI.Ctl.Commands.StatusCommand do
|
|||
processes: Enum.into(Keyword.get(result, :processes), %{}),
|
||||
run_queue: Keyword.get(result, :run_queue),
|
||||
net_ticktime: net_ticktime(result),
|
||||
|
||||
vm_memory_calculation_strategy: Keyword.get(result, :vm_memory_calculation_strategy),
|
||||
vm_memory_high_watermark_setting: Keyword.get(result, :vm_memory_high_watermark) |> formatted_watermark,
|
||||
vm_memory_high_watermark_setting:
|
||||
Keyword.get(result, :vm_memory_high_watermark) |> formatted_watermark,
|
||||
vm_memory_high_watermark_limit: Keyword.get(result, :vm_memory_limit),
|
||||
|
||||
disk_free_limit: Keyword.get(result, :disk_free_limit),
|
||||
disk_free: Keyword.get(result, :disk_free),
|
||||
|
||||
file_descriptors: Enum.into(Keyword.get(result, :file_descriptors), %{}),
|
||||
|
||||
alarms: Keyword.get(result, :alarms),
|
||||
listeners: listener_maps(Keyword.get(result, :listeners, [])),
|
||||
memory: Keyword.get(result, :memory) |> Enum.into(%{}),
|
||||
|
||||
data_directory: Keyword.get(result, :data_directory) |> to_string,
|
||||
raft_data_directory: Keyword.get(result, :raft_data_directory) |> to_string,
|
||||
|
||||
config_files: Keyword.get(result, :config_files) |> Enum.map(&to_string/1),
|
||||
log_files: Keyword.get(result, :log_files) |> Enum.map(&to_string/1),
|
||||
|
||||
active_plugins: Keyword.get(result, :active_plugins) |> Enum.map(&to_string/1),
|
||||
enabled_plugin_file: Keyword.get(result, :enabled_plugin_file) |> to_string,
|
||||
|
||||
totals: Keyword.get(result, :totals)
|
||||
}
|
||||
end
|
||||
|
||||
defp net_ticktime(result) do
|
||||
case Keyword.get(result, :kernel) do
|
||||
{:net_ticktime, n} -> n
|
||||
{:net_ticktime, n} -> n
|
||||
n when is_integer(n) -> n
|
||||
_ -> :undefined
|
||||
_ -> :undefined
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -55,12 +55,17 @@ defmodule RabbitMQ.CLI.Ctl.Commands.StopCommand do
|
|||
|
||||
def usage_additional() do
|
||||
[
|
||||
["<pidfile>", "node PID file path to monitor. To avoid using a PID file, use 'rabbitmqctl shutdown'"],
|
||||
[
|
||||
"<pidfile>",
|
||||
"node PID file path to monitor. To avoid using a PID file, use 'rabbitmqctl shutdown'"
|
||||
],
|
||||
["--idempotent", "return success if target node is not running (cannot be contacted)"]
|
||||
]
|
||||
end
|
||||
|
||||
def description(), do: "Stops RabbitMQ and its runtime (Erlang VM). Requires a local node pid file path to monitor progress."
|
||||
def description(),
|
||||
do:
|
||||
"Stops RabbitMQ and its runtime (Erlang VM). Requires a local node pid file path to monitor progress."
|
||||
|
||||
def help_section(), do: :node_management
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,13 @@ defmodule RabbitMQ.CLI.Ctl.Commands.SuspendListenersCommand do
|
|||
use RabbitMQ.CLI.Core.AcceptsNoPositionalArguments
|
||||
|
||||
def run([], %{node: node_name, timeout: timeout}) do
|
||||
:rabbit_misc.rpc_call(node_name, :rabbit_maintenance, :suspend_all_client_listeners, [], timeout)
|
||||
:rabbit_misc.rpc_call(
|
||||
node_name,
|
||||
:rabbit_maintenance,
|
||||
:suspend_all_client_listeners,
|
||||
[],
|
||||
timeout
|
||||
)
|
||||
end
|
||||
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
|
|
@ -37,10 +43,11 @@ defmodule RabbitMQ.CLI.Ctl.Commands.SuspendListenersCommand do
|
|||
|
||||
def help_section(), do: :operations
|
||||
|
||||
def description(), do: "Suspends client connection listeners so that no new client connections are accepted"
|
||||
def description(),
|
||||
do: "Suspends client connection listeners so that no new client connections are accepted"
|
||||
|
||||
def banner(_, %{node: node_name}) do
|
||||
"Will suspend client connection listeners on node #{node_name}. "
|
||||
<> "The node will no longer accept client connections!"
|
||||
"Will suspend client connection listeners on node #{node_name}. " <>
|
||||
"The node will no longer accept client connections!"
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -46,7 +46,9 @@ defmodule RabbitMQ.CLI.Ctl.Commands.SyncQueueCommand do
|
|||
|
||||
def help_section(), do: :replication
|
||||
|
||||
def description(), do: "Instructs a mirrored queue with unsynchronised mirrors (follower replicas) to synchronise them"
|
||||
def description(),
|
||||
do:
|
||||
"Instructs a mirrored queue with unsynchronised mirrors (follower replicas) to synchronise them"
|
||||
|
||||
def banner([queue], %{vhost: vhost, node: _node}) do
|
||||
"Synchronising queue '#{queue}' in vhost '#{vhost}' ..."
|
||||
|
|
|
|||
|
|
@ -13,9 +13,10 @@ defmodule RabbitMQ.CLI.Ctl.Commands.UpdateClusterNodesCommand do
|
|||
use RabbitMQ.CLI.Core.AcceptsOnePositionalArgument
|
||||
use RabbitMQ.CLI.Core.RequiresRabbitAppStopped
|
||||
|
||||
def run([seed_node], options=%{node: node_name}) do
|
||||
def run([seed_node], options = %{node: node_name}) do
|
||||
long_or_short_names = Config.get_option(:longnames, options)
|
||||
seed_node_normalised = Helpers.normalise_node(seed_node, long_or_short_names)
|
||||
|
||||
:rabbit_misc.rpc_call(
|
||||
node_name,
|
||||
:rabbit_mnesia,
|
||||
|
|
@ -42,7 +43,9 @@ defmodule RabbitMQ.CLI.Ctl.Commands.UpdateClusterNodesCommand do
|
|||
|
||||
def help_section(), do: :cluster_management
|
||||
|
||||
def description(), do: "Instructs a cluster member node to sync the list of known cluster members from <seed_node>"
|
||||
def description(),
|
||||
do:
|
||||
"Instructs a cluster member node to sync the list of known cluster members from <seed_node>"
|
||||
|
||||
def banner([seed_node], %{node: node_name}) do
|
||||
"Will seed #{node_name} from #{seed_node} on next start"
|
||||
|
|
|
|||
|
|
@ -21,13 +21,16 @@ defmodule RabbitMQ.CLI.Ctl.Commands.VersionCommand do
|
|||
def run([], %{formatter: "json"}) do
|
||||
{:ok, %{version: Version.local_version()}}
|
||||
end
|
||||
|
||||
def run([], %{formatter: "csv"}) do
|
||||
row = [version: Version.local_version()]
|
||||
{:ok, [row]}
|
||||
end
|
||||
|
||||
def run([], _opts) do
|
||||
{:ok, Version.local_version()}
|
||||
end
|
||||
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
|
||||
def help_section, do: :help
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ defmodule RabbitMQ.CLI.Ctl.Commands.WaitCommand do
|
|||
def validate_execution_environment([], %{pid: _} = opts) do
|
||||
Validators.rabbit_is_loaded([], opts)
|
||||
end
|
||||
|
||||
def validate_execution_environment([_pid_file], opts) do
|
||||
Validators.rabbit_is_loaded([], opts)
|
||||
end
|
||||
|
|
@ -102,7 +103,9 @@ defmodule RabbitMQ.CLI.Ctl.Commands.WaitCommand do
|
|||
|
||||
def help_section(), do: :node_management
|
||||
|
||||
def description(), do: "Waits for RabbitMQ node startup by monitoring a local PID file. See also 'rabbitmqctl await_online_nodes'"
|
||||
def description(),
|
||||
do:
|
||||
"Waits for RabbitMQ node startup by monitoring a local PID file. See also 'rabbitmqctl await_online_nodes'"
|
||||
|
||||
#
|
||||
# Implementation
|
||||
|
|
@ -253,7 +256,7 @@ defmodule RabbitMQ.CLI.Ctl.Commands.WaitCommand do
|
|||
{pid, _} ->
|
||||
case check_distribution(pid, node_name) do
|
||||
:ok -> {:ok, pid}
|
||||
_ -> {:error, :loop}
|
||||
_ -> {:error, :loop}
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -28,10 +28,13 @@ defmodule RabbitMQ.CLI.DefaultOutput do
|
|||
defp normalize_output(:ok, %{node: node_name, formatter: "json"}) do
|
||||
{:ok, %{"result" => "ok", "node" => node_name}}
|
||||
end
|
||||
|
||||
defp normalize_output(:ok, _opts), do: :ok
|
||||
|
||||
defp normalize_output({:ok, value}, %{node: node_name, formatter: "json"}) do
|
||||
{:ok, %{"result" => "ok", "node" => node_name, "value" => value}}
|
||||
end
|
||||
|
||||
defp normalize_output({:ok, _} = input, _opts), do: input
|
||||
defp normalize_output({:stream, _} = input, _opts), do: input
|
||||
defp normalize_output({:badrpc_multi, _, _} = input, _opts), do: {:error, input}
|
||||
|
|
@ -40,25 +43,30 @@ defmodule RabbitMQ.CLI.DefaultOutput do
|
|||
defp normalize_output({:badrpc, {:timeout, _n}} = input, _opts), do: {:error, input}
|
||||
defp normalize_output({:badrpc, {:timeout, _n, _msg}} = input, _opts), do: {:error, input}
|
||||
defp normalize_output({:badrpc, {:EXIT, reason}}, _opts), do: {:error, reason}
|
||||
|
||||
defp normalize_output({:error, exit_code, string}, _opts) when is_integer(exit_code) do
|
||||
{:error, exit_code, to_string(string)}
|
||||
end
|
||||
|
||||
defp normalize_output({:error, format, args}, _opts)
|
||||
when (is_list(format) or is_binary(format)) and is_list(args) do
|
||||
{:error, to_string(:rabbit_misc.format(format, args))}
|
||||
end
|
||||
|
||||
defp normalize_output({:error, _} = input, _opts), do: input
|
||||
|
||||
defp normalize_output({:error_string, string}, _opts) do
|
||||
{:error, to_string(string)}
|
||||
end
|
||||
|
||||
defp normalize_output(unknown, _opts) when is_atom(unknown), do: {:error, unknown}
|
||||
defp normalize_output({unknown, _} = input, _opts) when is_atom(unknown), do: {:error, input}
|
||||
defp normalize_output(result, _opts) when not is_atom(result), do: {:ok, result}
|
||||
|
||||
|
||||
defp format_output({:error, _} = result) do
|
||||
result
|
||||
end
|
||||
|
||||
defp format_output({:error, _, _} = result) do
|
||||
result
|
||||
end
|
||||
|
|
|
|||
|
|
@ -67,7 +67,8 @@ defmodule RabbitMQ.CLI.Diagnostics.Commands.AlarmsCommand do
|
|||
|
||||
def help_section(), do: :observability_and_health_checks
|
||||
|
||||
def description(), do: "Lists resource alarms (local or cluster-wide) in effect on the target node"
|
||||
def description(),
|
||||
do: "Lists resource alarms (local or cluster-wide) in effect on the target node"
|
||||
|
||||
def usage, do: "alarms"
|
||||
|
||||
|
|
|
|||
|
|
@ -49,7 +49,9 @@ defmodule RabbitMQ.CLI.Diagnostics.Commands.CertificatesCommand do
|
|||
|
||||
def help_section(), do: :configuration
|
||||
|
||||
def description(), do: "Displays certificates (public keys) for every listener on target node that is configured to use TLS"
|
||||
def description(),
|
||||
do:
|
||||
"Displays certificates (public keys) for every listener on target node that is configured to use TLS"
|
||||
|
||||
def banner(_, %{node: node_name}), do: "Certificates of node #{node_name} ..."
|
||||
end
|
||||
|
|
|
|||
|
|
@ -76,7 +76,9 @@ defmodule RabbitMQ.CLI.Diagnostics.Commands.CheckAlarmsCommand do
|
|||
|
||||
def help_section(), do: :observability_and_health_checks
|
||||
|
||||
def description(), do: "Health check that exits with a non-zero code if the target node reports any alarms, local or cluster-wide."
|
||||
def description(),
|
||||
do:
|
||||
"Health check that exits with a non-zero code if the target node reports any alarms, local or cluster-wide."
|
||||
|
||||
def usage, do: "check_alarms"
|
||||
|
||||
|
|
|
|||
|
|
@ -20,15 +20,18 @@ defmodule RabbitMQ.CLI.Diagnostics.Commands.CheckCertificateExpirationCommand do
|
|||
def validate(args, _) when length(args) > 0 do
|
||||
{:validation_failure, :too_many_args}
|
||||
end
|
||||
|
||||
def validate(_, %{unit: unit}) do
|
||||
case TU.known_unit?(unit) do
|
||||
true ->
|
||||
:ok
|
||||
|
||||
false ->
|
||||
{:validation_failure, "unit '#{unit}' is not supported. Please use one of: days, weeks, months, years"}
|
||||
{:validation_failure,
|
||||
"unit '#{unit}' is not supported. Please use one of: days, weeks, months, years"}
|
||||
end
|
||||
end
|
||||
|
||||
def validate(_, _), do: :ok
|
||||
|
||||
def run([], %{node: node_name, unit: unit, within: within, timeout: timeout}) do
|
||||
|
|
@ -45,10 +48,12 @@ defmodule RabbitMQ.CLI.Diagnostics.Commands.CheckCertificateExpirationCommand do
|
|||
xs when is_list(xs) ->
|
||||
listeners = listeners_on(xs, node_name)
|
||||
seconds = TU.convert(within, unit)
|
||||
Enum.reduce(listeners, [], fn (listener, acc) -> case listener_expiring_within(listener, seconds) do
|
||||
false -> acc
|
||||
expiring -> [expiring | acc]
|
||||
end
|
||||
|
||||
Enum.reduce(listeners, [], fn listener, acc ->
|
||||
case listener_expiring_within(listener, seconds) do
|
||||
false -> acc
|
||||
expiring -> [expiring | acc]
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
|
@ -63,7 +68,8 @@ defmodule RabbitMQ.CLI.Diagnostics.Commands.CheckCertificateExpirationCommand do
|
|||
end
|
||||
|
||||
def output(listeners, %{formatter: "json"}) do
|
||||
{:error, :check_failed, %{"result" => "error", "expired" => Enum.map(listeners, &expired_listener_map/1)}}
|
||||
{:error, :check_failed,
|
||||
%{"result" => "error", "expired" => Enum.map(listeners, &expired_listener_map/1)}}
|
||||
end
|
||||
|
||||
def output(listeners, %{}) do
|
||||
|
|
@ -73,6 +79,7 @@ defmodule RabbitMQ.CLI.Diagnostics.Commands.CheckCertificateExpirationCommand do
|
|||
def unit_label(1, unit) do
|
||||
unit |> String.slice(0..-2)
|
||||
end
|
||||
|
||||
def unit_label(_within, unit) do
|
||||
unit
|
||||
end
|
||||
|
|
@ -82,7 +89,7 @@ defmodule RabbitMQ.CLI.Diagnostics.Commands.CheckCertificateExpirationCommand do
|
|||
def usage_additional() do
|
||||
[
|
||||
["<period>", "period of time to check. Default is four (weeks)."],
|
||||
["<unit>", "time unit for the period, can be days, weeks, months, years. Default is weeks."],
|
||||
["<unit>", "time unit for the period, can be days, weeks, months, years. Default is weeks."]
|
||||
]
|
||||
end
|
||||
|
||||
|
|
@ -95,7 +102,9 @@ defmodule RabbitMQ.CLI.Diagnostics.Commands.CheckCertificateExpirationCommand do
|
|||
|
||||
def help_section(), do: :observability_and_health_checks
|
||||
|
||||
def description(), do: "Checks the expiration date on the certificates for every listener configured to use TLS"
|
||||
def description(),
|
||||
do: "Checks the expiration date on the certificates for every listener configured to use TLS"
|
||||
|
||||
def banner(_, %{node: node_name}), do: "Checking certificate expiration on node #{node_name} ..."
|
||||
def banner(_, %{node: node_name}),
|
||||
do: "Checking certificate expiration on node #{node_name} ..."
|
||||
end
|
||||
|
|
|
|||
|
|
@ -75,7 +75,8 @@ defmodule RabbitMQ.CLI.Diagnostics.Commands.CheckLocalAlarmsCommand do
|
|||
|
||||
def help_section(), do: :observability_and_health_checks
|
||||
|
||||
def description(), do: "Health check that exits with a non-zero code if the target node reports any local alarms"
|
||||
def description(),
|
||||
do: "Health check that exits with a non-zero code if the target node reports any local alarms"
|
||||
|
||||
def usage, do: "check_local_alarms"
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ defmodule RabbitMQ.CLI.Diagnostics.Commands.CheckPortConnectivityCommand do
|
|||
|
||||
import RabbitMQ.CLI.Diagnostics.Helpers,
|
||||
only: [check_listener_connectivity: 3]
|
||||
|
||||
import RabbitMQ.CLI.Core.Platform, only: [line_separator: 0]
|
||||
import RabbitMQ.CLI.Core.Listeners
|
||||
|
||||
|
|
@ -87,7 +88,8 @@ defmodule RabbitMQ.CLI.Diagnostics.Commands.CheckPortConnectivityCommand do
|
|||
{:error, Enum.join(lines, line_separator())}
|
||||
end
|
||||
|
||||
def description(), do: "Basic TCP connectivity health check for each listener's port on the target node"
|
||||
def description(),
|
||||
do: "Basic TCP connectivity health check for each listener's port on the target node"
|
||||
|
||||
def help_section(), do: :observability_and_health_checks
|
||||
|
||||
|
|
|
|||
|
|
@ -65,14 +65,16 @@ defmodule RabbitMQ.CLI.Diagnostics.Commands.CheckPortListenerCommand do
|
|||
def output({false, port, listeners}, %{node: node_name}) do
|
||||
ports = Enum.map(listeners, fn %{port: p} -> p end) |> Enum.sort() |> Enum.join(", ")
|
||||
|
||||
{:error, :check_failed,
|
||||
{:error, :check_failed,
|
||||
"No listener for port #{port} is active on node #{node_name}. " <>
|
||||
"Found listeners that use the following ports: #{ports}"}
|
||||
end
|
||||
|
||||
def help_section(), do: :observability_and_health_checks
|
||||
|
||||
def description(), do: "Health check that exits with a non-zero code if target node does not have an active listener for given port"
|
||||
def description(),
|
||||
do:
|
||||
"Health check that exits with a non-zero code if target node does not have an active listener for given port"
|
||||
|
||||
def usage, do: "check_port_listener <port>"
|
||||
|
||||
|
|
|
|||
|
|
@ -80,7 +80,9 @@ defmodule RabbitMQ.CLI.Diagnostics.Commands.CheckProtocolListenerCommand do
|
|||
|
||||
def help_section(), do: :observability_and_health_checks
|
||||
|
||||
def description(), do: "Health check that exits with a non-zero code if target node does not have an active listener for given protocol"
|
||||
def description(),
|
||||
do:
|
||||
"Health check that exits with a non-zero code if target node does not have an active listener for given protocol"
|
||||
|
||||
def usage, do: "check_protocol_listener <protocol>"
|
||||
|
||||
|
|
|
|||
|
|
@ -36,7 +36,9 @@ defmodule RabbitMQ.CLI.Diagnostics.Commands.CheckRunningCommand do
|
|||
|
||||
def help_section(), do: :observability_and_health_checks
|
||||
|
||||
def description(), do: "Health check that exits with a non-zero code if the RabbitMQ app on the target node is not running"
|
||||
def description(),
|
||||
do:
|
||||
"Health check that exits with a non-zero code if the RabbitMQ app on the target node is not running"
|
||||
|
||||
def usage, do: "check_running"
|
||||
|
||||
|
|
|
|||
|
|
@ -13,15 +13,14 @@ defmodule RabbitMQ.CLI.Diagnostics.Commands.CipherSuitesCommand do
|
|||
{args, Map.merge(%{all: false, format: "openssl"}, Helpers.case_insensitive_format(opts))}
|
||||
end
|
||||
|
||||
def switches(), do: [timeout: :integer,
|
||||
format: :string,
|
||||
all: :boolean]
|
||||
def switches(), do: [timeout: :integer, format: :string, all: :boolean]
|
||||
def aliases(), do: [t: :timeout]
|
||||
|
||||
def validate(_, %{format: format})
|
||||
when format != "openssl" and format != "erlang" and format != "map" do
|
||||
{:validation_failure, {:bad_argument, "Format should be either openssl, erlang or map"}}
|
||||
end
|
||||
|
||||
def validate(args, _) when length(args) > 0 do
|
||||
{:validation_failure, :too_many_args}
|
||||
end
|
||||
|
|
@ -29,27 +28,33 @@ defmodule RabbitMQ.CLI.Diagnostics.Commands.CipherSuitesCommand do
|
|||
def validate(_, _), do: :ok
|
||||
|
||||
def run([], %{node: node_name, timeout: timeout, format: format} = opts) do
|
||||
{mod, function} = case format do
|
||||
"openssl" -> {:rabbit_ssl, :cipher_suites_openssl};
|
||||
"erlang" -> {:rabbit_ssl, :cipher_suites_erlang};
|
||||
"map" -> {:rabbit_ssl, :cipher_suites}
|
||||
end
|
||||
args = case opts do
|
||||
%{all: true} -> [:all];
|
||||
%{} -> [:default]
|
||||
end
|
||||
{mod, function} =
|
||||
case format do
|
||||
"openssl" -> {:rabbit_ssl, :cipher_suites_openssl}
|
||||
"erlang" -> {:rabbit_ssl, :cipher_suites_erlang}
|
||||
"map" -> {:rabbit_ssl, :cipher_suites}
|
||||
end
|
||||
|
||||
args =
|
||||
case opts do
|
||||
%{all: true} -> [:all]
|
||||
%{} -> [:default]
|
||||
end
|
||||
|
||||
:rabbit_misc.rpc_call(node_name, mod, function, args, timeout)
|
||||
end
|
||||
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
|
||||
def banner([], %{format: "openssl"}), do: "Listing available cipher suites in OpenSSL format"
|
||||
def banner([], %{format: "openssl"}), do: "Listing available cipher suites in OpenSSL format"
|
||||
def banner([], %{format: "erlang"}), do: "Listing available cipher suites in Erlang term format"
|
||||
def banner([], %{format: "map"}), do: "Listing available cipher suites in map format"
|
||||
|
||||
def help_section(), do: :observability_and_health_checks
|
||||
|
||||
def description(), do: "Lists cipher suites enabled by default. To list all available cipher suites, add the --all argument."
|
||||
def description(),
|
||||
do:
|
||||
"Lists cipher suites enabled by default. To list all available cipher suites, add the --all argument."
|
||||
|
||||
def usage, do: "cipher_suites [--format <openssl | erlang | map>] [--all]"
|
||||
|
||||
|
|
|
|||
|
|
@ -15,11 +15,13 @@ defmodule RabbitMQ.CLI.Diagnostics.Commands.CommandLineArgumentsCommand do
|
|||
def validate(_, %{formatter: "json"}) do
|
||||
{:validation_failure, :unsupported_formatter}
|
||||
end
|
||||
|
||||
use RabbitMQ.CLI.Core.AcceptsNoPositionalArguments
|
||||
|
||||
def run([], %{node: node_name}) do
|
||||
:rabbit_misc.rpc_call(node_name, :init, :get_arguments, [])
|
||||
end
|
||||
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
|
||||
def formatter(), do: RabbitMQ.CLI.Formatters.Erlang
|
||||
|
|
@ -35,7 +37,8 @@ defmodule RabbitMQ.CLI.Diagnostics.Commands.CommandLineArgumentsCommand do
|
|||
|
||||
def help_section(), do: :configuration
|
||||
|
||||
def description(), do: "Displays target node's command-line arguments and flags as reported by the runtime"
|
||||
def description(),
|
||||
do: "Displays target node's command-line arguments and flags as reported by the runtime"
|
||||
|
||||
def banner(_, %{node: node_name}), do: "Command line arguments of node #{node_name} ..."
|
||||
end
|
||||
|
|
|
|||
|
|
@ -22,24 +22,37 @@ defmodule RabbitMQ.CLI.Diagnostics.Commands.ConsumeEventStreamCommand do
|
|||
def run([], %{node: node_name, timeout: timeout, duration: duration, pattern: pattern}) do
|
||||
pid = self()
|
||||
ref = make_ref()
|
||||
subscribed = :rabbit_misc.rpc_call(
|
||||
node_name,
|
||||
:rabbit_event_consumer, :register,
|
||||
[pid, ref, duration, pattern],
|
||||
timeout)
|
||||
|
||||
subscribed =
|
||||
:rabbit_misc.rpc_call(
|
||||
node_name,
|
||||
:rabbit_event_consumer,
|
||||
:register,
|
||||
[pid, ref, duration, pattern],
|
||||
timeout
|
||||
)
|
||||
|
||||
case subscribed do
|
||||
{:ok, ^ref} ->
|
||||
Stream.unfold(:confinue,
|
||||
fn(:finished) -> nil
|
||||
(:confinue) ->
|
||||
Stream.unfold(
|
||||
:confinue,
|
||||
fn
|
||||
:finished ->
|
||||
nil
|
||||
|
||||
:confinue ->
|
||||
receive do
|
||||
{^ref, data, :finished} ->
|
||||
{data, :finished};
|
||||
{^ref, data, :confinue} ->
|
||||
{data, :confinue}
|
||||
end
|
||||
end)
|
||||
error -> error
|
||||
{^ref, data, :finished} ->
|
||||
{data, :finished}
|
||||
|
||||
{^ref, data, :confinue} ->
|
||||
{data, :confinue}
|
||||
end
|
||||
end
|
||||
)
|
||||
|
||||
error ->
|
||||
error
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -65,6 +78,7 @@ defmodule RabbitMQ.CLI.Diagnostics.Commands.ConsumeEventStreamCommand do
|
|||
def banner([], %{node: node_name, duration: :infinity}) do
|
||||
"Streaming logs from node #{node_name} ..."
|
||||
end
|
||||
|
||||
def banner([], %{node: node_name, duration: duration}) do
|
||||
"Streaming logs from node #{node_name} for #{duration} seconds ..."
|
||||
end
|
||||
|
|
|
|||
|
|
@ -13,9 +13,13 @@ defmodule RabbitMQ.CLI.Diagnostics.Commands.DisableAuthAttemptSourceTrackingComm
|
|||
use RabbitMQ.CLI.Core.RequiresRabbitAppRunning
|
||||
|
||||
def run([], %{node: node_name}) do
|
||||
:rabbit_misc.rpc_call(node_name, :application, :set_env,
|
||||
[:rabbit, :track_auth_attempt_source, :false])
|
||||
:rabbit_misc.rpc_call(node_name, :application, :set_env, [
|
||||
:rabbit,
|
||||
:track_auth_attempt_source,
|
||||
false
|
||||
])
|
||||
end
|
||||
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
|
||||
def usage, do: "disable_track_auth_attempt_source"
|
||||
|
|
@ -29,7 +33,8 @@ defmodule RabbitMQ.CLI.Diagnostics.Commands.DisableAuthAttemptSourceTrackingComm
|
|||
|
||||
def help_section(), do: :configuration
|
||||
|
||||
def description(), do: "Disables the tracking of peer IP address and username of authentication attempts"
|
||||
def description(),
|
||||
do: "Disables the tracking of peer IP address and username of authentication attempts"
|
||||
|
||||
def banner([], _), do: "Disabling authentication attempt source tracking ..."
|
||||
end
|
||||
|
|
|
|||
|
|
@ -13,8 +13,11 @@ defmodule RabbitMQ.CLI.Diagnostics.Commands.EnableAuthAttemptSourceTrackingComma
|
|||
use RabbitMQ.CLI.Core.RequiresRabbitAppRunning
|
||||
|
||||
def run([], %{node: node_name}) do
|
||||
:rabbit_misc.rpc_call(node_name, :application, :set_env,
|
||||
[:rabbit, :track_auth_attempt_source, :true])
|
||||
:rabbit_misc.rpc_call(node_name, :application, :set_env, [
|
||||
:rabbit,
|
||||
:track_auth_attempt_source,
|
||||
true
|
||||
])
|
||||
end
|
||||
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
|
|
@ -30,7 +33,8 @@ defmodule RabbitMQ.CLI.Diagnostics.Commands.EnableAuthAttemptSourceTrackingComma
|
|||
|
||||
def help_section(), do: :configuration
|
||||
|
||||
def description(), do: "Enables the tracking of peer IP address and username of authentication attempts"
|
||||
def description(),
|
||||
do: "Enables the tracking of peer IP address and username of authentication attempts"
|
||||
|
||||
def banner([], _), do: "Enabling authentication attempt source tracking ..."
|
||||
end
|
||||
|
|
|
|||
|
|
@ -13,19 +13,22 @@ defmodule RabbitMQ.CLI.Diagnostics.Commands.ErlangCookieHashCommand do
|
|||
|
||||
def run([], %{node: node_name, timeout: timeout}) do
|
||||
:rabbit_data_coercion.to_binary(
|
||||
:rabbit_misc.rpc_call(node_name, :rabbit_nodes_common, :cookie_hash, [], timeout))
|
||||
:rabbit_misc.rpc_call(node_name, :rabbit_nodes_common, :cookie_hash, [], timeout)
|
||||
)
|
||||
end
|
||||
|
||||
def output(result, %{formatter: "json"}) do
|
||||
{:ok, %{"result" => "ok", "value" => result}}
|
||||
end
|
||||
|
||||
def output(result, _options) when is_bitstring(result) do
|
||||
{:ok, result}
|
||||
end
|
||||
|
||||
def help_section(), do: :configuration
|
||||
|
||||
def description(), do: "Displays a hash of the Erlang cookie (shared secret) used by the target node"
|
||||
def description(),
|
||||
do: "Displays a hash of the Erlang cookie (shared secret) used by the target node"
|
||||
|
||||
def usage, do: "erlang_cookie_hash"
|
||||
|
||||
|
|
|
|||
|
|
@ -19,22 +19,30 @@ defmodule RabbitMQ.CLI.Diagnostics.Commands.ErlangCookieSourcesCommand do
|
|||
switch_cookie = opts[:erlang_cookie]
|
||||
home_dir = get_home_dir()
|
||||
cookie_file_path = Path.join(home_dir, ".erlang.cookie")
|
||||
cookie_file_stat = case File.stat(Path.join(home_dir, ".erlang.cookie")) do
|
||||
{:error, :enoent} -> nil
|
||||
{:ok, value} -> value
|
||||
end
|
||||
cookie_file_type = case cookie_file_stat do
|
||||
nil -> nil
|
||||
value -> value.type
|
||||
end
|
||||
cookie_file_access = case cookie_file_stat do
|
||||
nil -> nil
|
||||
value -> value.access
|
||||
end
|
||||
cookie_file_size = case cookie_file_stat do
|
||||
nil -> nil
|
||||
value -> value.size
|
||||
end
|
||||
|
||||
cookie_file_stat =
|
||||
case File.stat(Path.join(home_dir, ".erlang.cookie")) do
|
||||
{:error, :enoent} -> nil
|
||||
{:ok, value} -> value
|
||||
end
|
||||
|
||||
cookie_file_type =
|
||||
case cookie_file_stat do
|
||||
nil -> nil
|
||||
value -> value.type
|
||||
end
|
||||
|
||||
cookie_file_access =
|
||||
case cookie_file_stat do
|
||||
nil -> nil
|
||||
value -> value.access
|
||||
end
|
||||
|
||||
cookie_file_size =
|
||||
case cookie_file_stat do
|
||||
nil -> nil
|
||||
value -> value.size
|
||||
end
|
||||
|
||||
%{
|
||||
os_env_cookie_set: System.get_env("RABBITMQ_ERLANG_COOKIE") != nil,
|
||||
|
|
@ -66,7 +74,7 @@ defmodule RabbitMQ.CLI.Diagnostics.Commands.ErlangCookieSourcesCommand do
|
|||
"Cookie file exists? #{result[:cookie_file_exists]}",
|
||||
"Cookie file type: #{result[:cookie_file_type] || "(n/a)"}",
|
||||
"Cookie file access: #{result[:cookie_file_access] || "(n/a)"}",
|
||||
"Cookie file size: #{result[:cookie_file_size] || "(n/a)"}",
|
||||
"Cookie file size: #{result[:cookie_file_size] || "(n/a)"}"
|
||||
]
|
||||
|
||||
switch_lines = [
|
||||
|
|
@ -106,11 +114,11 @@ defmodule RabbitMQ.CLI.Diagnostics.Commands.ErlangCookieSourcesCommand do
|
|||
"""
|
||||
def get_home_dir() do
|
||||
homedrive = System.get_env("HOMEDRIVE")
|
||||
homepath = System.get_env("HOMEPATH")
|
||||
homepath = System.get_env("HOMEPATH")
|
||||
|
||||
case {homedrive != nil, homepath != nil} do
|
||||
{true, true} -> "#{homedrive}#{homepath}"
|
||||
_ -> System.get_env("HOME")
|
||||
_ -> System.get_env("HOME")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ defmodule RabbitMQ.CLI.Diagnostics.Commands.ErlangVersionCommand do
|
|||
def switches() do
|
||||
[details: :boolean, offline: :boolean, timeout: :integer]
|
||||
end
|
||||
|
||||
def aliases(), do: [t: :timeout]
|
||||
|
||||
def merge_defaults(args, opts) do
|
||||
|
|
@ -21,29 +22,31 @@ defmodule RabbitMQ.CLI.Diagnostics.Commands.ErlangVersionCommand do
|
|||
def run([], %{details: details, offline: true}) do
|
||||
case details do
|
||||
true ->
|
||||
:rabbit_data_coercion.to_binary(
|
||||
:rabbit_misc.otp_system_version())
|
||||
:rabbit_data_coercion.to_binary(:rabbit_misc.otp_system_version())
|
||||
|
||||
false ->
|
||||
:rabbit_data_coercion.to_binary(
|
||||
:rabbit_misc.platform_and_version())
|
||||
:rabbit_data_coercion.to_binary(:rabbit_misc.platform_and_version())
|
||||
end
|
||||
end
|
||||
|
||||
def run([], %{node: node_name, timeout: timeout, details: details}) do
|
||||
case details do
|
||||
true ->
|
||||
:rabbit_data_coercion.to_binary(
|
||||
:rabbit_misc.rpc_call(node_name, :rabbit_misc, :otp_system_version, [], timeout))
|
||||
:rabbit_misc.rpc_call(node_name, :rabbit_misc, :otp_system_version, [], timeout)
|
||||
)
|
||||
|
||||
false ->
|
||||
:rabbit_data_coercion.to_binary(
|
||||
:rabbit_misc.rpc_call(node_name, :rabbit_misc, :platform_and_version, [], timeout))
|
||||
:rabbit_data_coercion.to_binary(
|
||||
:rabbit_misc.rpc_call(node_name, :rabbit_misc, :platform_and_version, [], timeout)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
def output(result, %{formatter: "json"}) do
|
||||
{:ok, %{"result" => "ok", "value" => result}}
|
||||
end
|
||||
|
||||
def output(result, _opts) when is_bitstring(result) do
|
||||
{:ok, result}
|
||||
end
|
||||
|
|
@ -66,6 +69,7 @@ defmodule RabbitMQ.CLI.Diagnostics.Commands.ErlangVersionCommand do
|
|||
def banner([], %{offline: true}) do
|
||||
"CLI Erlang/OTP version ..."
|
||||
end
|
||||
|
||||
def banner([], %{node: node_name}) do
|
||||
"Asking node #{node_name} for its Erlang/OTP version..."
|
||||
end
|
||||
|
|
|
|||
|
|
@ -20,16 +20,20 @@ defmodule RabbitMQ.CLI.Diagnostics.Commands.IsBootingCommand do
|
|||
"result" => true,
|
||||
"message" => "RabbitMQ on node #{node_name} is booting"
|
||||
}
|
||||
|
||||
{:ok, m}
|
||||
end
|
||||
|
||||
def output(false, %{node: node_name, formatter: "json"}) do
|
||||
m = %{
|
||||
"result" => false,
|
||||
"message" => "RabbitMQ on node #{node_name} is fully booted (check with is_running), stopped or has not started booting yet"
|
||||
"message" =>
|
||||
"RabbitMQ on node #{node_name} is fully booted (check with is_running), stopped or has not started booting yet"
|
||||
}
|
||||
|
||||
{:ok, m}
|
||||
end
|
||||
|
||||
def output(true, %{node: node_name}) do
|
||||
{:ok, "RabbitMQ on node #{node_name} is booting"}
|
||||
end
|
||||
|
|
|
|||
|
|
@ -18,19 +18,28 @@ defmodule RabbitMQ.CLI.Diagnostics.Commands.IsRunningCommand do
|
|||
end
|
||||
|
||||
def output(true, %{node: node_name, formatter: "json"}) do
|
||||
{:ok, %{"result" => true, "message" => "RabbitMQ on node #{node_name} is fully booted and running"}}
|
||||
{:ok,
|
||||
%{"result" => true, "message" => "RabbitMQ on node #{node_name} is fully booted and running"}}
|
||||
end
|
||||
|
||||
def output(false, %{node: node_name, formatter: "json"}) do
|
||||
{:ok,
|
||||
%{"result" => false, "message" => "RabbitMQ on node #{node_name} is not running or has not fully booted yet (check with is_booting)"}}
|
||||
%{
|
||||
"result" => false,
|
||||
"message" =>
|
||||
"RabbitMQ on node #{node_name} is not running or has not fully booted yet (check with is_booting)"
|
||||
}}
|
||||
end
|
||||
|
||||
def output(true, %{node: node_name}) do
|
||||
{:ok, "RabbitMQ on node #{node_name} is fully booted and running"}
|
||||
end
|
||||
|
||||
def output(false, %{node: node_name}) do
|
||||
{:ok,
|
||||
"RabbitMQ on node #{node_name} is not running or has not fully booted yet (check with is_booting)"}
|
||||
end
|
||||
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
|
||||
def help_section(), do: :observability_and_health_checks
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ defmodule RabbitMQ.CLI.Diagnostics.Commands.ListNetworkInterfacesCommand do
|
|||
def run([], %{offline: true}) do
|
||||
:rabbit_net.getifaddrs()
|
||||
end
|
||||
|
||||
def run([], %{node: node_name, timeout: timeout}) do
|
||||
:rabbit_misc.rpc_call(node_name, :rabbit_net, :getifaddrs, [], timeout)
|
||||
end
|
||||
|
|
@ -29,11 +30,14 @@ defmodule RabbitMQ.CLI.Diagnostics.Commands.ListNetworkInterfacesCommand do
|
|||
def output(nic_map, %{node: node_name, formatter: "json"}) when map_size(nic_map) == 0 do
|
||||
{:ok, %{"result" => "ok", "node" => node_name, "interfaces" => %{}}}
|
||||
end
|
||||
|
||||
def output(nic_map, %{node: node_name}) when map_size(nic_map) == 0 do
|
||||
{:ok, "Node #{node_name} reported no network interfaces"}
|
||||
end
|
||||
|
||||
def output(nic_map0, %{node: node_name, formatter: "json"}) do
|
||||
nic_map = Enum.map(nic_map0, fn ({k, v}) -> {to_string(k), v} end)
|
||||
nic_map = Enum.map(nic_map0, fn {k, v} -> {to_string(k), v} end)
|
||||
|
||||
{:ok,
|
||||
%{
|
||||
"result" => "ok",
|
||||
|
|
@ -41,11 +45,13 @@ defmodule RabbitMQ.CLI.Diagnostics.Commands.ListNetworkInterfacesCommand do
|
|||
"message" => "Node #{node_name} reported network interfaces"
|
||||
}}
|
||||
end
|
||||
|
||||
def output(nic_map, _) when is_map(nic_map) do
|
||||
lines = nic_lines(nic_map)
|
||||
|
||||
{:ok, Enum.join(lines, line_separator())}
|
||||
end
|
||||
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
|
||||
def help_section(), do: :observability_and_health_checks
|
||||
|
|
@ -63,15 +69,14 @@ defmodule RabbitMQ.CLI.Diagnostics.Commands.ListNetworkInterfacesCommand do
|
|||
#
|
||||
|
||||
defp nic_lines(nic_map) do
|
||||
Enum.reduce(nic_map, [],
|
||||
fn({iface, props}, acc) ->
|
||||
iface_lines = Enum.reduce(props, [],
|
||||
fn({prop, val}, inner_acc) ->
|
||||
["#{prop}: #{val}" | inner_acc]
|
||||
end)
|
||||
Enum.reduce(nic_map, [], fn {iface, props}, acc ->
|
||||
iface_lines =
|
||||
Enum.reduce(props, [], fn {prop, val}, inner_acc ->
|
||||
["#{prop}: #{val}" | inner_acc]
|
||||
end)
|
||||
|
||||
header = "#{bright("Interface #{iface}")}\n"
|
||||
acc ++ [header | iface_lines] ++ ["\n"]
|
||||
end)
|
||||
header = "#{bright("Interface #{iface}")}\n"
|
||||
acc ++ [header | iface_lines] ++ ["\n"]
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -26,30 +26,45 @@ defmodule RabbitMQ.CLI.Diagnostics.Commands.ListNodeAuthAttemptStatsCommand do
|
|||
|
||||
def run([], %{node: node_name, timeout: timeout, by_source: by_source}) do
|
||||
case by_source do
|
||||
:true ->
|
||||
true ->
|
||||
:rabbit_misc.rpc_call(
|
||||
node_name, :rabbit_core_metrics, :get_auth_attempts_by_source, [], timeout)
|
||||
:false ->
|
||||
node_name,
|
||||
:rabbit_core_metrics,
|
||||
:get_auth_attempts_by_source,
|
||||
[],
|
||||
timeout
|
||||
)
|
||||
|
||||
false ->
|
||||
:rabbit_misc.rpc_call(
|
||||
node_name, :rabbit_core_metrics, :get_auth_attempts, [], timeout)
|
||||
node_name,
|
||||
:rabbit_core_metrics,
|
||||
:get_auth_attempts,
|
||||
[],
|
||||
timeout
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
def output([], %{node: node_name, formatter: "json"}) do
|
||||
{:ok, %{"result" => "ok", "node" => node_name, "attempts" => []}}
|
||||
end
|
||||
def output([], %{node: node_name}) do
|
||||
|
||||
def output([], %{node: node_name}) do
|
||||
{:ok, "Node #{node_name} reported no authentication attempt stats"}
|
||||
end
|
||||
|
||||
def output(rows, %{node: node_name, formatter: "json"}) do
|
||||
maps = Enum.map(rows, &Map.new/1)
|
||||
|
||||
{:ok,
|
||||
%{
|
||||
"result" => "ok",
|
||||
"node" => node_name,
|
||||
"result" => "ok",
|
||||
"node" => node_name,
|
||||
"attempts" => maps
|
||||
}}
|
||||
end
|
||||
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
|
||||
def usage, do: "list_node_auth_attempts [--by-source]"
|
||||
|
|
|
|||
|
|
@ -23,25 +23,29 @@ defmodule RabbitMQ.CLI.Diagnostics.Commands.LogLocationCommand do
|
|||
|
||||
def run([], %{node: node_name, timeout: timeout, all: all}) do
|
||||
case all do
|
||||
true -> LogFiles.get_log_locations(node_name, timeout);
|
||||
true -> LogFiles.get_log_locations(node_name, timeout)
|
||||
false -> LogFiles.get_default_log_location(node_name, timeout)
|
||||
end
|
||||
end
|
||||
|
||||
def output({:ok, location}, %{node: node_name, formatter: "json"}) do
|
||||
{:ok, %{
|
||||
"result" => "ok",
|
||||
"node_name" => node_name,
|
||||
"paths" => [location]
|
||||
}}
|
||||
{:ok,
|
||||
%{
|
||||
"result" => "ok",
|
||||
"node_name" => node_name,
|
||||
"paths" => [location]
|
||||
}}
|
||||
end
|
||||
|
||||
def output(locations, %{node: node_name, formatter: "json"}) do
|
||||
{:ok, %{
|
||||
"result" => "ok",
|
||||
"node_name" => node_name,
|
||||
"paths" => locations
|
||||
}}
|
||||
{:ok,
|
||||
%{
|
||||
"result" => "ok",
|
||||
"node_name" => node_name,
|
||||
"paths" => locations
|
||||
}}
|
||||
end
|
||||
|
||||
use RabbitMQ.CLI.DefaultOutput
|
||||
|
||||
def help_section(), do: :configuration
|
||||
|
|
|
|||
|
|
@ -13,20 +13,21 @@ defmodule RabbitMQ.CLI.Diagnostics.Commands.LogTailCommand do
|
|||
alias RabbitMQ.CLI.Core.LogFiles
|
||||
|
||||
def switches, do: [number: :integer, timeout: :integer]
|
||||
def aliases, do: ['N': :number, t: :timeout]
|
||||
def aliases, do: [N: :number, t: :timeout]
|
||||
|
||||
def merge_defaults(args, opts) do
|
||||
{args, Map.merge(%{number: 50}, opts)}
|
||||
end
|
||||
|
||||
use RabbitMQ.CLI.Core.AcceptsNoPositionalArguments
|
||||
|
||||
def run([], %{node: node_name, timeout: timeout, number: n}) do
|
||||
case LogFiles.get_default_log_location(node_name, timeout) do
|
||||
{:ok, file} ->
|
||||
:rabbit_misc.rpc_call(node_name,
|
||||
:rabbit_log_tail, :tail_n_lines, [file, n],
|
||||
timeout)
|
||||
error -> error
|
||||
:rabbit_misc.rpc_call(node_name, :rabbit_log_tail, :tail_n_lines, [file, n], timeout)
|
||||
|
||||
error ->
|
||||
error
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ defmodule RabbitMQ.CLI.Diagnostics.Commands.LogTailStreamCommand do
|
|||
|
||||
alias RabbitMQ.CLI.Core.LogFiles
|
||||
|
||||
|
||||
def switches(), do: [duration: :integer, timeout: :integer]
|
||||
def aliases(), do: [d: :duration, t: :timeout]
|
||||
|
||||
|
|
@ -29,24 +28,38 @@ defmodule RabbitMQ.CLI.Diagnostics.Commands.LogTailStreamCommand do
|
|||
{:ok, file} ->
|
||||
pid = self()
|
||||
ref = make_ref()
|
||||
subscribed = :rabbit_misc.rpc_call(
|
||||
node_name,
|
||||
:rabbit_log_tail, :init_tail_stream,
|
||||
[file, pid, ref, duration],
|
||||
timeout)
|
||||
|
||||
subscribed =
|
||||
:rabbit_misc.rpc_call(
|
||||
node_name,
|
||||
:rabbit_log_tail,
|
||||
:init_tail_stream,
|
||||
[file, pid, ref, duration],
|
||||
timeout
|
||||
)
|
||||
|
||||
case subscribed do
|
||||
{:ok, ^ref} ->
|
||||
Stream.unfold(:confinue,
|
||||
fn(:finished) -> nil
|
||||
(:confinue) ->
|
||||
Stream.unfold(
|
||||
:confinue,
|
||||
fn
|
||||
:finished ->
|
||||
nil
|
||||
|
||||
:confinue ->
|
||||
receive do
|
||||
{^ref, data, :finished} -> {data, :finished};
|
||||
{^ref, data, :finished} -> {data, :finished}
|
||||
{^ref, data, :confinue} -> {data, :confinue}
|
||||
end
|
||||
end)
|
||||
error -> error
|
||||
end
|
||||
)
|
||||
|
||||
error ->
|
||||
error
|
||||
end
|
||||
error -> error
|
||||
|
||||
error ->
|
||||
error
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -67,6 +80,7 @@ defmodule RabbitMQ.CLI.Diagnostics.Commands.LogTailStreamCommand do
|
|||
def banner([], %{node: node_name, duration: :infinity}) do
|
||||
"Streaming logs from node #{node_name} ..."
|
||||
end
|
||||
|
||||
def banner([], %{node: node_name, duration: duration}) do
|
||||
"Streaming logs from node #{node_name} for #{duration} seconds ..."
|
||||
end
|
||||
|
|
|
|||
|
|
@ -19,7 +19,9 @@ defmodule RabbitMQ.CLI.Diagnostics.Commands.MaybeStuckCommand do
|
|||
|
||||
def help_section(), do: :observability_and_health_checks
|
||||
|
||||
def description(), do: "Detects Erlang processes (\"lightweight threads\") potentially not making progress on the target node"
|
||||
def description(),
|
||||
do:
|
||||
"Detects Erlang processes (\"lightweight threads\") potentially not making progress on the target node"
|
||||
|
||||
def usage, do: "maybe_stuck"
|
||||
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue