import json
import re
import logging
-from typing import TYPE_CHECKING, Iterator, Optional, Dict, Any, List
+from typing import TYPE_CHECKING, Iterator, Optional, Dict, Any, List, cast
-from ceph.deployment.service_spec import PlacementSpec, ServiceSpec, HostPlacementSpec, RGWSpec, CertificateSource
+from ceph.deployment.service_spec import PlacementSpec, ServiceSpec, HostPlacementSpec, RGWSpec, CertificateSource, NFSServiceSpec
from cephadm.schedule import HostAssignment
from cephadm.utils import SpecialHostLabels
+from cephadm.services.nfs import NFSService
+from cephadm.services.service_registry import service_registry
import rados
from mgr_util import get_cert_issuer_info
if TYPE_CHECKING:
from .module import CephadmOrchestrator
-LAST_MIGRATION = 8
+LAST_MIGRATION = 9
logger = logging.getLogger(__name__)
if self.migrate_7_8():
self.set(8)
+ if self.mgr.migration_current == 8 and not startup:
+ logger.info('Running migration 8 -> 9')
+ if self.migrate_8_9():
+ self.set(9)
+
def migrate_0_1(self) -> bool:
"""
Migration 0 -> 1
logger.error(f"Promtail -> Alloy migration failed: {e}")
return False
+ def migrate_8_9(self) -> bool:
+ # For NFS, check if nfs cluster is using old types of node ids (service_name.0)
+ # if yes, then store those daemons in mon store, to continue using old node ids for those daemons
+ nfs_services = []
+ service_specs = self.mgr.spec_store.get_specs_by_type('nfs')
+ nfs_service = cast(NFSService, service_registry.get_service('nfs'))
+ try:
+ for service_name, spec in service_specs.items():
+ # get grace tool dump
+ out = nfs_service.run_grace_tool(cast(NFSServiceSpec, spec), 'dump')
+ if service_name in out:
+ nfs_services.append(service_name)
+ logger.info(f'NFS service {nfs_service} needs to maintain old node ids after upgrade')
+ except Exception as e:
+ logger.exception(f'Got error while executing grace tool: {e}')
+ self.mgr.set_health_warning('CEPHADM_MIGRATION_FAILURE',
+ f'Cephadm migration failed: {e}',
+ 1, [str(e)])
+ return False
+ if nfs_services:
+ self.mgr.set_store('nfs_services_with_old_nodeid', ','.join(nfs_services))
+ self.mgr.remove_health_warning('CEPHADM_MIGRATION_FAILURE')
+ return True
+
def queue_migrate_rgw_spec(mgr: "CephadmOrchestrator", spec_dict: Dict[Any, Any]) -> None:
"""
for h, ls in osds_msg.items():
msg += f'\thost {h}: {" ".join([f"osd.{id}" for id in ls])}'
raise OrchestratorError(
- f'If {service_name} is removed then the following OSDs '
- f'will remain, --force to proceed anyway\n{msg}'
- )
+ f'If {service_name} is removed then the following OSDs will remain, --force to proceed anyway\n{msg}')
+ elif service_name.startswith('nfs.'):
+ # check if its using old node id style and remove from mon store
+ nfs_services = self.get_store('nfs_services_with_old_nodeid')
+ if nfs_services:
+ nfs_services = nfs_services.split(',')
+ if service_name in nfs_services:
+ nfs_services.remove(service_name)
+ val = ','.join(nfs_services) if nfs_services else None
+ self.set_store('nfs_services_with_old_nodeid', val)
spec = self.spec_store[service_name].spec
CephadmServe(self)._remove_service_config(spec)
for daemon_id in m.values():
if daemon_id is not None:
self.fence(daemon_id)
- nodeid = f'{rank}'
+ nodeid = self.get_daemon_nodeid(spec.service_name(), rank)
self.mgr.log.info(
"Removing %s from the ganesha grace table for service %s", nodeid, service_name
)
daemon_spec.final_config, daemon_spec.deps = self.generate_config(daemon_spec)
return daemon_spec
+ def get_daemon_nodeid(self, service_name: str, rank: Optional[int]) -> str:
+ out = self.mgr.get_store('nfs_services_with_old_nodeid')
+ if out and service_name in out.split(','):
+ return f'{service_name}.{rank}'
+ return str(rank)
+
def generate_config(self, daemon_spec: CephadmDaemonDeploySpec) -> Tuple[Dict[str, Any], List[str]]:
assert self.TYPE == daemon_spec.daemon_type
super().prepare_certificates(daemon_spec)
host = daemon_spec.host
spec = cast(NFSServiceSpec, self.mgr.spec_store[daemon_spec.service_name].spec)
- nodeid = f'{daemon_spec.rank}'
+ nodeid = self.get_daemon_nodeid(spec.service_name(), daemon_spec.rank)
nfs_idmap_conf = '/etc/ganesha/idmap.conf'
"tls_ktls": spec.tls_ktls,
"tls_debug": spec.tls_debug,
"ceph_nodes": ceph_nodes,
- "protocols": "3, 4" if spec.enable_nfsv3 else "4"
+ "protocols": "3, 4" if spec.enable_nfsv3 else "4",
+ "use_old_nodeid": False if nodeid.isdigit() else True
}
if spec.enable_haproxy_protocol:
context["haproxy_hosts"] = self._haproxy_hosts()
def run_grace_tool(self,
spec: NFSServiceSpec,
action: str,
- nodeid: str) -> None:
+ nodeid: str = '') -> str:
# write a temp keyring and referencing config file. this is a kludge
# because the ganesha-grace-tool can only authenticate as a client (and
# not a mgr). Also, it doesn't allow you to pass a keyring location via
'Ignore ganesha-rados-grace tool remove failure as %s does not exists for %s service',
nodeid, spec.service_name()
)
- return
+ return ''
raise RuntimeError(f'grace tool failed for service {spec.service_name()}: {stderr}')
+ return result.stdout.decode("utf-8")
finally:
self.mgr.check_mon_command({
RADOS_KV {
UserId = "{{ user }}";
- nodeid = {{ nodeid }};
+ nodeid = "{{ nodeid }}";
pool = "{{ pool }}";
namespace = "{{ namespace }}";
}
name = "client.{{ rgw_user }}";
}
+{% if use_old_nodeid %}
+Ceph {
+ Use_old_uuid = true;
+}
+
+{% endif %}
{% if tls_add %}
TLS_CONFIG{
Enable_TLS = {{ tls_add }};
'\n'
'RADOS_KV {\n'
' UserId = "nfs.foo.test.0.0";\n'
- ' nodeid = 0;\n'
+ ' nodeid = "0";\n'
' pool = ".nfs";\n'
' namespace = "foo";\n'
'}\n'
assert len(cephadm_module.spec_store.all_specs) == 0
+@mock.patch("cephadm.services.nfs.NFSService.run_grace_tool", mock.MagicMock())
@mock.patch("cephadm.serve.CephadmServe._run_cephadm", _run_cephadm('[]'))
def test_migrate_nfs_initial(cephadm_module: CephadmOrchestrator):
with with_host(cephadm_module, 'host1'):
assert cephadm_module.migration_current == LAST_MIGRATION
+@mock.patch("cephadm.services.nfs.NFSService.run_grace_tool", mock.MagicMock())
@mock.patch("cephadm.serve.CephadmServe._run_cephadm", _run_cephadm('[]'))
def test_migrate_nfs_initial_octopus(cephadm_module: CephadmOrchestrator):
with with_host(cephadm_module, 'host1'):