mirror of
https://github.com/ceph/ceph
synced 2026-08-01 22:45:39 +00:00
Merge PR #70675 into tentacle
* refs/pull/70675/head: mgr/nfs: Updated enable_nfsv3 check while export creation mgr/nfs: set nfs export protocols based on cluster's protocol settings mgr/cephadm: Updated NFS default protocol to v4 and v3 is enabled only when enable_nfsv3 is set in the spec Reviewed-by: Adam King <adking@redhat.com>
This commit is contained in:
commit
36ad54815f
@ -49,9 +49,14 @@ Alternatively, an NFS service can be applied using a YAML specification.
|
||||
- host2
|
||||
spec:
|
||||
port: 12345
|
||||
monitoring_port: 567
|
||||
enable_nfsv3: true
|
||||
|
||||
|
||||
In this example, we run the server on the non-default ``port`` of
|
||||
12345 (instead of the default 2049) on ``host1`` and ``host2``.
|
||||
By default, only the NFSv4 protocol is enabled. NFSv3 can be enabled by setting
|
||||
``enable_nfsv3`` to ``true`` in the service specification.
|
||||
|
||||
The specification can then be applied by running the following command:
|
||||
|
||||
|
||||
@ -31,7 +31,7 @@ Create NFS Ganesha Cluster
|
||||
|
||||
.. prompt:: bash #
|
||||
|
||||
ceph nfs cluster create <cluster_id> [<placement>] [--ingress] [--virtual_ip <value>] [--ingress-mode {default|keepalive-only|haproxy-standard|haproxy-protocol}] [--port <int>]
|
||||
ceph nfs cluster create <cluster_id> [<placement>] [--ingress] [--virtual_ip <value>] [--ingress-mode {default|keepalive-only|haproxy-standard|haproxy-protocol}] [--port <int>] [--enable-nfsv3]
|
||||
|
||||
This creates a common recovery pool for all NFS Ganesha daemons, new user based on
|
||||
``cluster_id``, and a common NFS Ganesha config RADOS object.
|
||||
@ -62,6 +62,9 @@ cluster)::
|
||||
|
||||
NFS can be deployed on a port other than 2049 (the default) with ``--port <port>``.
|
||||
|
||||
By default, only NFS v4 protocol is enabled. To enable both NFS v3 and v4 protocols,
|
||||
add the ``--enable-nfsv3`` flag.
|
||||
|
||||
To deploy NFS with a high-availability front-end (virtual IP and load balancer), add the
|
||||
``--ingress`` flag and specify a virtual IP address. This will deploy a combination
|
||||
of keepalived and haproxy to provide an high-availability NFS frontend for the NFS
|
||||
|
||||
@ -129,6 +129,7 @@ class NFSService(CephService):
|
||||
"nfs_idmap_conf": nfs_idmap_conf,
|
||||
"enable_nlm": str(spec.enable_nlm).lower(),
|
||||
"cluster_id": self.mgr._cluster_fsid,
|
||||
"protocols": "3, 4" if spec.enable_nfsv3 else "4"
|
||||
}
|
||||
if spec.enable_haproxy_protocol:
|
||||
context["haproxy_hosts"] = self._haproxy_hosts()
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
NFS_CORE_PARAM {
|
||||
Enable_NLM = {{ enable_nlm }};
|
||||
Enable_RQUOTA = false;
|
||||
Protocols = 3, 4;
|
||||
Protocols = {{ protocols }};
|
||||
mount_path_pseudo = true;
|
||||
Enable_UDP = false;
|
||||
NFS_Port = {{ port }};
|
||||
|
||||
@ -3391,7 +3391,7 @@ class TestIngressService:
|
||||
'NFS_CORE_PARAM {\n'
|
||||
' Enable_NLM = true;\n'
|
||||
' Enable_RQUOTA = false;\n'
|
||||
' Protocols = 3, 4;\n'
|
||||
' Protocols = 4;\n'
|
||||
' mount_path_pseudo = true;\n'
|
||||
' Enable_UDP = false;\n'
|
||||
' NFS_Port = 2049;\n'
|
||||
@ -3518,6 +3518,32 @@ class TestIngressService:
|
||||
)
|
||||
assert nfs_generated_conf == nfs_expected_conf
|
||||
|
||||
@patch("cephadm.serve.CephadmServe._run_cephadm")
|
||||
@patch("cephadm.services.nfs.NFSService.fence_old_ranks", MagicMock())
|
||||
@patch("cephadm.services.nfs.NFSService.run_grace_tool", MagicMock())
|
||||
@patch("cephadm.services.nfs.NFSService.purge", MagicMock())
|
||||
@patch("cephadm.services.nfs.NFSService.create_rados_config_obj", MagicMock())
|
||||
def test_nfs_enable_nfsv3(self, _run_cephadm, cephadm_module: CephadmOrchestrator):
|
||||
_run_cephadm.side_effect = async_side_effect(('{}', '', 0))
|
||||
|
||||
with with_host(cephadm_module, 'test'):
|
||||
# Test with enable_nfsv3=False (default)
|
||||
nfs_spec = NFSServiceSpec(service_id="foo", placement=PlacementSpec(hosts=['test']))
|
||||
with with_service(cephadm_module, nfs_spec) as _:
|
||||
nfs_generated_conf, _ = service_registry.get_service('nfs').generate_config(
|
||||
CephadmDaemonDeploySpec(host='test', daemon_id='foo.test.0.0', service_name=nfs_spec.service_name()))
|
||||
ganesha_conf = nfs_generated_conf['files']['ganesha.conf']
|
||||
assert "Protocols = 4;" in ganesha_conf
|
||||
|
||||
# Test with enable_nfsv3=True
|
||||
nfs_spec = NFSServiceSpec(service_id="foo", placement=PlacementSpec(hosts=['test']),
|
||||
enable_nfsv3=True)
|
||||
with with_service(cephadm_module, nfs_spec) as _:
|
||||
nfs_generated_conf, _ = service_registry.get_service('nfs').generate_config(
|
||||
CephadmDaemonDeploySpec(host='test', daemon_id='foo.test.0.0', service_name=nfs_spec.service_name()))
|
||||
ganesha_conf = nfs_generated_conf['files']['ganesha.conf']
|
||||
assert "Protocols = 3, 4;" in ganesha_conf
|
||||
|
||||
|
||||
class TestCephFsMirror:
|
||||
@patch("cephadm.serve.CephadmServe._run_cephadm")
|
||||
|
||||
@ -65,6 +65,7 @@ class NFSCluster:
|
||||
virtual_ip: Optional[str] = None,
|
||||
ingress_mode: Optional[IngressType] = None,
|
||||
port: Optional[int] = None,
|
||||
enable_nfsv3: bool = False,
|
||||
) -> None:
|
||||
if not port:
|
||||
port = 2049 # default nfs port
|
||||
@ -98,7 +99,8 @@ class NFSCluster:
|
||||
# use non-default port so we don't conflict with ingress
|
||||
port=ganesha_port,
|
||||
virtual_ip=virtual_ip_for_ganesha,
|
||||
enable_haproxy_protocol=enable_haproxy_protocol)
|
||||
enable_haproxy_protocol=enable_haproxy_protocol,
|
||||
enable_nfsv3=enable_nfsv3)
|
||||
completion = self.mgr.apply_nfs(spec)
|
||||
orchestrator.raise_if_exception(completion)
|
||||
ispec = IngressSpec(service_type='ingress',
|
||||
@ -116,7 +118,8 @@ class NFSCluster:
|
||||
# standalone nfs
|
||||
spec = NFSServiceSpec(service_type='nfs', service_id=cluster_id,
|
||||
placement=PlacementSpec.from_string(placement),
|
||||
port=port)
|
||||
port=port,
|
||||
enable_nfsv3=enable_nfsv3)
|
||||
completion = self.mgr.apply_nfs(spec)
|
||||
orchestrator.raise_if_exception(completion)
|
||||
log.debug("Successfully deployed nfs daemons with cluster id %s and placement %s",
|
||||
@ -140,6 +143,7 @@ class NFSCluster:
|
||||
ingress: Optional[bool] = None,
|
||||
ingress_mode: Optional[IngressType] = None,
|
||||
port: Optional[int] = None,
|
||||
enable_nfsv3: bool = False,
|
||||
) -> None:
|
||||
try:
|
||||
if virtual_ip:
|
||||
@ -163,7 +167,14 @@ class NFSCluster:
|
||||
self.create_empty_rados_obj(cluster_id)
|
||||
|
||||
if cluster_id not in available_clusters(self.mgr):
|
||||
self._call_orch_apply_nfs(cluster_id, placement, virtual_ip, ingress_mode, port)
|
||||
self._call_orch_apply_nfs(
|
||||
cluster_id,
|
||||
placement,
|
||||
virtual_ip,
|
||||
ingress_mode,
|
||||
port,
|
||||
enable_nfsv3=enable_nfsv3,
|
||||
)
|
||||
return
|
||||
raise NonFatalError(f"{cluster_id} cluster already exists")
|
||||
except Exception as e:
|
||||
|
||||
@ -227,6 +227,25 @@ class ExportMgr:
|
||||
self.rados_pool = POOL_NAME
|
||||
self._exports: Optional[Dict[str, List[Export]]] = export_ls
|
||||
|
||||
def _get_cluster_protocols(self, cluster_id: str) -> List[int]:
|
||||
"""Get the list of supported NFS protocols for a cluster.
|
||||
"""
|
||||
try:
|
||||
import orchestrator
|
||||
from ceph.deployment.service_spec import NFSServiceSpec
|
||||
|
||||
completion = self.mgr.describe_service(service_type='nfs', service_name=f'nfs.{cluster_id}')
|
||||
services = orchestrator.raise_if_exception(completion)
|
||||
for service in services:
|
||||
if service.spec:
|
||||
spec = cast(NFSServiceSpec, service.spec)
|
||||
if getattr(spec, 'enable_nfsv3', False):
|
||||
return [3, 4]
|
||||
return [4]
|
||||
except Exception as e:
|
||||
log.debug(f"Failed to get cluster protocols for {cluster_id}: {e}, defaulting to v4 only")
|
||||
return [4]
|
||||
|
||||
@property
|
||||
def exports(self) -> Dict[str, List[Export]]:
|
||||
if self._exports is None:
|
||||
@ -749,6 +768,8 @@ class ExportMgr:
|
||||
_validate_cmount_path(cmount_path, path) # type: ignore
|
||||
|
||||
pseudo_path = normalize_path(pseudo_path)
|
||||
# Get the protocols based on cluster's enable_nfsv3 setting
|
||||
protocols = self._get_cluster_protocols(cluster_id)
|
||||
|
||||
if not self._fetch_export(cluster_id, pseudo_path):
|
||||
export = self.create_export_from_dict(
|
||||
@ -766,6 +787,7 @@ class ExportMgr:
|
||||
},
|
||||
"clients": clients,
|
||||
"sectype": sectype,
|
||||
"protocols": protocols,
|
||||
},
|
||||
earmark_resolver
|
||||
)
|
||||
@ -797,6 +819,9 @@ class ExportMgr:
|
||||
if not bucket and not user_id:
|
||||
raise ErrorResponse("Must specify either bucket or user_id")
|
||||
|
||||
# Get the protocols based on cluster's enable_nfsv3 setting
|
||||
protocols = self._get_cluster_protocols(cluster_id)
|
||||
|
||||
if not self._fetch_export(cluster_id, pseudo_path):
|
||||
export = self.create_export_from_dict(
|
||||
cluster_id,
|
||||
@ -812,6 +837,7 @@ class ExportMgr:
|
||||
},
|
||||
"clients": clients,
|
||||
"sectype": sectype,
|
||||
"protocols": protocols,
|
||||
}
|
||||
)
|
||||
log.debug("creating rgw export %s", export)
|
||||
@ -868,6 +894,10 @@ class ExportMgr:
|
||||
else:
|
||||
new_export_dict['fsal']['cmount_path'] = '/'
|
||||
|
||||
# Set protocols based on cluster's enable_nfsv3 setting if not explicitly specified
|
||||
if 'protocols' not in new_export_dict:
|
||||
new_export_dict['protocols'] = self._get_cluster_protocols(cluster_id)
|
||||
|
||||
new_export = self.create_export_from_dict(
|
||||
cluster_id,
|
||||
new_export_dict.get('export_id', self._gen_export_id(cluster_id)),
|
||||
|
||||
@ -464,7 +464,7 @@ class Export:
|
||||
ex_dict.get('access_type', 'RO'),
|
||||
ex_dict.get('squash', 'no_root_squash'),
|
||||
ex_dict.get('security_label', True),
|
||||
ex_dict.get('protocols', [3, 4]),
|
||||
ex_dict.get('protocols', [4]),
|
||||
ex_dict.get('transports', ['TCP']),
|
||||
FSAL.from_dict(ex_dict.get('fsal', {})),
|
||||
[Client.from_dict(client) for client in ex_dict.get('clients', [])],
|
||||
|
||||
@ -133,11 +133,13 @@ class Module(orchestrator.OrchestratorClientMixin, MgrModule):
|
||||
ingress: Optional[bool] = None,
|
||||
virtual_ip: Optional[str] = None,
|
||||
ingress_mode: Optional[IngressType] = None,
|
||||
port: Optional[int] = None) -> None:
|
||||
port: Optional[int] = None,
|
||||
enable_nfsv3: bool = False) -> None:
|
||||
"""Create an NFS Cluster"""
|
||||
return self.nfs.create_nfs_cluster(cluster_id=cluster_id, placement=placement,
|
||||
virtual_ip=virtual_ip, ingress=ingress,
|
||||
ingress_mode=ingress_mode, port=port)
|
||||
ingress_mode=ingress_mode, port=port,
|
||||
enable_nfsv3=enable_nfsv3)
|
||||
|
||||
@NFSCLICommand('nfs cluster rm', perm='rw')
|
||||
@object_format.EmptyResponder()
|
||||
|
||||
@ -116,7 +116,7 @@ EXPORT {
|
||||
Path = /;
|
||||
Pseudo = /cephfs_b/;
|
||||
Access_Type = RW;
|
||||
Protocols = 4;
|
||||
Protocols = 3, 4;
|
||||
Attr_Expiration_Time = 0;
|
||||
|
||||
FSAL {
|
||||
@ -406,7 +406,7 @@ NFS_CORE_PARAM {
|
||||
assert export.pseudo == "/cephfs_b/"
|
||||
assert export.access_type == "RW"
|
||||
assert export.squash == "no_root_squash"
|
||||
assert export.protocols == [4]
|
||||
assert export.protocols == [3, 4]
|
||||
assert export.fsal.name == "CEPH"
|
||||
assert export.fsal.user_id == "nfs.foo.b.lgudhr"
|
||||
assert export.fsal.fs_name == "b"
|
||||
@ -1017,7 +1017,7 @@ NFS_CORE_PARAM {
|
||||
assert export.pseudo == "/mybucket"
|
||||
assert export.access_type == "none"
|
||||
assert export.squash == "none"
|
||||
assert export.protocols == [3, 4]
|
||||
assert export.protocols == [4]
|
||||
assert export.transports == ["TCP"]
|
||||
assert export.fsal.name == "RGW"
|
||||
assert export.fsal.user_id == "bucket_owner_user"
|
||||
@ -1060,7 +1060,7 @@ NFS_CORE_PARAM {
|
||||
assert export.pseudo == "/mybucket"
|
||||
assert export.access_type == "none"
|
||||
assert export.squash == "none"
|
||||
assert export.protocols == [3, 4]
|
||||
assert export.protocols == [4]
|
||||
assert export.transports == ["TCP"]
|
||||
assert export.fsal.name == "RGW"
|
||||
assert export.fsal.access_key_id == "the_access_key"
|
||||
@ -1102,7 +1102,7 @@ NFS_CORE_PARAM {
|
||||
assert export.pseudo == "/mybucket"
|
||||
assert export.access_type == "none"
|
||||
assert export.squash == "none"
|
||||
assert export.protocols == [3, 4]
|
||||
assert export.protocols == [4]
|
||||
assert export.transports == ["TCP"]
|
||||
assert export.fsal.name == "RGW"
|
||||
assert export.fsal.access_key_id == "the_access_key"
|
||||
@ -1151,7 +1151,7 @@ NFS_CORE_PARAM {
|
||||
assert export.pseudo == "/cephfs2"
|
||||
assert export.access_type == "none"
|
||||
assert export.squash == "none"
|
||||
assert export.protocols == [3, 4]
|
||||
assert export.protocols == [4]
|
||||
assert export.transports == ["TCP"]
|
||||
assert export.fsal.name == "CEPH"
|
||||
assert export.fsal.user_id == "nfs.foo.myfs.86ca58ef"
|
||||
@ -1190,7 +1190,7 @@ NFS_CORE_PARAM {
|
||||
assert export.pseudo == "/cephfs3"
|
||||
assert export.access_type == "RW"
|
||||
assert export.squash == "root"
|
||||
assert export.protocols == [3, 4]
|
||||
assert export.protocols == [4]
|
||||
assert export.fsal.name == "CEPH"
|
||||
assert export.fsal.user_id == "nfs.foo.myfs.86ca58ef"
|
||||
assert export.fsal.cephx_key == "thekeyforclientabc"
|
||||
|
||||
@ -1185,6 +1185,7 @@ class NFSServiceSpec(ServiceSpec):
|
||||
extra_entrypoint_args: Optional[GeneralArgList] = None,
|
||||
idmap_conf: Optional[Dict[str, Dict[str, str]]] = None,
|
||||
custom_configs: Optional[List[CustomConfig]] = None,
|
||||
enable_nfsv3: bool = False,
|
||||
):
|
||||
assert service_type == 'nfs'
|
||||
super(NFSServiceSpec, self).__init__(
|
||||
@ -1199,6 +1200,7 @@ class NFSServiceSpec(ServiceSpec):
|
||||
self.enable_haproxy_protocol = enable_haproxy_protocol
|
||||
self.idmap_conf = idmap_conf
|
||||
self.enable_nlm = enable_nlm
|
||||
self.enable_nfsv3 = enable_nfsv3
|
||||
|
||||
def get_port_start(self) -> List[int]:
|
||||
if self.port:
|
||||
|
||||
Loading…
Reference in New Issue
Block a user