diff options
Diffstat (limited to 'deps/rabbitmq_cli/lib/rabbitmq/cli/queues/commands/check_if_node_is_mirror_sync_critical_command.ex')
-rw-r--r-- | deps/rabbitmq_cli/lib/rabbitmq/cli/queues/commands/check_if_node_is_mirror_sync_critical_command.ex | 105 |
1 files changed, 105 insertions, 0 deletions
diff --git a/deps/rabbitmq_cli/lib/rabbitmq/cli/queues/commands/check_if_node_is_mirror_sync_critical_command.ex b/deps/rabbitmq_cli/lib/rabbitmq/cli/queues/commands/check_if_node_is_mirror_sync_critical_command.ex new file mode 100644 index 0000000000..c31b83d29c --- /dev/null +++ b/deps/rabbitmq_cli/lib/rabbitmq/cli/queues/commands/check_if_node_is_mirror_sync_critical_command.ex @@ -0,0 +1,105 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +## Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. + +defmodule RabbitMQ.CLI.Queues.Commands.CheckIfNodeIsMirrorSyncCriticalCommand do + @moduledoc """ + Exits with a non-zero code if there are classic mirrored queues that don't + have any in sync mirrors online and would potentially lose data + if the target node is shut down. + + This command is meant to be used as a pre-upgrade (pre-shutdown) check. + """ + + @behaviour RabbitMQ.CLI.CommandBehaviour + + import RabbitMQ.CLI.Core.Platform, only: [line_separator: 0] + + def scopes(), do: [:diagnostics, :queues] + + use RabbitMQ.CLI.Core.AcceptsDefaultSwitchesAndTimeout + use RabbitMQ.CLI.Core.MergesNoDefaults + use RabbitMQ.CLI.Core.AcceptsNoPositionalArguments + use RabbitMQ.CLI.Core.RequiresRabbitAppRunning + + def run([], %{node: node_name, timeout: timeout}) do + case :rabbit_misc.rpc_call(node_name, + :rabbit_nodes, :is_single_node_cluster, [], timeout) do + # if target node is the only one in the cluster, the check makes little sense + # and false positives can be misleading + true -> {:ok, :single_node_cluster} + false -> + case :rabbit_misc.rpc_call(node_name, + :rabbit_amqqueue, :list_local_mirrored_classic_without_synchronised_mirrors_for_cli, [], timeout) do + [] -> {:ok, []} + qs when is_list(qs) -> {:ok, qs} + other -> other + end + other -> other + end + end + + def output({:ok, :single_node_cluster}, %{formatter: "json"}) do + {:ok, %{ + "result" => "ok", + "message" => "Target node seems to be the only one in a single node cluster, the check does not apply" + }} + end + def output({:ok, []}, %{formatter: "json"}) do + {:ok, %{"result" => "ok"}} + end + def output({:ok, :single_node_cluster}, %{silent: true}) do + {:ok, :check_passed} + end + def output({:ok, []}, %{silent: true}) do + {:ok, :check_passed} + end + def output({:ok, :single_node_cluster}, %{node: node_name}) do + {:ok, "Node #{node_name} seems to be the only one in a single node cluster, the check does not apply"} + end + def output({:ok, []}, %{node: node_name}) do + {:ok, "Node #{node_name} reported no classic mirrored queues without online synchronised mirrors"} + end + def output({:ok, qs}, %{node: node_name, formatter: "json"}) when is_list(qs) do + {:error, :check_failed, + %{ + "result" => "error", + "queues" => qs, + "message" => "Node #{node_name} reported local classic mirrored queues without online synchronised mirrors" + }} + end + def output({:ok, qs}, %{silent: true}) when is_list(qs) do + {:error, :check_failed} + end + def output({:ok, qs}, %{node: node_name}) when is_list(qs) do + lines = queue_lines(qs, node_name) + + {:error, :check_failed, Enum.join(lines, line_separator())} + end + use RabbitMQ.CLI.DefaultOutput + + def help_section(), do: :observability_and_health_checks + + def description() do + "Health check that exits with a non-zero code if there are classic mirrored queues " <> + "without online synchronised mirrors (queues that would potentially lose data if the target node is shut down)" + end + + def usage, do: "check_if_node_is_mirror_sync_critical" + + def banner([], %{node: node_name}) do + "Checking if node #{node_name} is critical for data safety of any classic mirrored queues ..." + end + + # + # Implementation + # + + def queue_lines(qs, node_name) do + for q <- qs do + "#{q["readable_name"]} would lose its only synchronised replica (master) if node #{node_name} is stopped" + end + end +end |