Skip to content

Commit e0394b8

Browse files
authored
Merge pull request ceph#57293 from phlogistonjohn/jjm-smb-create-only
smb: have create cluster and create share commands only create Reviewed-by: Adam King <[email protected]> Reviewed-by: Avan Thakkar <[email protected]>
2 parents e2b9d1e + 5da3eb1 commit e0394b8

File tree

3 files changed

+133
-18
lines changed

3 files changed

+133
-18
lines changed

src/pybind/mgr/smb/handler.py

Lines changed: 43 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -190,37 +190,44 @@ def __init__(self, store: ConfigStore) -> None:
190190
self.destination_store = store
191191
self.incoming: Dict[EntryKey, SMBResource] = {}
192192
self.deleted: Dict[EntryKey, SMBResource] = {}
193-
self._keycache: Set[EntryKey] = set()
193+
self._store_keycache: Set[EntryKey] = set()
194+
self._virt_keycache: Set[EntryKey] = set()
194195

195196
def stage(self, resource: SMBResource) -> None:
196-
self._keycache = set()
197+
self._virt_keycache = set()
197198
ekey = resource_key(resource)
198199
if resource.intent == Intent.REMOVED:
199200
self.deleted[ekey] = resource
200201
else:
201202
self.deleted.pop(ekey, None)
202203
self.incoming[ekey] = resource
203204

204-
def _virtual_keys(self) -> Iterator[EntryKey]:
205-
new = set(self.incoming.keys())
206-
for ekey in self.destination_store:
207-
if ekey in self.deleted:
208-
continue
209-
yield ekey
210-
new.discard(ekey)
211-
for ekey in new:
212-
yield ekey
205+
def _virtual_keys(self) -> Collection[EntryKey]:
206+
if self._virt_keycache:
207+
return self._virt_keycache
208+
self._virt_keycache = set(self._store_keys()) - set(
209+
self.deleted
210+
) | set(self.incoming)
211+
return self._virt_keycache
212+
213+
def _store_keys(self) -> Collection[EntryKey]:
214+
if not self._store_keycache:
215+
self._store_keycache = set(self.destination_store)
216+
return self._store_keycache
213217

214218
def __iter__(self) -> Iterator[EntryKey]:
215-
self._keycache = set(self._virtual_keys())
216-
return iter(self._keycache)
219+
return iter(self._virtual_keys())
217220

218221
def namespaces(self) -> Collection[str]:
219222
return {k[0] for k in self}
220223

221224
def contents(self, ns: str) -> Collection[str]:
222225
return {kname for kns, kname in self if kns == ns}
223226

227+
def is_new(self, resource: SMBResource) -> bool:
228+
ekey = resource_key(resource)
229+
return ekey not in self._store_keys()
230+
224231
def get_cluster(self, cluster_id: str) -> resources.Cluster:
225232
ekey = (str(ClusterEntry.namespace), cluster_id)
226233
if ekey in self.incoming:
@@ -335,7 +342,12 @@ def __init__(
335342
f' orch {self._orch!r}'
336343
)
337344

338-
def apply(self, inputs: Iterable[SMBResource]) -> ResultGroup:
345+
def apply(
346+
self, inputs: Iterable[SMBResource], *, create_only: bool = False
347+
) -> ResultGroup:
348+
"""Apply resource configuration changes.
349+
Set `create_only` to disable changing existing resource values.
350+
"""
339351
log.debug('applying changes to internal data store')
340352
results = ResultGroup()
341353
staging = _Staging(self.internal_store)
@@ -344,7 +356,9 @@ def apply(self, inputs: Iterable[SMBResource]) -> ResultGroup:
344356
for resource in incoming:
345357
staging.stage(resource)
346358
for resource in incoming:
347-
results.append(self._check(resource, staging))
359+
results.append(
360+
self._check(resource, staging, create_only=create_only)
361+
)
348362
except ErrorResult as err:
349363
results.append(err)
350364
except Exception as err:
@@ -421,9 +435,22 @@ def _search_resources(self, matcher: _Matcher) -> List[SMBResource]:
421435
log.debug("search found %d resources", len(out))
422436
return out
423437

424-
def _check(self, resource: SMBResource, staging: _Staging) -> Result:
438+
def _check(
439+
self,
440+
resource: SMBResource,
441+
staging: _Staging,
442+
*,
443+
create_only: bool = False,
444+
) -> Result:
425445
"""Check/validate a staged resource."""
426446
log.debug('staging resource: %r', resource)
447+
if create_only:
448+
if not staging.is_new(resource):
449+
return Result(
450+
resource,
451+
success=False,
452+
msg='a resource with the same ID already exists',
453+
)
427454
try:
428455
if isinstance(
429456
resource, (resources.Cluster, resources.RemovedCluster)

src/pybind/mgr/smb/module.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ def cluster_create(
178178
placement=pspec,
179179
)
180180
to_apply.append(cluster)
181-
return self._handler.apply(to_apply).squash(cluster)
181+
return self._handler.apply(to_apply, create_only=True).squash(cluster)
182182

183183
@cli.SMBCommand('cluster rm', perm='rw')
184184
def cluster_rm(self, cluster_id: str) -> handler.Result:
@@ -220,7 +220,7 @@ def share_create(
220220
subvolume=subvolume,
221221
),
222222
)
223-
return self._handler.apply([share]).one()
223+
return self._handler.apply([share], create_only=True).one()
224224

225225
@cli.SMBCommand('share rm', perm='rw')
226226
def share_rm(self, cluster_id: str, share_id: str) -> handler.Result:

src/pybind/mgr/smb/tests/test_handler.py

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1116,3 +1116,91 @@ def test_rand_name():
11161116
assert len(name) == 18
11171117
name = smb.handler.rand_name('')
11181118
assert len(name) == 8
1119+
1120+
1121+
def test_apply_with_create_only(thandler):
1122+
test_apply_full_cluster_create(thandler)
1123+
1124+
to_apply = [
1125+
smb.resources.Cluster(
1126+
cluster_id='mycluster1',
1127+
auth_mode=smb.enums.AuthMode.ACTIVE_DIRECTORY,
1128+
domain_settings=smb.resources.DomainSettings(
1129+
realm='MYDOMAIN.EXAMPLE.ORG',
1130+
join_sources=[
1131+
smb.resources.JoinSource(
1132+
source_type=smb.enums.JoinSourceType.RESOURCE,
1133+
ref='join1',
1134+
),
1135+
],
1136+
),
1137+
custom_dns=['192.168.76.204'],
1138+
),
1139+
smb.resources.Share(
1140+
cluster_id='mycluster1',
1141+
share_id='homedirs',
1142+
name='Altered Home Directries',
1143+
cephfs=smb.resources.CephFSStorage(
1144+
volume='cephfs',
1145+
subvolume='homedirs',
1146+
path='/',
1147+
),
1148+
),
1149+
smb.resources.Share(
1150+
cluster_id='mycluster1',
1151+
share_id='foodirs',
1152+
name='Foo Directries',
1153+
cephfs=smb.resources.CephFSStorage(
1154+
volume='cephfs',
1155+
subvolume='homedirs',
1156+
path='/foo',
1157+
),
1158+
),
1159+
]
1160+
results = thandler.apply(to_apply, create_only=True)
1161+
assert not results.success
1162+
rs = results.to_simplified()
1163+
assert len(rs['results']) == 3
1164+
assert (
1165+
rs['results'][0]['msg']
1166+
== 'a resource with the same ID already exists'
1167+
)
1168+
assert (
1169+
rs['results'][1]['msg']
1170+
== 'a resource with the same ID already exists'
1171+
)
1172+
1173+
# no changes to the store
1174+
assert (
1175+
'shares',
1176+
'mycluster1.foodirs',
1177+
) not in thandler.internal_store.data
1178+
assert ('shares', 'mycluster1.homedirs') in thandler.internal_store.data
1179+
assert (
1180+
thandler.internal_store.data[('shares', 'mycluster1.homedirs')][
1181+
'name'
1182+
]
1183+
== 'Home Directries'
1184+
)
1185+
1186+
# foodirs share is new, it can be applied separately
1187+
to_apply = [
1188+
smb.resources.Share(
1189+
cluster_id='mycluster1',
1190+
share_id='foodirs',
1191+
name='Foo Directries',
1192+
cephfs=smb.resources.CephFSStorage(
1193+
volume='cephfs',
1194+
subvolume='homedirs',
1195+
path='/foo',
1196+
),
1197+
),
1198+
]
1199+
results = thandler.apply(to_apply, create_only=True)
1200+
assert results.success
1201+
rs = results.to_simplified()
1202+
assert len(rs['results']) == 1
1203+
assert (
1204+
'shares',
1205+
'mycluster1.foodirs',
1206+
) in thandler.internal_store.data

0 commit comments

Comments
 (0)