From: John Mulligan Date: Sat, 6 Jul 2024 17:37:06 +0000 (-0400) Subject: mgr/smb: add SqliteMirroringStore class X-Git-Tag: v20.0.0~1489^2~7 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=4d5066832a887b18a69d03bd06340540554231e6;p=ceph.git mgr/smb: add SqliteMirroringStore class Add a SqliteMirroringStore class that can mirror objects from a particular namespace into the same namespace in another store. The Mirror configuration classes can be used to modify and merge objects from each store when fetched. Signed-off-by: John Mulligan --- diff --git a/src/pybind/mgr/smb/sqlite_store.py b/src/pybind/mgr/smb/sqlite_store.py index 5052cf0bc5f..277cde14fbf 100644 --- a/src/pybind/mgr/smb/sqlite_store.py +++ b/src/pybind/mgr/smb/sqlite_store.py @@ -18,11 +18,19 @@ else: Cursor = Connection = Any import contextlib +import copy import json import logging from .config_store import ObjectCachingEntry -from .proto import ConfigEntry, EntryKey, FindParams, Protocol, Simplified +from .proto import ( + ConfigEntry, + ConfigStore, + EntryKey, + FindParams, + Protocol, + Simplified, +) log = logging.getLogger(__name__) @@ -385,6 +393,100 @@ class SqliteStore: self.set_object((keyns, keyname), obj) +class Mirror: + """A mirror configuration for a SqliteMirroringStore namespace. + The mirror will store a copy of an object in a separate ConfigStore. + This copy may be modified by the filter methods and combined using + the `merge` method. + """ + + namespace: str + store: ConfigStore + + def __init__(self, namespace: str, store: ConfigStore) -> None: + self.namespace = namespace + self.store = store + + def filter_object(self, obj: Simplified) -> Simplified: + """Return a potentially modified object to be stored in the sqlite db store.""" + return obj + + def filter_mirror_object(self, obj: Simplified) -> Simplified: + """Return a potentially modified object to be stored in the mirror store.""" + return obj + + def merge(self, obj1: Simplified, obj2: Simplified) -> Simplified: + """Combine, if desired, the objects fetched from the sqlite db store + and the mirror store. + """ + obj = copy.deepcopy(obj1) + obj.update(obj2) + return obj + + +class SqliteMirroringStore(SqliteStore): + """A store based on the SqliteStore that supports mirroring objects in + specified namespaces from the database into a different store. + + The purpose of the mirror is to store objects in the db for the speed and + efficiency of the db's search features while storing a copy of the object + in a different store that will have other properties. The mirror classes + can configure how objects in each namespace are handled and which store + takes precedence when fetching and merging results. + """ + + def __init__( + self, + backend: DBAcessor, + tables: Iterable[Table], + mirrors: Iterable[Mirror], + ) -> None: + super().__init__(backend, tables) + self._mirrors: Dict[str, Mirror] = {m.namespace: m for m in mirrors} + + def _mirror(self, key: EntryKey) -> Optional[Mirror]: + ns, _ = key + return self._mirrors.get(ns) + + def set_object(self, key: EntryKey, obj: Simplified) -> None: + """Create or update a simplified object in the store.""" + mirror = self._mirror(key) + if mirror is None: + log.debug("Mirroring set_object: no mirror for key %r", key) + super().set_object(key, obj) + return + log.debug("Mirroring set_object: mirror=%r", mirror) + obj_for_store = mirror.filter_object(obj) + obj_for_mirror = mirror.filter_mirror_object(obj) + mirror.store[key].set(obj_for_mirror) + super().set_object(key, obj_for_store) + + def get_object(self, key: EntryKey) -> Simplified: + """Fetch a simplified object from the store.""" + mirror = self._mirror(key) + if mirror is None: + log.debug("Mirroring get_object: no mirror for %r", key) + return super().get_object(key) + log.debug("Mirroring get_object: mirror=%r", mirror) + obj = super().get_object(key) + mirror_obj = mirror.store[key].get() + return mirror.merge(obj, mirror_obj) + + +class MirrorJoinAuths(Mirror): + """Mirroring configuration for objects in the join_auths namespace.""" + + def __init__(self, store: ConfigStore) -> None: + super().__init__('join_auths', store) + + +class MirrorUsersAndGroups(Mirror): + """Mirroring configuration for objects in the users_and_groups namespace.""" + + def __init__(self, store: ConfigStore) -> None: + super().__init__('users_and_groups', store) + + def _tables( *, specialize: bool = True,