def _check_cert_source(self, spec: ServiceSpec) -> str:
cert_warning = ''
+ # Warn the user when certificate_source is changing, as this will
+ # trigger a service reconfiguration that may cause client disconnections,
+ # CA trust chain changes, or temporary TLS downtime.
+ if spec.service_name() in self.spec_store:
+ old_spec = self.spec_store[spec.service_name()].spec
+ old_source = getattr(old_spec, 'certificate_source', None)
+ new_source = getattr(spec, 'certificate_source', None)
+ if old_source and new_source and old_source != new_source:
+ cert_warning = (
+ f"\n\nWarning: 'certificate_source' changed from '{old_source}' to "
+ f"'{new_source}' for service '{spec.service_name()}'.\n"
+ f"This will trigger a service reconfiguration on the next reconciliation cycle, which may cause\n"
+ f"temporary client disconnections and/or a change in the TLS certificate authority trust chain.\n"
+ )
+ self.log.warning(
+ f"certificate_source changed from '{old_source}' to '{new_source}' "
+ f"for service '{spec.service_name()}'. This will trigger a service "
+ f"reconfiguration."
+ )
+
if spec.is_using_certificates_source(CertificateSource.REFERENCE):
svc = service_registry.get_service(spec.service_type)
if svc.SCOPE == TLSObjectScope.SERVICE:
f"\n > ceph orch certmgr cert set --cert-name {svc.cert_name} --service-name {spec.service_name()} -i <cert-key-pem-file> \n"
)
else:
- cert_warning = (
+ cert_warning += (
f"\n\n\nWarning: SSL is configured with '{CertificateSource.REFERENCE.value}', and this service uses per-host certificates.\n\n"
f"To configure keys/certificates, run the following commands for each host daemons are deployed on:\n"
f" > ceph orch certmgr cert set --cert-name {svc.cert_name} --service-name {spec.service_name()} --hostname <host> -i <cert-file>\n"
spec: Optional[ServiceSpec] = None,
daemon_type: Optional[str] = None,
) -> List[str]:
- return []
+
+ ssl_enabled = getattr(spec, 'ssl', False)
+ if not spec or not ssl_enabled:
+ return []
+
+ deps = []
+ cert_source = getattr(spec, 'certificate_source', None)
+ if cert_source:
+ deps.append(f'certificate_source: {cert_source}')
+ if spec.ssl_cert and spec.ssl_key:
+ deps.append(f'ssl_cert: {str(utils.md5_hash(spec.ssl_cert))}')
+ deps.append(f'ssl_key: {str(utils.md5_hash(spec.ssl_key))}')
+ if spec.ssl_ca_cert:
+ deps.append(f'ssl_ca_cert: {str(utils.md5_hash(spec.ssl_ca_cert))}')
+
+ return sorted(deps)
@classmethod
def sorted_dependencies(
spec: Optional[ServiceSpec] = None,
daemon_type: Optional[str] = None) -> List[str]:
deps = []
+ # we keep the following deps calculation for backward compatibility
+ # as old RGW specs use rgw_frontend_ssl_certificate instead of modern
+ # ssl_cert/ssl_key fields
rgw_spec = cast(RGWSpec, spec)
ssl_cert = getattr(rgw_spec, 'rgw_frontend_ssl_certificate', None)
if ssl_cert:
ssl_cert = '\n'.join(ssl_cert)
deps.append(f'ssl-cert:{utils.config_hash(ssl_cert)}')
- return sorted(deps)
+ parent_deps = super().get_dependencies(mgr, spec, daemon_type)
+ return sorted(deps + parent_deps)
def set_realm_zg_zone(self, spec: RGWSpec) -> None:
assert self.TYPE == spec.service_type
assert ingress_spec.backend_service
daemons = mgr.cache.get_daemons_by_service(ingress_spec.backend_service)
deps = [d.name() for d in daemons]
- for attr in ['ssl_cert', 'ssl_key']:
- ssl_cert_key = getattr(ingress_spec, attr, None)
- if ssl_cert_key:
- assert isinstance(ssl_cert_key, str)
- deps.append(f'ssl-cert-key:{utils.config_hash(ssl_cert_key)}')
backend_spec = mgr.spec_store[ingress_spec.backend_service].spec
if backend_spec.service_type == 'nfs':
hosts = get_placement_hosts(spec, mgr.cache.get_schedulable_hosts(), mgr.cache.get_draining_hosts())
deps.append(f'placement_hosts:{",".join(sorted(h.hostname for h in hosts))}')
- return sorted(deps)
+
+ from cephadm.services.cephadmservice import CephadmService
+ parent_deps = CephadmService.get_dependencies(mgr, spec)
+ return sorted(deps + parent_deps)
def haproxy_generate_config(
self,
def get_dependencies(cls, mgr: "CephadmOrchestrator",
spec: Optional[ServiceSpec] = None,
daemon_type: Optional[str] = None) -> List[str]:
+ deps = []
if spec:
iscsi_spec = cast(IscsiServiceSpec, spec)
- return [get_trusted_ips(mgr, iscsi_spec)]
+ deps = [get_trusted_ips(mgr, iscsi_spec)]
else:
- return [mgr.get_mgr_ip()]
+ deps = [mgr.get_mgr_ip()]
+
+ parent_deps = super().get_dependencies(mgr, spec, daemon_type)
+ return sorted(deps + parent_deps)
def prepare_create(self, daemon_spec: CephadmDaemonDeploySpec) -> CephadmDaemonDeploySpec:
assert self.TYPE == daemon_spec.daemon_type
for service in ['mgr']
for d in mgr.cache.get_daemons_by_service(service)
]
- return deps
+ parent_deps = super().get_dependencies(mgr, spec, daemon_type)
+ return sorted(deps + parent_deps)
def generate_config(self, daemon_spec: CephadmDaemonDeploySpec) -> Tuple[Dict[str, Any], List[str]]:
assert self.TYPE == daemon_spec.daemon_type
for service in ['prometheus', 'loki', 'mgmt-gateway', 'oauth2-proxy']:
deps += [d.name() for d in mgr.cache.get_daemons_by_service(service)]
- return sorted(deps)
+ parent_deps = super().get_dependencies(mgr, spec, daemon_type)
+ return sorted(deps + parent_deps)
def generate_prom_services(self, security_enabled: bool, mgmt_gw_enabled: bool) -> List[str]:
dashboard = f.read()
config_file['files'][f'/etc/grafana/provisioning/dashboards/{file_name}'] = dashboard
- return config_file, self.get_dependencies(self.mgr)
+ return config_file, self.get_dependencies(self.mgr, spec)
def get_active_daemon(self, daemon_descrs: List[DaemonDescription]) -> DaemonDescription:
# Use the least-created one as the active daemon
from .service_registry import register_cephadm_service
from orchestrator import DaemonDescription, OrchestratorError
-from cephadm import utils
from cephadm.services.cephadmservice import AuthEntity, CephadmDaemonDeploySpec, CephService
from cephadm.schedule import get_placement_hosts
if TYPE_CHECKING:
assert spec
deps: List[str] = []
nfs_spec = cast(NFSServiceSpec, spec)
- # add dependency of tls fields
- if (spec.ssl and spec.ssl_cert and spec.ssl_key and spec.ssl_ca_cert):
- deps.append(f'ssl_cert: {utils.config_hash(spec.ssl_cert)}')
- deps.append(f'ssl_key: {utils.config_hash(spec.ssl_key)}')
- deps.append(f'ssl_ca_cert: {utils.config_hash(spec.ssl_ca_cert)}')
+ deps.append(f'enable_rdma: {nfs_spec.enable_rdma}')
+ deps.append(f'rdma_port: {nfs_spec.rdma_port}')
deps.append(f'tls_ktls: {nfs_spec.tls_ktls}')
deps.append(f'tls_debug: {nfs_spec.tls_debug}')
deps.append(f'tls_min_version: {nfs_spec.tls_min_version}')
deps.append(f'tls_ciphers: {nfs_spec.tls_ciphers}')
- deps.append(f'enable_rdma: {nfs_spec.enable_rdma}')
- deps.append(f'rdma_port: {nfs_spec.rdma_port}')
- return sorted(deps)
+ parent_deps = super().get_dependencies(mgr, spec, daemon_type)
+ return sorted(deps + parent_deps)
def prepare_create(self, daemon_spec: CephadmDaemonDeploySpec) -> CephadmDaemonDeploySpec:
assert self.TYPE == daemon_spec.daemon_type
for service in ['mgmt-gateway']
for d in mgr.cache.get_daemons_by_service(service)
]
- return deps
+ parent_deps = super().get_dependencies(mgr, spec, daemon_type)
+ return sorted(deps + parent_deps)
def get_service_ips_and_hosts(self, service_name: str) -> List[str]:
entries = set()