Skip to content

Commit b862e39

Browse files
mgr/nfs: Add Spec parameter for ganesha.conf Bind_addr
Fixes: https://tracker.ceph.com/issues/71031 Signed-off-by: Shweta Bhosale <[email protected]>
1 parent 70afb7b commit b862e39

File tree

7 files changed

+299
-60
lines changed

7 files changed

+299
-60
lines changed

doc/cephadm/services/nfs.rst

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,11 @@ Alternatively, an NFS service can be applied using a YAML specification.
4747
hosts:
4848
- host1
4949
- host2
50+
networks:
51+
- 1.2.3.4/24
52+
ip_addrs:
53+
host1: 10.0.0.100
54+
host2: 10.0.0.101
5055
spec:
5156
port: 12345
5257
monitoring_port: 567
@@ -59,7 +64,13 @@ Alternatively, an NFS service can be applied using a YAML specification.
5964
6065
In this example, we run the server on the non-default ``port`` of
6166
12345 (instead of the default 2049) on ``host1`` and ``host2``.
62-
The default monitoring port can be customized using the ``monitoring_port``
67+
You can bind the NFS data port to a specific IP address using either the
68+
``ip_addrs`` or ``networks`` section. If ``ip_addrs`` is provided and
69+
the specified IP is assigned to the host, that IP will be used. If the
70+
IP is not present but ``networks`` is specified, an IP matching one of
71+
the given networks will be selected. If neither condition is met, the
72+
daemon will not start on that node.
73+
The default NFS monitoring port can be customized using the ``monitoring_port``
6374
parameter. Additionally, you can specify the ``monitoring_ip_addrs`` or
6475
``monitoring_networks`` parameters to bind the monitoring port to a specific
6576
IP address or network. If ``monitoring_ip_addrs`` is provided and the specified

src/pybind/mgr/cephadm/schedule.py

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -460,18 +460,34 @@ def get_candidates(self) -> List[DaemonPlacement]:
460460
"placement spec is empty: no hosts, no label, no pattern, no count")
461461

462462
# allocate an IP?
463-
if self.spec.networks:
463+
if self.spec.networks or self.spec.ip_addrs:
464464
orig = ls.copy()
465465
ls = []
466466
for p in orig:
467-
ip = self.find_ip_on_host(p.hostname, self.spec.networks)
467+
ip = None
468+
# daemon can have specific ip if 'ip_addrs' is spcified in spec, we can use this
469+
# parameter for all services, if they need to bind to specific ip
470+
# If ip not present and networks is passed, ip of that network will be used
471+
if self.spec.ip_addrs:
472+
ip = self.spec.ip_addrs.get(p.hostname)
473+
host_ips: List[str] = []
474+
for net_details in self.networks.get(p.hostname, {}).values():
475+
for ips in net_details.values():
476+
host_ips.extend(ips)
477+
if ip and ip not in host_ips:
478+
logger.debug(f"IP {ip} is not configured on host {p.hostname}.")
479+
ip = None
480+
if not ip and self.spec.networks:
481+
ip = self.find_ip_on_host(p.hostname, self.spec.networks)
468482
if ip:
469483
ls.append(DaemonPlacement(daemon_type=self.primary_daemon_type,
470484
hostname=p.hostname, network=p.network,
471485
name=p.name, ports=p.ports, ip=ip))
472486
else:
473487
logger.debug(
474-
f'Skipping {p.hostname} with no IP in network(s) {self.spec.networks}'
488+
f"Skipping {p.hostname} with no IP in provided networks or ip_addrs "
489+
f"{f'networks: {self.spec.networks}' if self.spec.networks else ''}"
490+
f"{f'ip_addrs: {self.spec.ip_addrs}' if self.spec.ip_addrs else ''}"
475491
)
476492

477493
if self.filter_new_host:

src/pybind/mgr/cephadm/service_discovery.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ class Server: # type: ignore
2222
from cephadm.services.ingress import IngressSpec
2323
from cephadm.services.cephadmservice import CephExporterService
2424
from cephadm.services.nvmeof import NvmeofService
25+
from cephadm.services.service_registry import service_registry
2526

2627
if TYPE_CHECKING:
2728
from cephadm.module import CephadmOrchestrator
@@ -263,8 +264,10 @@ def nfs_sd_config(self) -> List[Dict[str, Collection[str]]]:
263264
srv_entries = []
264265
for dd in self.mgr.cache.get_daemons_by_type('nfs'):
265266
assert dd.hostname is not None
266-
addr = dd.ip if dd.ip else self.mgr.inventory.get_addr(dd.hostname)
267-
port = NFSService.DEFAULT_EXPORTER_PORT
267+
nfs = cast(NFSService, service_registry.get_service('nfs'))
268+
monitoring_ip, monitoring_port = nfs.get_monitoring_details(dd.service_name(), dd.hostname)
269+
addr = monitoring_ip or dd.ip or self.mgr.inventory.get_addr(dd.hostname)
270+
port = monitoring_port or NFSService.DEFAULT_EXPORTER_PORT
268271
srv_entries.append({
269272
'targets': [build_url(host=addr, port=port).lstrip('/')],
270273
'labels': {'instance': dd.hostname}

src/pybind/mgr/cephadm/services/nfs.py

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -98,32 +98,28 @@ def generate_config(self, daemon_spec: CephadmDaemonDeploySpec) -> Tuple[Dict[st
9898
self.create_rados_config_obj(spec)
9999

100100
port = daemon_spec.ports[0] if daemon_spec.ports else 2049
101-
monitoring_port = spec.monitoring_port if spec.monitoring_port else 9587
101+
monitoring_ip, monitoring_port = self.get_monitoring_details(daemon_spec.service_name, host)
102102

103103
# create the RGW keyring
104104
rgw_user = f'{rados_user}-rgw'
105105
rgw_keyring = self.create_rgw_keyring(daemon_spec)
106+
bind_addr = ''
106107
if spec.virtual_ip and not spec.enable_haproxy_protocol:
107108
bind_addr = spec.virtual_ip
108109
daemon_spec.port_ips = {str(port): spec.virtual_ip}
109-
else:
110-
bind_addr = daemon_spec.ip if daemon_spec.ip else ''
110+
# update daemon spec ip for prometheus, as monitoring will happen on this
111+
# ip, if no monitor ip specified
112+
daemon_spec.ip = bind_addr
113+
elif daemon_spec.ip:
114+
bind_addr = daemon_spec.ip
115+
daemon_spec.port_ips = {str(port): daemon_spec.ip}
111116
if not bind_addr:
112117
logger.warning(f'Bind address in {daemon_type}.{daemon_id}\'s ganesha conf is defaulting to empty')
113118
else:
114119
logger.debug("using haproxy bind address: %r", bind_addr)
115120

116-
# check if monitor needs to be bind on specific ip
117-
monitoring_addr = spec.monitoring_ip_addrs.get(host) if spec.monitoring_ip_addrs else None
118-
if monitoring_addr and monitoring_addr not in self.mgr.cache.get_host_network_ips(host):
119-
logger.debug(f"Monitoring IP {monitoring_addr} is not configured on host {daemon_spec.host}.")
120-
monitoring_addr = None
121-
if not monitoring_addr and spec.monitoring_networks:
122-
monitoring_addr = self.mgr.get_first_matching_network_ip(daemon_spec.host, spec, spec.monitoring_networks)
123-
if not monitoring_addr:
124-
logger.debug(f"No IP address found in the network {spec.monitoring_networks} on host {daemon_spec.host}.")
125-
if monitoring_addr:
126-
daemon_spec.port_ips.update({str(monitoring_port): monitoring_addr})
121+
if monitoring_ip:
122+
daemon_spec.port_ips.update({str(monitoring_port): monitoring_ip})
127123

128124
# generate the ganesha config
129125
def get_ganesha_conf() -> str:
@@ -136,7 +132,7 @@ def get_ganesha_conf() -> str:
136132
"url": f'rados://{POOL_NAME}/{spec.service_id}/{spec.rados_config_name()}',
137133
# fall back to default NFS port if not present in daemon_spec
138134
"port": port,
139-
"monitoring_addr": monitoring_addr,
135+
"monitoring_addr": monitoring_ip,
140136
"monitoring_port": monitoring_port,
141137
"bind_addr": bind_addr,
142138
"haproxy_hosts": [],
@@ -386,3 +382,18 @@ def _haproxy_hosts(self) -> List[str]:
386382
# one address per interface/subnet is enough
387383
cluster_ips.append(addrs[0])
388384
return cluster_ips
385+
386+
def get_monitoring_details(self, service_name: str, host: str) -> Tuple[Optional[str], Optional[int]]:
387+
spec = cast(NFSServiceSpec, self.mgr.spec_store[service_name].spec)
388+
monitoring_port = spec.monitoring_port if spec.monitoring_port else 9587
389+
390+
# check if monitor needs to be bind on specific ip
391+
monitoring_addr = spec.monitoring_ip_addrs.get(host) if spec.monitoring_ip_addrs else None
392+
if monitoring_addr and monitoring_addr not in self.mgr.cache.get_host_network_ips(host):
393+
logger.debug(f"Monitoring IP {monitoring_addr} is not configured on host {host}.")
394+
monitoring_addr = None
395+
if not monitoring_addr and spec.monitoring_networks:
396+
monitoring_addr = self.mgr.get_first_matching_network_ip(host, spec, spec.monitoring_networks)
397+
if not monitoring_addr:
398+
logger.debug(f"No IP address found in the network {spec.monitoring_networks} on host {host}.")
399+
return monitoring_addr, monitoring_port

src/pybind/mgr/cephadm/tests/test_service_discovery.py

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from unittest.mock import MagicMock
22
from cephadm.service_discovery import Root
3+
from cephadm.services.service_registry import service_registry
34

45

56
class FakeDaemonDescription:
@@ -39,35 +40,56 @@ def get_daemons_by_service(self, service_type):
3940
FakeDaemonDescription('1.2.3.5', [9200], 'node1')]
4041

4142
def get_daemons_by_type(self, daemon_type):
42-
return [FakeDaemonDescription('1.2.3.4', [9100], 'node0', 'ingress', 'haproxy'),
43-
FakeDaemonDescription('1.2.3.5', [9200], 'node1', 'ingress', 'haproxy')]
43+
if daemon_type == 'ingress':
44+
return [FakeDaemonDescription('1.2.3.4', [9100], 'node0', 'ingress', 'haproxy'),
45+
FakeDaemonDescription('1.2.3.5', [9200], 'node1', 'ingress', 'haproxy')]
46+
else:
47+
return [FakeDaemonDescription('1.2.3.4', [1234], 'node0', daemon_type, daemon_type),
48+
FakeDaemonDescription('1.2.3.5', [1234], 'node1', daemon_type, daemon_type)]
4449

4550

4651
class FakeInventory:
4752
def get_addr(self, name: str):
4853
return '1.2.3.4'
4954

5055

56+
class FakeNFSServiceSpec:
57+
def __init__(self, port):
58+
self.monitoring_port = None
59+
self.monitoring_ip_addrs = None
60+
self.monitoring_networks = None
61+
62+
63+
class FakeIngressServiceSpec:
64+
def __init__(self, port):
65+
self.monitor_port = port
66+
67+
5168
class FakeServiceSpec:
5269
def __init__(self, port):
5370
self.monitor_port = port
5471

5572

5673
class FakeSpecDescription:
57-
def __init__(self, port):
58-
self.spec = FakeServiceSpec(port)
74+
def __init__(self, service, port):
75+
if service == 'ingress':
76+
self.spec = FakeIngressServiceSpec(port)
77+
elif service == 'nfs':
78+
self.spec = FakeNFSServiceSpec(port)
79+
else:
80+
self.spec = FakeServiceSpec(port)
5981

6082

6183
class FakeSpecStore():
6284
def __init__(self, mgr):
6385
self.mgr = mgr
64-
self._specs = {'ingress': FakeSpecDescription(9049)}
86+
self._specs = {'ingress': FakeSpecDescription('ingress', 9049), 'nfs': FakeSpecDescription('nfs', 9587)}
6587

6688
def __contains__(self, name):
6789
return name in self._specs
6890

6991
def __getitem__(self, name):
70-
return self._specs['ingress']
92+
return self._specs[name]
7193

7294

7395
class FakeMgr:
@@ -80,6 +102,7 @@ def __init__(self):
80102
self.inventory = FakeInventory()
81103
self.cache = FakeCache()
82104
self.spec_store = FakeSpecStore(self)
105+
service_registry.init_services(self)
83106

84107
def get_mgr_id(self):
85108
return 'mgr-1'

0 commit comments

Comments
 (0)