From 6b9ec080a09daa2470d4e6dc3db030acec3a1378 Mon Sep 17 00:00:00 2001 From: John Mulligan Date: Thu, 1 Feb 2024 19:42:21 -0500 Subject: [PATCH] pybind/mgr/smb: add internal.py intermediate funcs for internal config store Signed-off-by: John Mulligan --- src/pybind/mgr/smb/internal.py | 166 +++++++++++++++++++++++++++++++++ 1 file changed, 166 insertions(+) create mode 100644 src/pybind/mgr/smb/internal.py diff --git a/src/pybind/mgr/smb/internal.py b/src/pybind/mgr/smb/internal.py new file mode 100644 index 00000000000..d40561b08db --- /dev/null +++ b/src/pybind/mgr/smb/internal.py @@ -0,0 +1,166 @@ +"""Support for working with the internal data store and the strucutured +resources that the internal store holds. +""" +from typing import Collection, Tuple, Type, TypeVar + +from . import resources +from .enums import AuthMode, ConfigNS, State +from .proto import ConfigEntry, ConfigStore, Self, Simplifiable, one +from .resources import SMBResource +from .results import ErrorResult + +T = TypeVar('T') + + +class ResourceEntry: + """Base class for resource entry getter/setter objects.""" + + namespace: ConfigNS + + def __init__(self, key: str, config_entry: ConfigEntry) -> None: + self.key = key + self.config_entry = config_entry + + @property + def uri(self) -> str: + return self.config_entry.uri + + def get(self) -> SMBResource: + return one(resources.load(self.config_entry.get())) + + def get_resource_type(self, cls: Type[T]) -> T: + obj = self.get() + assert isinstance(obj, cls) + return obj + + def set(self, resource: Simplifiable) -> None: + self.config_entry.set(resource.to_simplified()) + + def create_or_update(self, resource: Simplifiable) -> State: + try: + previous = self.config_entry.get() + except KeyError: + previous = None + current = resource.to_simplified() + if current == previous: + return State.PRESENT + self.config_entry.set(current) + return State.CREATED if previous is None else State.UPDATED + + def remove(self) -> bool: + return self.config_entry.remove() + + +class ClusterEntry(ResourceEntry): + """Cluster resource getter/setter for the smb internal data store(s).""" + + namespace = ConfigNS.CLUSTERS + + @classmethod + def from_store(cls, store: ConfigStore, cluster_id: str) -> Self: + return cls(cluster_id, store[str(cls.namespace), cluster_id]) + + @classmethod + def ids(cls, store: ConfigStore) -> Collection[str]: + return store.contents(str(cls.namespace)) + + def get_cluster(self) -> resources.Cluster: + return self.get_resource_type(resources.Cluster) + + def create_or_update(self, resource: Simplifiable) -> State: + assert isinstance(resource, resources.Cluster) + try: + previous = self.config_entry.get() + except KeyError: + previous = None + current = resource.to_simplified() + if current == previous: + return State.PRESENT + elif previous is None: + self.config_entry.set(current) + return State.CREATED + # cluster is special in that is has some fields that we do not + # permit changing. + prev = getattr( + resources.Cluster, '_resource_config' + ).object_from_simplified(previous) + if resource.auth_mode != prev.auth_mode: + raise ErrorResult( + resource, + 'auth_mode value may not be changed', + status={'existing_auth_mode': prev.auth_mode}, + ) + if resource.auth_mode == AuthMode.ACTIVE_DIRECTORY: + assert resource.domain_settings + assert prev.domain_settings + if resource.domain_settings.realm != prev.domain_settings.realm: + raise ErrorResult( + resource, + 'domain/realm value may not be changed', + status={ + 'existing_domain_realm': prev.domain_settings.realm + }, + ) + self.config_entry.set(current) + return State.UPDATED + + +class ShareEntry(ResourceEntry): + """Share resource getter/setter for the smb internal data store(s).""" + + namespace = ConfigNS.SHARES + + @classmethod + def from_store( + cls, store: ConfigStore, cluster_id: str, share_id: str + ) -> Self: + key = f'{cluster_id}.{share_id}' + return cls(key, store[str(cls.namespace), key]) + + @classmethod + def ids(cls, store: ConfigStore) -> Collection[Tuple[str, str]]: + return [_split(k) for k in store.contents(str(cls.namespace))] + + def get_share(self) -> resources.Share: + return self.get_resource_type(resources.Share) + + +class JoinAuthEntry(ResourceEntry): + """JoinAuth resource getter/setter for the smb internal data store(s).""" + + namespace = ConfigNS.JOIN_AUTHS + + @classmethod + def from_store(cls, store: ConfigStore, auth_id: str) -> Self: + return cls(auth_id, store[str(cls.namespace), auth_id]) + + @classmethod + def ids(cls, store: ConfigStore) -> Collection[str]: + return store.contents(str(cls.namespace)) + + def get_join_auth(self) -> resources.JoinAuth: + return self.get_resource_type(resources.JoinAuth) + + +class UsersAndGroupsEntry(ResourceEntry): + """UsersAndGroupsEntry resource getter/setter for the smb internal data + store(s). + """ + + namespace = ConfigNS.USERS_AND_GROUPS + + @classmethod + def from_store(cls, store: ConfigStore, auth_id: str) -> Self: + return cls(auth_id, store[str(cls.namespace), auth_id]) + + @classmethod + def ids(cls, store: ConfigStore) -> Collection[str]: + return store.contents(str(cls.namespace)) + + def get_users_and_groups(self) -> resources.UsersAndGroups: + return self.get_resource_type(resources.UsersAndGroups) + + +def _split(share_key: str) -> Tuple[str, str]: + cluster_id, share_id = share_key.split('.', 1) + return cluster_id, share_id -- 2.39.5