Skip to content

Commit f597caa

Browse files
authored
Merge pull request ceph#59419 from phlogistonjohn/jjm-smb-ctdb-vips
smb: cluster public ip addresses support Reviewed-by: Adam King <[email protected]> Reviewed-by: Anoop C S <[email protected]> Reviewed-by: Avan Thakkar <[email protected]> Reviewed-by: Michael Adam <[email protected]>
2 parents 02fe44a + dc09d17 commit f597caa

File tree

9 files changed

+456
-9
lines changed

9 files changed

+456
-9
lines changed

doc/cephadm/services/smb.rst

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -105,21 +105,43 @@ custom_dns
105105
Active Directory even if the Ceph host nodes are not tied into the Active
106106
Directory DNS domain(s).
107107

108-
include_ceph_users:
108+
include_ceph_users
109109
A list of cephx user (aka entity) names that the Samba Containers may use.
110110
The cephx keys for each user in the list will automatically be added to
111111
the keyring in the container.
112112

113-
cluster_meta_uri:
113+
cluster_meta_uri
114114
A string containing a URI that identifies where the cluster structure
115115
metadata will be stored. Required if ``clustered`` feature is set. Must be
116116
a RADOS pseudo-URI.
117117

118-
cluster_lock_uri:
118+
cluster_lock_uri
119119
A string containing a URI that identifies where Samba/CTDB will store a
120120
cluster lock. Required if ``clustered`` feature is set. Must be a RADOS
121121
pseudo-URI.
122122

123+
cluster_public_addrs
124+
List of objects; optional. Supported only when using Samba's clustering.
125+
Assign "virtual" IP addresses that will be managed by the clustering
126+
subsystem and may automatically move between nodes running Samba
127+
containers.
128+
Fields:
129+
130+
address
131+
Required string. An IP address with a required prefix length (example:
132+
``192.168.4.51/24``). This address will be assigned to one of the
133+
host's network devices and managed automatically.
134+
destination
135+
Optional. String or list of strings. A ``destination`` defines where
136+
the system will assign the managed IPs. Each string value must be a
137+
network address (example ``192.168.4.0/24``). One or more destinations
138+
may be supplied. The typical case is to use exactly one destination and
139+
so the value may be supplied as a string, rather than a list with a
140+
single item. Each destination network will be mapped to a device on a
141+
host. Run ``cephadm list-networks`` for an example of these mappings.
142+
If destination is not supplied the network is automatically determined
143+
using the address value supplied and taken as the destination.
144+
123145

124146
.. note::
125147

doc/mgr/smb.rst

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,27 @@ clustering
376376
enables clustering regardless of the placement count. A value of ``never``
377377
disables clustering regardless of the placement count. If unspecified,
378378
``default`` is assumed.
379+
public_addrs
380+
List of objects; optional. Supported only when using Samba's clustering.
381+
Assign "virtual" IP addresses that will be managed by the clustering
382+
subsystem and may automatically move between nodes running Samba
383+
containers.
384+
Fields:
385+
386+
address
387+
Required string. An IP address with a required prefix length (example:
388+
``192.168.4.51/24``). This address will be assigned to one of the
389+
host's network devices and managed automatically.
390+
destination
391+
Optional. String or list of strings. A ``destination`` defines where
392+
the system will assign the managed IPs. Each string value must be a
393+
network address (example ``192.168.4.0/24``). One or more destinations
394+
may be supplied. The typical case is to use exactly one destination and
395+
so the value may be supplied as a string, rather than a list with a
396+
single item. Each destination network will be mapped to a device on a
397+
host. Run ``cephadm list-networks`` for an example of these mappings.
398+
If destination is not supplied the network is automatically determined
399+
using the address value supplied and taken as the destination.
379400
custom_smb_global_options
380401
Optional mapping. Specify key-value pairs that will be directly added to
381402
the global ``smb.conf`` options (or equivalent) of a Samba server. Do
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
roles:
2+
# Test is for basic smb deployment & functionality. one node cluster is OK
3+
- - host.a
4+
- mon.a
5+
- mgr.x
6+
- osd.0
7+
- osd.1
8+
- client.0
9+
- - host.b
10+
- mon.b
11+
- osd.2
12+
- osd.3
13+
- - host.c
14+
- mon.c
15+
- osd.4
16+
- osd.5
17+
# Reserve a host for acting as a domain controller and smb client
18+
- - host.d
19+
- cephadm.exclude
20+
overrides:
21+
ceph:
22+
log-only-match:
23+
- CEPHADM_
24+
tasks:
25+
- cephadm.deploy_samba_ad_dc:
26+
role: host.d
27+
- vip:
28+
count: 2
29+
- cephadm:
30+
31+
- cephadm.shell:
32+
host.a:
33+
- ceph fs volume create cephfs
34+
- cephadm.wait_for_service:
35+
service: mds.cephfs
36+
37+
- cephadm.shell:
38+
host.a:
39+
# add subvolgroup & subvolumes for test
40+
- cmd: ceph fs subvolumegroup create cephfs smb
41+
- cmd: ceph fs subvolume create cephfs sv1 --group-name=smb --mode=0777
42+
- cmd: ceph fs subvolume create cephfs sv2 --group-name=smb --mode=0777
43+
# set up smb cluster and shares
44+
- cmd: ceph mgr module enable smb
45+
# TODO: replace sleep with poll of mgr state?
46+
- cmd: sleep 30
47+
- cmd: ceph smb apply -i -
48+
stdin: |
49+
# --- Begin Embedded YAML
50+
- resource_type: ceph.smb.cluster
51+
cluster_id: adipctdb
52+
auth_mode: active-directory
53+
domain_settings:
54+
realm: DOMAIN1.SINK.TEST
55+
join_sources:
56+
- source_type: resource
57+
ref: join1-admin
58+
custom_dns:
59+
- "{{ctx.samba_ad_dc_ip}}"
60+
public_addrs:
61+
- address: {{VIP0}}/{{VIPPREFIXLEN}}
62+
- address: {{VIP1}}/{{VIPPREFIXLEN}}
63+
placement:
64+
count: 3
65+
- resource_type: ceph.smb.join.auth
66+
auth_id: join1-admin
67+
auth:
68+
username: Administrator
69+
password: Passw0rd
70+
- resource_type: ceph.smb.share
71+
cluster_id: adipctdb
72+
share_id: share1
73+
cephfs:
74+
volume: cephfs
75+
subvolumegroup: smb
76+
subvolume: sv1
77+
path: /
78+
- resource_type: ceph.smb.share
79+
cluster_id: adipctdb
80+
share_id: share2
81+
cephfs:
82+
volume: cephfs
83+
subvolumegroup: smb
84+
subvolume: sv2
85+
path: /
86+
# --- End Embedded YAML
87+
# Wait for the smb service to start
88+
- cephadm.wait_for_service:
89+
service: smb.adipctdb
90+
# Since this is a true cluster there should be a clustermeta in rados
91+
- cephadm.shell:
92+
host.a:
93+
- cmd: rados --pool=.smb -N adipctdb get cluster.meta.json /dev/stdout
94+
95+
# Check if shares exist
96+
- cephadm.exec:
97+
host.d:
98+
- sleep 30
99+
- "{{ctx.samba_client_container_cmd|join(' ')}} smbclient -U DOMAIN1\\\\ckent%1115Rose. //{{'host.a'|role_to_remote|attr('ip_address')}}/share1 -c ls"
100+
- "{{ctx.samba_client_container_cmd|join(' ')}} smbclient -U DOMAIN1\\\\ckent%1115Rose. //{{'host.a'|role_to_remote|attr('ip_address')}}/share2 -c ls"
101+
102+
# verify CTDB is healthy, cluster well formed
103+
- cephadm.exec:
104+
host.a:
105+
- "{{ctx.cephadm}} ls --no-detail | {{ctx.cephadm}} shell jq -r 'map(select(.name | startswith(\"smb.adipctdb\")))[-1].name' > /tmp/svcname"
106+
- "{{ctx.cephadm}} enter -n $(cat /tmp/svcname) ctdb status > /tmp/ctdb_status"
107+
- cat /tmp/ctdb_status
108+
- grep 'pnn:0 .*OK' /tmp/ctdb_status
109+
- grep 'pnn:1 .*OK' /tmp/ctdb_status
110+
- grep 'pnn:2 .*OK' /tmp/ctdb_status
111+
- grep 'Number of nodes:3' /tmp/ctdb_status
112+
- rm -rf /tmp/svcname /tmp/ctdb_status
113+
114+
# Test the two assigned VIPs
115+
- cephadm.exec:
116+
host.d:
117+
- sleep 30
118+
- "{{ctx.samba_client_container_cmd|join(' ')}} smbclient -U DOMAIN1\\\\ckent%1115Rose. //{{VIP0}}/share1 -c ls"
119+
- "{{ctx.samba_client_container_cmd|join(' ')}} smbclient -U DOMAIN1\\\\ckent%1115Rose. //{{VIP1}}/share1 -c ls"
120+
- "{{ctx.samba_client_container_cmd|join(' ')}} smbclient -U DOMAIN1\\\\ckent%1115Rose. //{{VIP0}}/share2 -c ls"
121+
- "{{ctx.samba_client_container_cmd|join(' ')}} smbclient -U DOMAIN1\\\\ckent%1115Rose. //{{VIP1}}/share2 -c ls"
122+
123+
- cephadm.shell:
124+
host.a:
125+
- cmd: ceph smb apply -i -
126+
stdin: |
127+
# --- Begin Embedded YAML
128+
- resource_type: ceph.smb.cluster
129+
cluster_id: adipctdb
130+
intent: removed
131+
- resource_type: ceph.smb.join.auth
132+
auth_id: join1-admin
133+
intent: removed
134+
- resource_type: ceph.smb.share
135+
cluster_id: adipctdb
136+
share_id: share1
137+
intent: removed
138+
- resource_type: ceph.smb.share
139+
cluster_id: adipctdb
140+
share_id: share2
141+
intent: removed
142+
# --- End Embedded YAML
143+
# Wait for the smb service to be removed
144+
- cephadm.wait_for_service_not_present:
145+
service: smb.adipctdb

src/cephadm/cephadmlib/daemons/smb.py

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import re
66
import socket
77

8-
from typing import List, Dict, Tuple, Optional, Any
8+
from typing import List, Dict, Tuple, Optional, Any, NamedTuple
99

1010
from .. import context_getters
1111
from .. import daemon_form
@@ -27,6 +27,7 @@
2727
from ..daemon_identity import DaemonIdentity, DaemonSubIdentity
2828
from ..deploy import DeploymentType
2929
from ..exceptions import Error
30+
from ..host_facts import list_networks
3031
from ..net_utils import EndPoint
3132

3233

@@ -52,6 +53,20 @@ def valid(cls, value: str) -> bool:
5253
return False
5354

5455

56+
class ClusterPublicIP(NamedTuple):
57+
address: str
58+
destinations: List[str]
59+
60+
@classmethod
61+
def convert(cls, item: Dict[str, Any]) -> 'ClusterPublicIP':
62+
assert isinstance(item, dict)
63+
address = item['address']
64+
assert isinstance(address, str)
65+
destinations = item['destinations']
66+
assert isinstance(destinations, list)
67+
return cls(address, destinations)
68+
69+
5570
class Config:
5671
identity: DaemonIdentity
5772
instance_id: str
@@ -92,6 +107,7 @@ def __init__(
92107
rank_generation: int = -1,
93108
cluster_meta_uri: str = '',
94109
cluster_lock_uri: str = '',
110+
cluster_public_addrs: Optional[List[ClusterPublicIP]] = None,
95111
) -> None:
96112
self.identity = identity
97113
self.instance_id = instance_id
@@ -110,6 +126,7 @@ def __init__(
110126
self.rank_generation = rank_generation
111127
self.cluster_meta_uri = cluster_meta_uri
112128
self.cluster_lock_uri = cluster_lock_uri
129+
self.cluster_public_addrs = cluster_public_addrs
113130

114131
def __str__(self) -> str:
115132
return (
@@ -376,6 +393,7 @@ def __init__(self, ctx: CephadmContext, ident: DaemonIdentity):
376393
self._cached_layout: Optional[ContainerLayout] = None
377394
self._rank_info = context_getters.fetch_rank_info(ctx)
378395
self.smb_port = 445
396+
self._network_mapper = _NetworkMapper(ctx)
379397
logger.debug('Created SMB ContainerDaemonForm instance')
380398

381399
@staticmethod
@@ -415,6 +433,7 @@ def validate(self) -> None:
415433
vhostname = configs.get('virtual_hostname', '')
416434
cluster_meta_uri = configs.get('cluster_meta_uri', '')
417435
cluster_lock_uri = configs.get('cluster_lock_uri', '')
436+
cluster_public_addrs = configs.get('cluster_public_addrs', [])
418437

419438
if not instance_id:
420439
raise Error('invalid instance (cluster) id')
@@ -432,6 +451,12 @@ def validate(self) -> None:
432451
# the cluster/instanced id to the system hostname
433452
hname = socket.getfqdn()
434453
vhostname = f'{instance_id}-{hname}'
454+
_public_addrs = [
455+
ClusterPublicIP.convert(v) for v in cluster_public_addrs
456+
]
457+
if _public_addrs:
458+
# cache the cephadm networks->devices mapping for later
459+
self._network_mapper.load()
435460

436461
self._instance_cfg = Config(
437462
identity=self._identity,
@@ -447,6 +472,7 @@ def validate(self) -> None:
447472
vhostname=vhostname,
448473
cluster_meta_uri=cluster_meta_uri,
449474
cluster_lock_uri=cluster_lock_uri,
475+
cluster_public_addrs=_public_addrs,
450476
)
451477
if self._rank_info:
452478
(
@@ -674,7 +700,54 @@ def _write_ctdb_stub_config(self, path: pathlib.Path) -> None:
674700
'recovery_lock': f'!{reclock_cmd}',
675701
'cluster_meta_uri': self._cfg.cluster_meta_uri,
676702
'nodes_cmd': nodes_cmd,
703+
'public_addresses': self._network_mapper.for_sambacc(
704+
self._cfg
705+
),
677706
},
678707
}
679708
with file_utils.write_new(path) as fh:
680709
json.dump(stub_config, fh)
710+
711+
712+
class _NetworkMapper:
713+
"""Helper class that maps between cephadm-friendly address-networks
714+
groupings to ctdb-friendly address-device groupings.
715+
"""
716+
717+
def __init__(self, ctx: CephadmContext):
718+
self._ctx = ctx
719+
self._networks: Dict = {}
720+
721+
def load(self) -> None:
722+
logger.debug('fetching networks')
723+
self._networks = list_networks(self._ctx)
724+
725+
def _convert(self, addr: ClusterPublicIP) -> ClusterPublicIP:
726+
devs = []
727+
for net in addr.destinations:
728+
if net not in self._networks:
729+
# ignore mappings that cant exist on this host
730+
logger.warning(
731+
'destination network %r not found in %r',
732+
net,
733+
self._networks.keys(),
734+
)
735+
continue
736+
for dev in self._networks[net]:
737+
logger.debug(
738+
'adding device %s from network %r for public ip %s',
739+
dev,
740+
net,
741+
addr.address,
742+
)
743+
devs.append(dev)
744+
return ClusterPublicIP(addr.address, devs)
745+
746+
def for_sambacc(self, cfg: Config) -> List[Dict[str, Any]]:
747+
if not cfg.cluster_public_addrs:
748+
return []
749+
addrs = (self._convert(a) for a in (cfg.cluster_public_addrs or []))
750+
return [
751+
{'address': a.address, 'interfaces': a.destinations}
752+
for a in addrs
753+
]

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,9 @@ def generate_config(
7070
config_blobs['cluster_meta_uri'] = smb_spec.cluster_meta_uri
7171
if smb_spec.cluster_lock_uri:
7272
config_blobs['cluster_lock_uri'] = smb_spec.cluster_lock_uri
73+
cluster_public_addrs = smb_spec.strict_cluster_ip_specs()
74+
if cluster_public_addrs:
75+
config_blobs['cluster_public_addrs'] = cluster_public_addrs
7376
ceph_users = smb_spec.include_ceph_users or []
7477
config_blobs.update(
7578
self._ceph_config_and_keyring_for(

src/pybind/mgr/smb/handler.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1205,6 +1205,7 @@ def _generate_smb_service_spec(
12051205
user_sources=user_sources,
12061206
custom_dns=cluster.custom_dns,
12071207
include_ceph_users=user_entities,
1208+
cluster_public_addrs=cluster.service_spec_public_addrs(),
12081209
)
12091210

12101211

0 commit comments

Comments
 (0)