charm-ceph-osd/unit_tests/test_actions_osd_out_in.py
Luciano Lo Giudice 93e9885aa7 Only consider mounted OSD directories
When gathering the list of local OSD ids, the charm would consider
the entries under '/var/lib/ceph/osd/ceph-XXX' where 'XXX" was the
OSD id. However, if an entry under that directory isn't mounted,
then the OSD that would represent that entry should be discarded,
as it's no longer active. This patchset thus filters those entries
by looking for them in the mount points.

Closes-Bug: #1934938
Change-Id: I69c6356e450cc0c96de4afe571b438d4a2ea5177
2021-09-02 18:55:18 -03:00

166 lines
5.7 KiB
Python

# Copyright 2016 Canonical Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import mock
import subprocess
import sys
from test_utils import CharmTestCase
sys.path.append('hooks')
import osd_in_out as actions
def mock_check_output(cmd, **kwargs):
action, osd_id = cmd[-2:] # get the last two arguments from cmd
return "marked {} osd.{}. \n".format(action, osd_id).encode("utf-8")
class OSDOutTestCase(CharmTestCase):
def setUp(self):
super(OSDOutTestCase, self).setUp(
actions, ["check_output",
"get_local_osd_ids",
"assess_status",
"parse_osds_arguments",
"function_fail",
"function_set"])
self.check_output.side_effect = mock_check_output
def test_osd_out(self):
self.get_local_osd_ids.return_value = ["5", "6", "7"]
self.parse_osds_arguments.return_value = {"5"}
actions.osd_out()
self.check_output.assert_called_once_with(
["ceph", "--id", "osd-upgrade", "osd", "out", "5"],
stderr=subprocess.STDOUT
)
self.assess_status.assert_called_once_with()
def test_osd_out_all(self):
self.get_local_osd_ids.return_value = ["5", "6", "7"]
self.parse_osds_arguments.return_value = {"all"}
actions.osd_out()
self.check_output.assert_has_calls(
[mock.call(
["ceph", "--id", "osd-upgrade", "osd", "out", i],
stderr=subprocess.STDOUT
) for i in set(["5", "6", "7"])])
self.assess_status.assert_called_once_with()
def test_osd_out_not_local(self):
self.get_local_osd_ids.return_value = ["5"]
self.parse_osds_arguments.return_value = {"6", "7", "8"}
actions.osd_out()
self.check_output.assert_not_called()
self.function_fail.assert_called_once_with(
"invalid ceph OSD device id: "
"{}".format(",".join(set(["6", "7", "8"]))))
self.assess_status.assert_not_called()
class OSDInTestCase(CharmTestCase):
def setUp(self):
super(OSDInTestCase, self).setUp(
actions, ["check_output",
"get_local_osd_ids",
"assess_status",
"parse_osds_arguments",
"function_fail",
"function_set"])
self.check_output.side_effect = mock_check_output
def test_osd_in(self):
self.get_local_osd_ids.return_value = ["5", "6", "7"]
self.parse_osds_arguments.return_value = {"5"}
actions.osd_in()
self.check_output.assert_called_once_with(
["ceph", "--id", "osd-upgrade", "osd", "in", "5"],
stderr=subprocess.STDOUT
)
self.assess_status.assert_called_once_with()
def test_osd_in_all(self):
self.get_local_osd_ids.return_value = ["5", "6", "7"]
self.parse_osds_arguments.return_value = {"all"}
actions.osd_in()
self.check_output.assert_has_calls(
[mock.call(
["ceph", "--id", "osd-upgrade", "osd", "in", i],
stderr=subprocess.STDOUT
) for i in set(["5", "6", "7"])])
self.assess_status.assert_called_once_with()
def test_osd_in_not_local(self):
self.get_local_osd_ids.return_value = ["5"]
self.parse_osds_arguments.return_value = {"6"}
actions.osd_in()
self.check_output.assert_not_called()
self.function_fail.assert_called_once_with(
"invalid ceph OSD device id: 6")
self.assess_status.assert_not_called()
class OSDMountTestCase(CharmTestCase):
def setUp(self):
super(OSDMountTestCase, self).setUp(actions, [])
@mock.patch('os.path.exists')
@mock.patch('os.listdir')
@mock.patch('charms_ceph.utils.filesystem_mounted')
def test_mounted_osds(self, fs_mounted, listdir, exists):
exists.return_value = True
listdir.return_value = [
'/var/lib/ceph/osd/ceph-1', '/var/lib/ceph/osd/ceph-2']
fs_mounted.side_effect = lambda x: x == listdir.return_value[0]
osds = actions.get_local_osd_ids()
self.assertIn(listdir.return_value[0][-1], osds)
self.assertNotIn(listdir.return_value[1][-1], osds)
class MainTestCase(CharmTestCase):
def setUp(self):
super(MainTestCase, self).setUp(actions, ["function_fail"])
def test_invokes_action(self):
dummy_calls = []
def dummy_action():
dummy_calls.append(True)
with mock.patch.dict(actions.ACTIONS, {"foo": dummy_action}):
actions.main(["foo"])
self.assertEqual(dummy_calls, [True])
def test_unknown_action(self):
"""Unknown actions aren't a traceback."""
exit_string = actions.main(["foo"])
self.assertEqual("Action foo undefined", exit_string)
def test_failing_action(self):
"""Actions which traceback trigger function_fail() calls."""
dummy_calls = []
self.function_fail.side_effect = dummy_calls.append
def dummy_action():
raise ValueError("uh oh")
with mock.patch.dict(actions.ACTIONS, {"foo": dummy_action}):
actions.main(["foo"])
self.assertEqual(dummy_calls, ["Action foo failed: uh oh"])