]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
pybind/mgr/smb: add external.py intermediate funcs for external config store
authorJohn Mulligan <jmulligan@redhat.com>
Fri, 2 Feb 2024 00:42:06 +0000 (19:42 -0500)
committerJohn Mulligan <jmulligan@redhat.com>
Thu, 25 Apr 2024 23:10:39 +0000 (19:10 -0400)
The external stores are used to share configuration with the samba
containers.

Signed-off-by: John Mulligan <jmulligan@redhat.com>
src/pybind/mgr/smb/external.py [new file with mode: 0644]

diff --git a/src/pybind/mgr/smb/external.py b/src/pybind/mgr/smb/external.py
new file mode 100644 (file)
index 0000000..ffda318
--- /dev/null
@@ -0,0 +1,133 @@
+"""Support for working with the external data stores and the items within.
+
+The external data stores exist specifially to share configuration with
+systems outside the mgr module. The naming is simpilfied and the
+cluster id acts as a namespace so that external entities can be
+granted access to only one cluster at a time.
+"""
+from typing import Collection, Tuple
+
+from .proto import ConfigStore, EntryKey
+
+# Convert identifiers into config entry keys
+
+
+def config_key(cluster_id: str, override: bool = False) -> EntryKey:
+    """Return key identifying a (cluster) config in an external store."""
+    return (cluster_id, 'config.smb.override' if override else 'config.smb')
+
+
+def cluster_placeholder_key(cluster_id: str) -> EntryKey:
+    """Return key identifying a cluster info in an external store."""
+    return (cluster_id, 'cluster-info')
+
+
+def join_source_key(cluster_id: str, name: str) -> EntryKey:
+    """Return key identifying a join source object in an external store."""
+    # don't feed "keys" back into this function, it's only for
+    # generating the key from an internal name
+    assert not name.startswith('join.')
+    assert not name.endswith('.json')
+    return (cluster_id, f'join.{name}.json')
+
+
+def users_and_groups_key(cluster_id: str, name: str) -> EntryKey:
+    """Return key identifying a users-and-groups object in an external
+    store.
+    """
+    # don't feed "keys" back into this function, it's only for
+    # generating the key from an internal name
+    assert not name.startswith('users-groups.')
+    assert not name.endswith('.json')
+    return (cluster_id, f'users-groups.{name}.json')
+
+
+def spec_backup_key(cluster_id: str) -> EntryKey:
+    """Return key identifying a smb service spec backup object in an external
+    store.
+    """
+    return (cluster_id, 'spec.smb')
+
+
+# Enumerate keys in a store
+
+
+def stored_join_source_keys(
+    store: ConfigStore, cluster_id: str
+) -> Collection[str]:
+    """Return a collection of names for join source objects in an external
+    store.
+    """
+    return [k for k in store.contents(cluster_id) if k.startswith('join.')]
+
+
+def stored_cluster_placeholder_keys(
+    store: ConfigStore, cluster_id: str
+) -> Collection[str]:
+    """Return a collection of names for join source objects in an external
+    store.
+    """
+    return [k for k in store.contents(cluster_id) if k == 'cluster-info']
+
+
+def stored_usergroup_source_keys(
+    store: ConfigStore, cluster_id: str
+) -> Collection[str]:
+    """Return a collection of names for users & groups source objects in an external
+    store.
+    """
+    return [
+        k for k in store.contents(cluster_id) if k.startswith('users-groups.')
+    ]
+
+
+def stored_config_keys(
+    store: ConfigStore, cluster_id: str
+) -> Collection[str]:
+    """Return a collection of names for config objects in an external store."""
+    return [
+        k for k in store.contents(cluster_id) if k.startswith('config.smb')
+    ]
+
+
+def stored_cluster_ids(
+    store: ConfigStore, *alt: ConfigStore
+) -> Collection[str]:
+    """Return a collection of cluster ids present in one or more external
+    config stores.
+    """
+    ns = set(store.namespaces())
+    for alt_store in alt:
+        ns.update(store.namespaces())
+    return ns
+
+
+# Remove objcts in an external config store
+
+
+def rm_cluster(store: ConfigStore, cluster_id: str) -> None:
+    """Remove all objects belonging to a given cluster id in a config store."""
+    knames = set(store.contents(cluster_id))
+    for kname in knames:
+        store.remove((cluster_id, kname))
+    # if the config store needs to remove the (now empty) namespace explicitly
+    # it must provide a remove_namespace method.
+    rm_namespace = getattr(store, 'remove_namespace', None)
+    if rm_namespace:
+        rm_namespace(cluster_id)
+    # TODO: this can probably be removed once the code has matured
+    assert cluster_id not in store.namespaces()
+
+
+def rm_other_in_ns(
+    store: ConfigStore,
+    namespace: str,
+    expected_keys: Collection[Tuple[str, str]],
+) -> None:
+    """Remove all objects within a given namespace that do not appear in the
+    given collection of expected_keys.
+    """
+    nskeys = set(key for key in store if key[0] == namespace)
+    remove_keys = nskeys - set(expected_keys)
+    for key in remove_keys:
+        store.remove(key)