.. prompt:: bash #
- ceph nfs cluster create <cluster_id> [<placement>] [--ingress] [--virtual_ip <value>] [--ingress-mode {default|keepalive-only|haproxy-standard|haproxy-protocol}] [--port <int>] [--enable-rdma] [--rdma_port <int>] [-i <spec_file>] [--enable-nfsv3]
+ ceph nfs cluster create <cluster_id> [<placement>] [--ingress] [--virtual_ip <value>] [--ingress-mode {default|keepalive-only|haproxy-standard|haproxy-protocol}] [--ingress-placement <placement>] [--port <int>] [--enable-rdma] [--rdma_port <int>] [--enable-nfsv3] [-i <spec_file>]
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.
appropriate backend NFS servers, and redeploying NFS servers when they
fail.
+By default, the *ingress* service follows the same placement as the NFS
+Ganesha daemons (the optional ``<placement>`` argument). To schedule
+keepalived and HAProxy on a different set of hosts, pass
+``--ingress-placement`` with a separate placement string. For example,
+to run three NFS daemons on ``host1`` and ``host2`` while colocating
+ingress on three dedicated nodes::
+
+ ceph nfs cluster create mynfs "2 host1 host2" --ingress --virtual_ip 192.168.1.100/24 --ingress-placement "3 host3 host4 host5"
+
+If ``--ingress-placement`` is omitted, both services share the NFS
+placement.
+
An optional ``--ingress-mode`` parameter can be provided to choose
how the *ingress* service is configured:
tls_ciphers: Optional[str] = None,
enable_rdma: bool = False,
rdma_port: Optional[int] = None,
+ ingress_placement: Optional[str] = None,
) -> None:
if not port:
port = 2049 # default nfs port
ingress_mode = IngressType.default
ingress_mode = ingress_mode.canonicalize()
pspec = PlacementSpec.from_string(placement)
+ ingress_pspec = PlacementSpec.from_string(ingress_placement) if ingress_placement else None
if ingress_mode == IngressType.keepalive_only:
# enforce count=1 for nfs over keepalive only
pspec.count = 1
+ if ingress_pspec:
+ ingress_pspec.count = 1
ganesha_port = 10000 + port # semi-arbitrary, fix me someday
frontend_port: Optional[int] = port
ispec = IngressSpec(service_type='ingress',
service_id='nfs.' + cluster_id,
backend_service='nfs.' + cluster_id,
- placement=pspec,
+ placement=ingress_pspec or pspec,
frontend_port=frontend_port,
monitor_port=7000 + port, # semi-arbitrary, fix me someday
virtual_ip=virtual_ip,
tls_ciphers: Optional[str] = None,
enable_rdma: bool = False,
rdma_port: Optional[int] = None,
+ ingress_placement: Optional[str] = None,
) -> None:
try:
if virtual_ip:
tls_min_version=tls_min_version,
tls_ciphers=tls_ciphers,
enable_rdma=enable_rdma,
- rdma_port=rdma_port
+ rdma_port=rdma_port,
+ ingress_placement=ingress_placement
)
return
raise NonFatalError(f"{cluster_id} cluster already exists")
enable_rdma: bool = False,
rdma_port: Optional[int] = None,
enable_nfsv3: bool = False,
+ ingress_placement: Optional[str] = None,
inbuf: Optional[str] = None) -> None:
"""Create an NFS Cluster"""
cluster_qos_config = None
tls_min_version=tls_min_version,
tls_ciphers=tls_ciphers,
enable_rdma=enable_rdma,
- rdma_port=rdma_port)
+ rdma_port=rdma_port,
+ ingress_placement=ingress_placement)
@NFSCLICommand('nfs cluster rm', perm='rw')
@object_format.EmptyResponder()
from rados import ObjectNotFound
-from ceph.deployment.service_spec import NFSServiceSpec
+from ceph.deployment.service_spec import NFSServiceSpec, PlacementSpec
from ceph.utils import with_units_to_int, bytes_to_human
from nfs import Module
from nfs.export import ExportMgr, normalize_path
self._do_mock_test(self._do_test_export_qos_bw_ops, qos_type, clust_bw_params, clust_ops_params, export_bw_params, export_ops_params)
+class TestNFSClusterIngressPlacement:
+ cluster_id = 'mynfs'
+ virtual_ip = '192.168.1.100/24'
+ nfs_placement = '2 host1 host2'
+ ingress_placement = '3 host3 host4 host5'
+
+ def test_create_nfs_cluster_passes_ingress_placement(self):
+ mgr = MagicMock()
+ cluster = NFSCluster(mgr)
+ with mock.patch('nfs.cluster.create_ganesha_pool'), \
+ mock.patch.object(cluster, 'create_empty_rados_obj'), \
+ mock.patch('nfs.cluster.available_clusters', return_value=[]), \
+ mock.patch.object(cluster, '_call_orch_apply_nfs') as mock_apply:
+ cluster.create_nfs_cluster(
+ cluster_id=self.cluster_id,
+ placement='host1',
+ virtual_ip=self.virtual_ip,
+ ingress=True,
+ ingress_placement=self.ingress_placement,
+ )
+ mock_apply.assert_called_once()
+ assert mock_apply.call_args.kwargs['ingress_placement'] == self.ingress_placement
+
+
@pytest.mark.parametrize(
"path,expected",
[