From a0d21c7108e8f95b541bdb7653d2595f68e42520 Mon Sep 17 00:00:00 2001 From: Adam King Date: Fri, 25 Feb 2022 17:55:06 -0500 Subject: [PATCH] mgr/cephadm: block draining last _admin host Fixes: https://tracker.ceph.com/issues/54413 Signed-off-by: Adam King --- src/pybind/mgr/cephadm/module.py | 18 ++++++++++++++++-- src/pybind/mgr/orchestrator/_interface.py | 2 +- src/pybind/mgr/orchestrator/module.py | 4 ++-- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/pybind/mgr/cephadm/module.py b/src/pybind/mgr/cephadm/module.py index ae428249e4601..116088e1657ad 100644 --- a/src/pybind/mgr/cephadm/module.py +++ b/src/pybind/mgr/cephadm/module.py @@ -2701,12 +2701,26 @@ Then run the following: return self.to_remove_osds.all_osds() @handle_orch_error - def drain_host(self, hostname): - # type: (str) -> str + def drain_host(self, hostname, force=False): + # type: (str, bool) -> str """ Drain all daemons from a host. :param host: host name """ + + # if we drain the last admin host we could end up removing the only instance + # of the config and keyring and cause issues + if not force: + p = PlacementSpec(label='_admin') + admin_hosts = p.filter_matching_hostspecs(self.inventory.all_specs()) + if len(admin_hosts) == 1 and admin_hosts[0] == hostname: + raise OrchestratorValidationError(f"Host {hostname} is the last host with the '_admin'" + " label.\nDraining this host could cause the removal" + " of the last cluster config/keyring managed by cephadm.\n" + "It is recommended to add the _admin label to another host" + " before completing this operation.\nIf you're certain this is" + " what you want rerun this command with --force.") + self.add_host_label(hostname, '_no_schedule') daemons: List[orchestrator.DaemonDescription] = self.cache.get_daemons_by_host(hostname) diff --git a/src/pybind/mgr/orchestrator/_interface.py b/src/pybind/mgr/orchestrator/_interface.py index 344dea13854e8..b3311019747ac 100644 --- a/src/pybind/mgr/orchestrator/_interface.py +++ b/src/pybind/mgr/orchestrator/_interface.py @@ -355,7 +355,7 @@ class Orchestrator(object): """ raise NotImplementedError() - def drain_host(self, hostname: str) -> OrchResult[str]: + def drain_host(self, hostname: str, force: bool = False) -> OrchResult[str]: """ drain all daemons from a host diff --git a/src/pybind/mgr/orchestrator/module.py b/src/pybind/mgr/orchestrator/module.py index cdf93f7a4bd46..857667dff53f6 100644 --- a/src/pybind/mgr/orchestrator/module.py +++ b/src/pybind/mgr/orchestrator/module.py @@ -360,9 +360,9 @@ class OrchestratorCli(OrchestratorClientMixin, MgrModule, return HandleCommandResult(stdout=completion.result_str()) @_cli_write_command('orch host drain') - def _drain_host(self, hostname: str) -> HandleCommandResult: + def _drain_host(self, hostname: str, force: bool = False) -> HandleCommandResult: """drain all daemons from a host""" - completion = self.drain_host(hostname) + completion = self.drain_host(hostname, force) raise_if_exception(completion) return HandleCommandResult(stdout=completion.result_str()) -- 2.39.5