diff options
Diffstat (limited to 'src/pybind/mgr/dashboard/controllers/health.py')
-rw-r--r-- | src/pybind/mgr/dashboard/controllers/health.py | 189 |
1 files changed, 189 insertions, 0 deletions
diff --git a/src/pybind/mgr/dashboard/controllers/health.py b/src/pybind/mgr/dashboard/controllers/health.py new file mode 100644 index 00000000..fab19bcb --- /dev/null +++ b/src/pybind/mgr/dashboard/controllers/health.py @@ -0,0 +1,189 @@ +# -*- coding: utf-8 -*- +from __future__ import absolute_import + +import json + +from . import ApiController, Endpoint, BaseController + +from .. import mgr +from ..security import Permission, Scope +from ..services.ceph_service import CephService +from ..services.iscsi_cli import IscsiGatewaysConfig + + +class HealthData(object): + """ + A class to be used in combination with BaseController to allow either + "full" or "minimal" sets of health data to be collected. + + To function properly, it needs BaseCollector._has_permissions to be passed + in as ``auth_callback``. + """ + + def __init__(self, auth_callback, minimal=True): + self._has_permissions = auth_callback + self._minimal = minimal + + @staticmethod + def _partial_dict(orig, keys): + return {k: orig[k] for k in keys} + + def all_health(self): + result = { + "health": self.basic_health(), + } + + if self._has_permissions(Permission.READ, Scope.MONITOR): + result['mon_status'] = self.mon_status() + + if self._has_permissions(Permission.READ, Scope.CEPHFS): + result['fs_map'] = self.fs_map() + + if self._has_permissions(Permission.READ, Scope.OSD): + result['osd_map'] = self.osd_map() + result['scrub_status'] = self.scrub_status() + result['pg_info'] = self.pg_info() + + if self._has_permissions(Permission.READ, Scope.MANAGER): + result['mgr_map'] = self.mgr_map() + + if self._has_permissions(Permission.READ, Scope.POOL): + result['pools'] = self.pools() + result['df'] = self.df() + result['client_perf'] = self.client_perf() + + if self._has_permissions(Permission.READ, Scope.HOSTS): + result['hosts'] = self.host_count() + + if self._has_permissions(Permission.READ, Scope.RGW): + result['rgw'] = self.rgw_count() + + if self._has_permissions(Permission.READ, Scope.ISCSI): + result['iscsi_daemons'] = self.iscsi_daemons() + + return result + + def basic_health(self): + health_data = mgr.get("health") + health = json.loads(health_data['json']) + + # Transform the `checks` dict into a list for the convenience + # of rendering from javascript. + checks = [] + for k, v in health['checks'].items(): + v['type'] = k + checks.append(v) + + checks = sorted(checks, key=lambda c: c['severity']) + health['checks'] = checks + return health + + def client_perf(self): + result = CephService.get_client_perf() + if self._minimal: + result = self._partial_dict( + result, + ['read_bytes_sec', 'read_op_per_sec', + 'recovering_bytes_per_sec', 'write_bytes_sec', + 'write_op_per_sec'] + ) + return result + + def df(self): + df = mgr.get('df') + + del df['stats_by_class'] + + if self._minimal: + df = dict(stats=self._partial_dict( + df['stats'], + ['total_avail_bytes', 'total_bytes', + 'total_used_raw_bytes'] + )) + return df + + def fs_map(self): + fs_map = mgr.get('fs_map') + if self._minimal: + fs_map = self._partial_dict(fs_map, ['filesystems', 'standbys']) + fs_map['filesystems'] = [self._partial_dict(item, ['mdsmap']) for + item in fs_map['filesystems']] + for fs in fs_map['filesystems']: + mdsmap_info = fs['mdsmap']['info'] + min_mdsmap_info = dict() + for k, v in mdsmap_info.items(): + min_mdsmap_info[k] = self._partial_dict(v, ['state']) + return fs_map + + def host_count(self): + return len(mgr.list_servers()) + + def iscsi_daemons(self): + gateways = IscsiGatewaysConfig.get_gateways_config()['gateways'] + return len(gateways) if gateways else 0 + + def mgr_map(self): + mgr_map = mgr.get('mgr_map') + if self._minimal: + mgr_map = self._partial_dict(mgr_map, ['active_name', 'standbys']) + return mgr_map + + def mon_status(self): + mon_status = json.loads(mgr.get('mon_status')['json']) + if self._minimal: + mon_status = self._partial_dict(mon_status, ['monmap', 'quorum']) + mon_status['monmap'] = self._partial_dict( + mon_status['monmap'], ['mons'] + ) + mon_status['monmap']['mons'] = [{}] * \ + len(mon_status['monmap']['mons']) + return mon_status + + def osd_map(self): + osd_map = mgr.get('osd_map') + assert osd_map is not None + # Not needed, skip the effort of transmitting this to UI + del osd_map['pg_temp'] + if self._minimal: + osd_map = self._partial_dict(osd_map, ['osds']) + osd_map['osds'] = [ + self._partial_dict(item, ['in', 'up']) + for item in osd_map['osds'] + ] + else: + osd_map['tree'] = mgr.get('osd_map_tree') + osd_map['crush'] = mgr.get('osd_map_crush') + osd_map['crush_map_text'] = mgr.get('osd_map_crush_map_text') + osd_map['osd_metadata'] = mgr.get('osd_metadata') + return osd_map + + def pg_info(self): + return CephService.get_pg_info() + + def pools(self): + pools = CephService.get_pool_list_with_stats() + if self._minimal: + pools = [{}] * len(pools) + return pools + + def rgw_count(self): + return len(CephService.get_service_list('rgw')) + + def scrub_status(self): + return CephService.get_scrub_status() + + +@ApiController('/health') +class Health(BaseController): + def __init__(self): + super(Health, self).__init__() + self.health_full = HealthData(self._has_permissions, minimal=False) + self.health_minimal = HealthData(self._has_permissions, minimal=True) + + @Endpoint() + def full(self): + return self.health_full.all_health() + + @Endpoint() + def minimal(self): + return self.health_minimal.all_health() |