From: Joshua Schmid Date: Thu, 9 Jul 2020 09:40:54 +0000 (+0200) Subject: mgr/osd_support: add unittests X-Git-Tag: v15.2.5~105^2~3 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=a1707b7f8e72e45bb5fafa3239081a209664b17b;p=ceph.git mgr/osd_support: add unittests Signed-off-by: Joshua Schmid (cherry picked from commit 0fcfc5455dde176bc1bc82fb3176c8be4788c15d) --- diff --git a/src/pybind/mgr/osd_support/__init__.py b/src/pybind/mgr/osd_support/__init__.py index 88ed2b9f57c1..f4f9e26e0643 100644 --- a/src/pybind/mgr/osd_support/__init__.py +++ b/src/pybind/mgr/osd_support/__init__.py @@ -1 +1,7 @@ +import os + +if 'UNITTEST' in os.environ: + import tests + tests.mock_ceph_modules() # type: ignore + from .module import OSDSupport diff --git a/src/pybind/mgr/osd_support/module.py b/src/pybind/mgr/osd_support/module.py index 5b743200e003..a219ca868cc6 100644 --- a/src/pybind/mgr/osd_support/module.py +++ b/src/pybind/mgr/osd_support/module.py @@ -188,10 +188,13 @@ class OSDSupport(MgrModule): self.run = False self.event.set() + def get_osds_in_cluster(self) -> List[str]: + osd_map = self.get_osdmap() + return [x.get('osd') for x in osd_map.dump().get('osds', [])] + def osds_not_in_cluster(self, osd_ids: List[int]) -> Set[int]: self.log.info(f"Checking if provided osds <{osd_ids}> exist in the cluster") - osd_map = self.get_osdmap() - cluster_osds = [x.get('osd') for x in osd_map.dump().get('osds', [])] + cluster_osds = self.get_osds_in_cluster() not_in_cluster = set() for osd_id in osd_ids: if int(osd_id) not in cluster_osds: @@ -235,7 +238,7 @@ class OSDSupport(MgrModule): osd_df = self.osd_df() osd_nodes = osd_df.get('nodes', []) for osd_node in osd_nodes: - if osd_node.get('id', None) == int(osd_id): + if osd_node.get('id') == int(osd_id): return osd_node.get('pgs', -1) return -1 @@ -243,8 +246,9 @@ class OSDSupport(MgrModule): osd_df = self.osd_df() osd_nodes = osd_df.get('nodes', []) for osd_node in osd_nodes: - if osd_node.get('id', None) == int(osd_id): - return float(osd_node.get('crush_weight')) + if osd_node.get('id') == int(osd_id): + if 'crush_weight' in osd_node: + return float(osd_node.get('crush_weight')) return -1.0 def reweight_osd(self, osd_id: int, weight: float = 0.0) -> bool: diff --git a/src/pybind/mgr/osd_support/tests/__init__.py b/src/pybind/mgr/osd_support/tests/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/src/pybind/mgr/osd_support/tests/fixtures.py b/src/pybind/mgr/osd_support/tests/fixtures.py new file mode 100644 index 000000000000..e948a3af14ca --- /dev/null +++ b/src/pybind/mgr/osd_support/tests/fixtures.py @@ -0,0 +1,14 @@ +from osd_support import OSDSupport +import pytest + +from tests import mock + + +@pytest.yield_fixture() +def osd_support_module(): + with mock.patch("osd_support.module.OSDSupport.get_osdmap"), \ + mock.patch("osd_support.module.OSDSupport.osd_df"), \ + mock.patch("osd_support.module.OSDSupport.mon_command", return_value=(0, '', '')): + m = OSDSupport.__new__(OSDSupport) + m.__init__('osd_support', 0, 0) + yield m diff --git a/src/pybind/mgr/osd_support/tests/test_osd_support.py b/src/pybind/mgr/osd_support/tests/test_osd_support.py new file mode 100644 index 000000000000..208a3e896d58 --- /dev/null +++ b/src/pybind/mgr/osd_support/tests/test_osd_support.py @@ -0,0 +1,144 @@ +import pytest +from .fixtures import osd_support_module as osdsf +from tests import mock + + +class TestOSDSupport: + + def test_init(self, osdsf): + assert osdsf.osd_ids == set() + assert osdsf.emptying_osds == set() + assert osdsf.check_osds == set() + assert osdsf.empty == set() + + def test_osds_not_in_cluster(self, osdsf): + assert osdsf.osds_not_in_cluster([1, 2]) == {1, 2} + + @mock.patch("osd_support.module.OSDSupport.get_osds_in_cluster") + def test_osds_in_cluster(self, osds_in_cluster, osdsf): + osds_in_cluster.return_value = [1] + assert osdsf.osds_not_in_cluster([1, 2]) == {2} + + @pytest.mark.parametrize( + "is_empty, osd_ids, expected", + [ + (False, {1, 2}, []), + (True, {1, 2}, [1, 2]), + (None, {1, 2}, []), + ] + ) + def test_empty_osd(self, osdsf, is_empty, osd_ids, expected): + with mock.patch("osd_support.module.OSDSupport.is_empty", return_value=is_empty): + assert osdsf.empty_osds(osd_ids) == expected + + @pytest.mark.parametrize( + "pg_count, expected", + [ + (0, True), + (1, False), + (-1, False), + (9999999999999, False), + ] + ) + def test_is_emtpy(self, pg_count, expected, osdsf): + with mock.patch("osd_support.module.OSDSupport.get_pg_count", return_value=pg_count): + assert osdsf.is_empty(1) == expected + + @pytest.mark.parametrize( + "osd_ids, reweight_out, expected", + [ + ({1}, [False], False), + ({1}, [True], True), + ({1, 2}, [True, True], True), + ({1, 2}, [True, False], False), + ] + ) + def test_reweight_osds(self, osdsf, osd_ids, reweight_out, expected): + with mock.patch("osd_support.module.OSDSupport.reweight_osd", side_effect=reweight_out): + assert osdsf.reweight_osds(osd_ids) == expected + + @pytest.mark.parametrize( + "osd_id, osd_df, expected", + [ + # missing 'nodes' key + (1, dict(nodes=[]), -1), + # missing 'pgs' key + (1, dict(nodes=[dict(id=1)]), -1), + # id != osd_id + (1, dict(nodes=[dict(id=999, pgs=1)]), -1), + # valid + (1, dict(nodes=[dict(id=1, pgs=1)]), 1), + ] + ) + def test_get_pg_count(self, osdsf, osd_id, osd_df, expected): + with mock.patch("osd_support.module.OSDSupport.osd_df", return_value=osd_df): + assert osdsf.get_pg_count(osd_id) == expected + + @pytest.mark.parametrize( + "osd_id, osd_df, expected", + [ + # missing 'nodes' key + (1, dict(nodes=[]), -1.0), + # missing 'crush_weight' key + (1, dict(nodes=[dict(id=1)]), -1.0), + # id != osd_id + (1, dict(nodes=[dict(id=999, crush_weight=1)]), -1.0), + # valid + (1, dict(nodes=[dict(id=1, crush_weight=1)]), float(1)), + ] + ) + def test_get_osd_weight(self, osdsf, osd_id, osd_df, expected): + with mock.patch("osd_support.module.OSDSupport.osd_df", return_value=osd_df): + assert osdsf.get_osd_weight(osd_id) == expected + + @pytest.mark.parametrize( + "osd_id, initial_osd_weight, mon_cmd_return, weight, expected", + [ + # is already weighted correctly + (1, 1.0, (0, '', ''), 1.0, True), + # needs reweight, no errors in mon_cmd + (1, 2.0, (0, '', ''), 1.0, True), + # needs reweight, errors in mon_cmd + (1, 2.0, (1, '', ''), 1.0, False), + ] + ) + def test_reweight_osd(self, osdsf, osd_id, initial_osd_weight, mon_cmd_return, weight, expected): + with mock.patch("osd_support.module.OSDSupport.get_osd_weight", return_value=initial_osd_weight),\ + mock.patch("osd_support.module.OSDSupport.mon_command", return_value=mon_cmd_return): + assert osdsf.reweight_osd(osd_id, weight=weight) == expected + + @pytest.mark.parametrize( + "osd_ids, ok_to_stop, expected", + [ + # no osd_ids provided + ({}, [], set()), + # all osds are ok_to_stop + ({1, 2}, [True], {1, 2}), + # osds are ok_to_stop after the second iteration + ({1, 2}, [False, True], {2}), + # osds are never ok_to_stop, (taking the sample size `(len(osd_ids))` into account), + # expected to get a empty set() + ({1, 2}, [False, False], set()), + ] + ) + def test_find_stop_threshold(self, osdsf, osd_ids, ok_to_stop, expected): + with mock.patch("osd_support.module.OSDSupport.ok_to_stop", side_effect=ok_to_stop): + assert osdsf.find_osd_stop_threshold(osd_ids) == expected + + @pytest.mark.parametrize( + "osd_ids, mon_cmd_return, expected", + [ + # ret is 0 + ([1], (0, '', ''), True), + # no input yields True + ([], (0, '', ''), True), + # ret is != 0 + ([1], (-1, '', ''), False), + # no input, but ret != 0 + ([], (-1, '', ''), False), + ] + ) + def test_ok_to_stop(self, osdsf, osd_ids, mon_cmd_return, expected): + with mock.patch("osd_support.module.OSDSupport.mon_command", return_value=mon_cmd_return): + assert osdsf.ok_to_stop(osd_ids) == expected +