From d9cea01476f8b1f3f34c836eb3d4699ebe0001c7 Mon Sep 17 00:00:00 2001 From: Frode Nordahl Date: Mon, 4 Mar 2019 17:44:39 +0100 Subject: [PATCH] Add summary stats for mirrored pools and images in workload status Change-Id: I550473edf7c7253b96fdb323b8b4761049a1de88 --- src/lib/charm/openstack/ceph_rbd_mirror.py | 51 ++++++++++++++++--- ...est_lib_charm_openstack_ceph_rbd_mirror.py | 25 ++++++++- 2 files changed, 69 insertions(+), 7 deletions(-) diff --git a/src/lib/charm/openstack/ceph_rbd_mirror.py b/src/lib/charm/openstack/ceph_rbd_mirror.py index 8ff7f4a..d195c79 100644 --- a/src/lib/charm/openstack/ceph_rbd_mirror.py +++ b/src/lib/charm/openstack/ceph_rbd_mirror.py @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +import collections import json import socket import subprocess @@ -64,12 +65,39 @@ class CephRBDMirrorCharm(charms_openstack.plugins.CephCharm): reactive.is_flag_set('ceph-local.available') and reactive.is_flag_set('ceph-remote.available')): endpoint = reactive.endpoint_from_flag('ceph-local.available') - for pool, attrs in endpoint.pools.items(): - if 'rbd' in attrs['applications']: - status = self.mirror_pool_status(pool) - ch_core.hookenv.log('DEBUG: mirror_pool_status({}) = "{}"' - .format(pool, status), - level=ch_core.hookenv.INFO) + stats = self.mirror_pools_summary( + (pool for pool, attrs in endpoint.pools.items() + if 'rbd' in attrs['applications'])) + ch_core.hookenv.log('mirror_pools_summary = "{}"' + .format(stats), + level=ch_core.hookenv.DEBUG) + status = 'active' + pool_msg = '' + image_msg = '' + for health, count in stats['pool_health'].items(): + if not pool_msg: + pool_msg = 'Pools ' + pool_msg += '{} ({}) '.format(health, count) + if health != 'OK': + status = 'blocked' + for state, count in stats['image_states'].items(): + if not image_msg: + image_msg = 'Images ' + if state == 'stopped': + state_name = 'Primary' + elif state == 'replaying': + state_name = 'Secondary' + else: + state_name = state + image_msg += '{} ({}) '.format(state_name, count) + msg = '' + if pool_msg: + msg = 'Unit is ready ({})'.format( + pool_msg + image_msg.rstrip()) + else: + status = 'waiting' + msg = 'Waiting for pools to be created' + return status, msg return None, None def _mirror_pool_info(self, pool): @@ -93,6 +121,17 @@ class CephRBDMirrorCharm(charms_openstack.plugins.CephCharm): universal_newlines=True) return json.loads(output) + def mirror_pools_summary(self, pools): + stats = {} + stats['pool_health'] = collections.defaultdict(int) + stats['image_states'] = collections.defaultdict(int) + for pool in pools: + pool_stat = self.mirror_pool_status(pool) + stats['pool_health'][pool_stat['summary']['health']] += 1 + for state, value in pool_stat['summary']['states'].items(): + stats['image_states'][state] += value + return stats + def mirror_pool_enable(self, pool): base_cmd = ['rbd', '--id', self.ceph_id, 'mirror', 'pool'] subprocess.check_call(base_cmd + ['enable', pool, 'pool']) diff --git a/unit_tests/test_lib_charm_openstack_ceph_rbd_mirror.py b/unit_tests/test_lib_charm_openstack_ceph_rbd_mirror.py index 8e17ed4..0893387 100644 --- a/unit_tests/test_lib_charm_openstack_ceph_rbd_mirror.py +++ b/unit_tests/test_lib_charm_openstack_ceph_rbd_mirror.py @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +import collections import mock import charms_openstack.test_utils as test_utils @@ -37,9 +38,31 @@ class TestCephRBDMirrorCharm(Helper): self.is_flag_set.return_value = True self.patch_object(ceph_rbd_mirror.reactive, 'endpoint_from_flag') self.assertEqual(crmc.custom_assess_status_check(), - (None, None)) + ('waiting', 'Waiting for pools to be created')) self.endpoint_from_flag.assert_called_once_with( 'ceph-local.available') + crmc.mirror_pools_summary = mock.MagicMock() + crmc.mirror_pools_summary.return_value = collections.OrderedDict({ + 'pool_health': collections.OrderedDict( + {'OK': 1, 'WARN': 1, 'ERROR': 1}), + 'image_states': collections.OrderedDict( + {'stopped': 2, 'replaying': 2}), + }) + result = crmc.custom_assess_status_check() + self.assertTrue('blocked' in result[0]) + # the order of which the statuses appear in the string is undefined + self.assertTrue('OK (1)' in result[1]) + self.assertTrue('WARN (1)' in result[1]) + self.assertTrue('ERROR (1)' in result[1]) + self.assertTrue('Primary (2)' in result[1]) + self.assertTrue('Secondary (2)' in result[1]) + crmc.mirror_pools_summary.return_value = collections.OrderedDict({ + 'pool_health': collections.OrderedDict({'OK': 1}), + 'image_states': collections.OrderedDict({'stopped': 2}), + }) + self.assertEqual(crmc.custom_assess_status_check(), + ('active', 'Unit is ready (Pools OK (1) ' + 'Images Primary (2))')) def test__mirror_pool_info(self): self.patch_object(ceph_rbd_mirror.socket, 'gethostname')