From 649b8e8cc5c400130d54fbb515e39f5ea449a84f Mon Sep 17 00:00:00 2001 From: Jason Dillaman Date: Fri, 16 Mar 2018 12:03:15 -0400 Subject: [PATCH] mgr/dashboard: avoid stale path data in block iSCSI status Signed-off-by: Jason Dillaman --- .../mgr/dashboard/controllers/tcmu_iscsi.py | 14 ++++ .../mgr/dashboard/tests/test_tcmu_iscsi.py | 68 ++++++++++++++----- 2 files changed, 66 insertions(+), 16 deletions(-) diff --git a/src/pybind/mgr/dashboard/controllers/tcmu_iscsi.py b/src/pybind/mgr/dashboard/controllers/tcmu_iscsi.py index eb54718f67f..1838b16c2c8 100644 --- a/src/pybind/mgr/dashboard/controllers/tcmu_iscsi.py +++ b/src/pybind/mgr/dashboard/controllers/tcmu_iscsi.py @@ -72,6 +72,7 @@ class TcmuIscsi(RESTController): 'tcmu-runner', service_id, perf_key)[perf_key] or [[0, 0]])[-1][1] / 1000000000 if lock_acquired_time > image.get('optimized_since', 0): + image['optimized_daemon'] = hostname image['optimized_since'] = lock_acquired_time image['stats'] = {} image['stats_history'] = {} @@ -85,6 +86,19 @@ class TcmuIscsi(RESTController): daemon['non_optimized_paths'] += 1 image['non_optimized_paths'].append(hostname) + # clear up races w/ tcmu-runner clients that haven't detected + # loss of optimized path + for image in images.values(): + optimized_daemon = image.get('optimized_daemon', None) + if optimized_daemon: + for daemon_name in image['optimized_paths']: + if daemon_name != optimized_daemon: + daemon = daemons[daemon_name] + daemon['optimized_paths'] -= 1 + daemon['non_optimized_paths'] += 1 + image['non_optimized_paths'].append(daemon_name) + image['optimized_paths'] = [optimized_daemon] + return { 'daemons': sorted(daemons.values(), key=lambda d: d['server_hostname']), 'images': sorted(images.values(), key=lambda i: ['id']), diff --git a/src/pybind/mgr/dashboard/tests/test_tcmu_iscsi.py b/src/pybind/mgr/dashboard/tests/test_tcmu_iscsi.py index 93b97d2509c..645695a1227 100644 --- a/src/pybind/mgr/dashboard/tests/test_tcmu_iscsi.py +++ b/src/pybind/mgr/dashboard/tests/test_tcmu_iscsi.py @@ -8,39 +8,68 @@ from .helper import ControllerTestCase mocked_servers = [{ 'ceph_version': 'ceph version 13.0.0-5083- () mimic (dev)', - 'hostname': 'ceph-dev', - 'services': [{'id': 'a:b', 'type': 'tcmu-runner'}] + 'hostname': 'ceph-dev1', + 'services': [{'id': 'ceph-dev1:pool1/image1', 'type': 'tcmu-runner'}] +}, { + 'ceph_version': 'ceph version 13.0.0-5083- () mimic (dev)', + 'hostname': 'ceph-dev2', + 'services': [{'id': 'ceph-dev2:pool1/image1', 'type': 'tcmu-runner'}] }] -mocked_metadata = { +mocked_metadata1 = { 'ceph_version': 'ceph version 13.0.0-5083- () mimic (dev)', 'pool_name': 'pool1', 'image_name': 'image1', 'image_id': '42', - 'optimized_since': 100.0, + 'optimized_since': 1152121348, +} + +mocked_metadata2 = { + 'ceph_version': 'ceph version 13.0.0-5083- () mimic (dev)', + 'pool_name': 'pool1', + 'image_name': 'image1', + 'image_id': '42', + 'optimized_since': 0, } mocked_get_daemon_status = { 'lock_owner': 'true', } -mocked_get_counter = { - 'librbd-42-pool1-image1.lock_acquired_time': [[10000.0, 10000.0]], +mocked_get_counter1 = { + 'librbd-42-pool1-image1.lock_acquired_time': [[1152121348 * 1000000000, + 1152121348 * 1000000000]], 'librbd-42-pool1-image1.rd': [[0, 0], [1, 43]], 'librbd-42-pool1-image1.wr': [[0, 0], [1, 44]], 'librbd-42-pool1-image1.rd_bytes': [[0, 0], [1, 45]], 'librbd-42-pool1-image1.wr_bytes': [[0, 0], [1, 46]], } +mocked_get_counter2 = { + 'librbd-42-pool1-image1.lock_acquired_time': [[0, 0]], + 'librbd-42-pool1-image1.rd': [], + 'librbd-42-pool1-image1.wr': [], + 'librbd-42-pool1-image1.rd_bytes': [], + 'librbd-42-pool1-image1.wr_bytes': [], +} + + +def _get_counter(_daemon_type, daemon_name, _stat): + if daemon_name == 'ceph-dev1:pool1/image1': + return mocked_get_counter1 + elif daemon_name == 'ceph-dev2:pool1/image1': + return mocked_get_counter2 + return Exception('invalid daemon name') + class TcmuIscsiControllerTest(ControllerTestCase): @classmethod def setup_server(cls): mgr.list_servers.return_value = mocked_servers - mgr.get_metadata.return_value = mocked_metadata + mgr.get_metadata.side_effect = [mocked_metadata1, mocked_metadata2] mgr.get_daemon_status.return_value = mocked_get_daemon_status - mgr.get_counter.return_value = mocked_get_counter + mgr.get_counter.side_effect = _get_counter mgr.url_prefix = '' TcmuIscsi._cp_config['tools.authenticate.on'] = False # pylint: disable=protected-access @@ -50,17 +79,24 @@ class TcmuIscsiControllerTest(ControllerTestCase): self._get('/api/test/tcmu') self.assertStatus(200) self.assertJsonBody({ - 'daemons': [{ - 'server_hostname': 'ceph-dev', - 'version': 'ceph version 13.0.0-5083- () mimic (dev)', - 'optimized_paths': 1, 'non_optimized_paths': 0}], + 'daemons': [ + { + 'server_hostname': 'ceph-dev1', + 'version': 'ceph version 13.0.0-5083- () mimic (dev)', + 'optimized_paths': 1, 'non_optimized_paths': 0}, + { + 'server_hostname': 'ceph-dev2', + 'version': 'ceph version 13.0.0-5083- () mimic (dev)', + 'optimized_paths': 0, 'non_optimized_paths': 1}], 'images': [{ - 'device_id': 'b', + 'device_id': 'pool1/image1', 'pool_name': 'pool1', 'name': 'image1', - 'id': '42', 'optimized_paths': ['ceph-dev'], - 'non_optimized_paths': [], - 'optimized_since': 1e-05, + 'id': '42', + 'optimized_paths': ['ceph-dev1'], + 'non_optimized_paths': ['ceph-dev2'], + 'optimized_daemon': 'ceph-dev1', + 'optimized_since': 1152121348, 'stats': { 'rd': 43.0, 'wr': 44.0, -- 2.39.5