]> git.apps.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
mgr/smb: move a handful of small functions to utils.py
authorJohn Mulligan <jmulligan@redhat.com>
Wed, 8 May 2024 14:45:05 +0000 (10:45 -0400)
committerJohn Mulligan <jmulligan@redhat.com>
Mon, 24 Jun 2024 12:41:08 +0000 (08:41 -0400)
Includes updating the files that were using them and even
a few test functions.
Amusingly, the `one` function was being tested as part of
test_handler.py demonstrating the confusion that not having
a utils.py and corresponding test file brought.

Signed-off-by: John Mulligan <jmulligan@redhat.com>
src/pybind/mgr/smb/handler.py
src/pybind/mgr/smb/internal.py
src/pybind/mgr/smb/module.py
src/pybind/mgr/smb/proto.py
src/pybind/mgr/smb/resources.py
src/pybind/mgr/smb/results.py
src/pybind/mgr/smb/tests/test_handler.py
src/pybind/mgr/smb/tests/test_utils.py [new file with mode: 0644]
src/pybind/mgr/smb/utils.py [new file with mode: 0644]

index cc799517ba6105a164662e73921821724ba78bcb..84702e72f7885a5d99ee517ddae01e7671d049b4 100644 (file)
@@ -13,8 +13,6 @@ from typing import (
 )
 
 import logging
-import random
-import string
 import time
 
 from ceph.deployment.service_spec import SMBSpec
@@ -46,10 +44,10 @@ from .proto import (
     OrchSubmitter,
     PathResolver,
     Simplified,
-    checked,
 )
 from .resources import SMBResource
 from .results import ErrorResult, Result, ResultGroup
+from .utils import checked, ynbool
 
 ClusterRef = Union[resources.Cluster, resources.RemovedCluster]
 ShareRef = Union[resources.Share, resources.RemovedShare]
@@ -956,11 +954,6 @@ def _ug_refs(cluster: resources.Cluster) -> Collection[str]:
     }
 
 
-def _ynbool(value: bool) -> str:
-    """Convert a bool to an smb.conf compatible string."""
-    return 'Yes' if value else 'No'
-
-
 def _generate_share(
     share: resources.Share, resolver: PathResolver, cephx_entity: str
 ) -> Dict[str, Dict[str, str]]:
@@ -988,8 +981,8 @@ def _generate_share(
             'ceph:config_file': '/etc/ceph/ceph.conf',
             'ceph:filesystem': share.cephfs.volume,
             'ceph:user_id': cephx_entity,
-            'read only': _ynbool(share.readonly),
-            'browseable': _ynbool(share.browseable),
+            'read only': ynbool(share.readonly),
+            'browseable': ynbool(share.browseable),
             'kernel share modes': 'no',
             'x:ceph:id': f'{share.cluster_id}.{share.share_id}',
         }
@@ -1251,11 +1244,3 @@ def _cephx_data_entity(cluster_id: str) -> str:
     use for data access.
     """
     return f'client.smb.fs.cluster.{cluster_id}'
-
-
-def rand_name(prefix: str, max_len: int = 18, suffix_len: int = 8) -> str:
-    trunc = prefix[: (max_len - suffix_len)]
-    suffix = ''.join(
-        random.choice(string.ascii_lowercase) for _ in range(suffix_len)
-    )
-    return f'{trunc}{suffix}'
index 1b2554317eca33c01cdfc202a93a0e7f262ea9c6..3571ed44400e548c075ac26148063190e2e73a94 100644 (file)
@@ -12,10 +12,10 @@ from .proto import (
     EntryKey,
     Self,
     Simplifiable,
-    one,
 )
 from .resources import SMBResource
 from .results import ErrorResult
+from .utils import one
 
 T = TypeVar('T')
 
index d8133a59c478960c4fc843606fdbfb3e2df9c59d..70c97d1a975483f3e7a822ce3cdf5eb32ae61477 100644 (file)
@@ -6,7 +6,16 @@ import orchestrator
 from ceph.deployment.service_spec import PlacementSpec, SMBSpec
 from mgr_module import MgrModule, Option
 
-from . import cli, fs, handler, mon_store, rados_store, resources, results
+from . import (
+    cli,
+    fs,
+    handler,
+    mon_store,
+    rados_store,
+    resources,
+    results,
+    utils,
+)
 from .enums import AuthMode, JoinSourceType, UserGroupSourceType
 from .proto import AccessAuthorizer, Simplified
 
@@ -116,7 +125,7 @@ class Module(orchestrator.OrchestratorClientMixin, MgrModule):
                         'a domain join username & password value'
                         ' must contain a "%" separator'
                     )
-                rname = handler.rand_name(cluster_id)
+                rname = utils.rand_name(cluster_id)
                 join_sources.append(
                     resources.JoinSource(
                         source_type=JoinSourceType.RESOURCE,
@@ -156,7 +165,7 @@ class Module(orchestrator.OrchestratorClientMixin, MgrModule):
             for unpw in define_user_pass or []:
                 username, password = unpw.split('%', 1)
                 users.append({'name': username, 'password': password})
-            rname = handler.rand_name(cluster_id)
+            rname = utils.rand_name(cluster_id)
             user_group_settings.append(
                 resources.UserGroupSource(
                     source_type=UserGroupSourceType.RESOURCE, ref=rname
index 6af80866cff32518c2d1d8622c2342a1e6481a10..ffcc647a48edadabf44e2a6472c7dcfdb0fe6499 100644 (file)
@@ -9,7 +9,6 @@ from typing import (
     List,
     Optional,
     Tuple,
-    TypeVar,
 )
 
 import sys
@@ -155,27 +154,3 @@ class AccessAuthorizer(Protocol):
         self, volume: str, entity: str, caps: str = ''
     ) -> None:
         ...  # pragma: no cover
-
-
-T = TypeVar('T')
-
-
-# TODO: move to a utils.py
-def one(lst: List[T]) -> T:
-    if len(lst) != 1:
-        raise ValueError("list does not contain exactly one element")
-    return lst[0]
-
-
-class IsNoneError(ValueError):
-    pass
-
-
-def checked(v: Optional[T]) -> T:
-    """Ensures the provided value is not a None or raises a IsNoneError.
-    Intended use is similar to an `assert v is not None` but more usable in
-    one-liners and list/dict/etc comprehensions.
-    """
-    if v is None:
-        raise IsNoneError('value is None')
-    return v
index 290758c578e615f909d5cabb7537d5a765c4a16c..842b4387855120265bbc6d6ba6b7847dd4745ca5 100644 (file)
@@ -16,7 +16,8 @@ from .enums import (
     LoginCategory,
     UserGroupSourceType,
 )
-from .proto import Self, Simplified, checked
+from .proto import Self, Simplified
+from .utils import checked
 
 
 def _get_intent(data: Simplified) -> Intent:
index 77bda1e6a29af05c369a7ef5d038cec01637c58a..b62d6e66377684261e3b409e10da98c46643ac78 100644 (file)
@@ -2,8 +2,9 @@ from typing import Iterable, Iterator, List, Optional
 
 import errno
 
-from .proto import Simplified, one
+from .proto import Simplified
 from .resources import SMBResource
+from .utils import one
 
 _DOMAIN = 'domain'
 
index 2eab2a1cd915946f79cbe522b5274f48ed488ffc..93395c7522543babfe1f46d6d4809eadd3271871 100644 (file)
@@ -574,14 +574,6 @@ def test_apply_no_matching_cluster_error(thandler):
     assert not rg.success
 
 
-def test_one():
-    assert smb.proto.one(['a']) == 'a'
-    with pytest.raises(ValueError):
-        smb.proto.one([])
-    with pytest.raises(ValueError):
-        smb.proto.one(['a', 'b'])
-
-
 def test_apply_full_cluster_create(thandler):
     to_apply = [
         smb.resources.JoinAuth(
@@ -1259,23 +1251,6 @@ def test_apply_cluster_bad_linked_auth(thandler):
     assert rs['results'][1]['msg'] == 'join auth linked to different cluster'
 
 
-def test_rand_name():
-    name = smb.handler.rand_name('bob')
-    assert name.startswith('bob')
-    assert len(name) == 11
-    name = smb.handler.rand_name('carla')
-    assert name.startswith('carla')
-    assert len(name) == 13
-    name = smb.handler.rand_name('dangeresque')
-    assert name.startswith('dangeresqu')
-    assert len(name) == 18
-    name = smb.handler.rand_name('fhqwhgadsfhqwhgadsfhqwhgads')
-    assert name.startswith('fhqwhgadsf')
-    assert len(name) == 18
-    name = smb.handler.rand_name('')
-    assert len(name) == 8
-
-
 def test_apply_with_create_only(thandler):
     test_apply_full_cluster_create(thandler)
 
diff --git a/src/pybind/mgr/smb/tests/test_utils.py b/src/pybind/mgr/smb/tests/test_utils.py
new file mode 100644 (file)
index 0000000..5017990
--- /dev/null
@@ -0,0 +1,28 @@
+import pytest
+
+import smb.utils
+
+
+def test_one():
+    assert smb.utils.one(['a']) == 'a'
+    with pytest.raises(ValueError):
+        smb.utils.one([])
+    with pytest.raises(ValueError):
+        smb.utils.one(['a', 'b'])
+
+
+def test_rand_name():
+    name = smb.utils.rand_name('bob')
+    assert name.startswith('bob')
+    assert len(name) == 11
+    name = smb.utils.rand_name('carla')
+    assert name.startswith('carla')
+    assert len(name) == 13
+    name = smb.utils.rand_name('dangeresque')
+    assert name.startswith('dangeresqu')
+    assert len(name) == 18
+    name = smb.utils.rand_name('fhqwhgadsfhqwhgadsfhqwhgads')
+    assert name.startswith('fhqwhgadsf')
+    assert len(name) == 18
+    name = smb.utils.rand_name('')
+    assert len(name) == 8
diff --git a/src/pybind/mgr/smb/utils.py b/src/pybind/mgr/smb/utils.py
new file mode 100644 (file)
index 0000000..2646815
--- /dev/null
@@ -0,0 +1,46 @@
+"""Assorted utility functions for smb mgr module."""
+from typing import List, Optional, TypeVar
+
+import random
+import string
+
+T = TypeVar('T')
+
+
+def one(lst: List[T]) -> T:
+    """Given a list, ensure that the list contains exactly one item and return
+    it.  A ValueError will be raised in the case that the list does not contain
+    exactly one item.
+    """
+    if len(lst) != 1:
+        raise ValueError("list does not contain exactly one element")
+    return lst[0]
+
+
+class IsNoneError(ValueError):
+    """A ValueError subclass raised by ``checked`` function."""
+
+    pass
+
+
+def checked(v: Optional[T]) -> T:
+    """Ensures the provided value is not a None or raises a IsNoneError.
+    Intended use is similar to an `assert v is not None` but more usable in
+    one-liners and list/dict/etc comprehensions.
+    """
+    if v is None:
+        raise IsNoneError('value is None')
+    return v
+
+
+def ynbool(value: bool) -> str:
+    """Convert a bool to an smb.conf-style boolean string."""
+    return 'Yes' if value else 'No'
+
+
+def rand_name(prefix: str, max_len: int = 18, suffix_len: int = 8) -> str:
+    trunc = prefix[: (max_len - suffix_len)]
+    suffix = ''.join(
+        random.choice(string.ascii_lowercase) for _ in range(suffix_len)
+    )
+    return f'{trunc}{suffix}'