From 87618ccd20b413da554f8d55ac12971eb8e06b6f Mon Sep 17 00:00:00 2001 From: =?utf8?q?Stephan=20M=C3=BCller?= Date: Mon, 27 May 2019 15:10:58 +0200 Subject: [PATCH] mgr/dashboard: Pool list shows current r/w byte usage in graph MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Now the pool list will show the current read and write byte usage in the sparkline graph like it is already done in the OSD listing. Fixes: https://tracker.ceph.com/issues/39650 Signed-off-by: Stephan Müller (cherry picked from commit 2f18cf577033f8a4f3e0115b1eb86240c084fc98) Conflicts (trivial): src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-list/pool-list.component.spec.ts --- qa/tasks/mgr/dashboard/test_pool.py | 2 +- .../pool-list/pool-list.component.spec.ts | 51 +++++++++++++------ .../pool/pool-list/pool-list.component.ts | 14 +++-- .../frontend/src/app/ceph/pool/pool-stat.ts | 2 +- .../mgr/dashboard/services/ceph_service.py | 30 ++++++++--- 5 files changed, 71 insertions(+), 28 deletions(-) diff --git a/qa/tasks/mgr/dashboard/test_pool.py b/qa/tasks/mgr/dashboard/test_pool.py index 590c351607352..82acd606e09b1 100644 --- a/qa/tasks/mgr/dashboard/test_pool.py +++ b/qa/tasks/mgr/dashboard/test_pool.py @@ -25,7 +25,7 @@ class PoolTest(DashboardTestCase): pool_list_stat_schema = JObj(sub_elems={ 'latest': int, 'rate': float, - 'series': JList(JAny(none=False)), + 'rates': JList(JAny(none=False)), }) pool_list_stats_schema = JObj(sub_elems={ diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-list/pool-list.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-list/pool-list.component.spec.ts index 5ba17640fc04a..bb7d86d348198 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-list/pool-list.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-list/pool-list.component.spec.ts @@ -2,6 +2,7 @@ import { HttpClientTestingModule } from '@angular/common/http/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { RouterTestingModule } from '@angular/router/testing'; +import * as _ from 'lodash'; import { BsModalService } from 'ngx-bootstrap/modal'; import { TabsModule } from 'ngx-bootstrap/tabs'; import { ToastrModule } from 'ngx-toastr'; @@ -257,14 +258,34 @@ describe('PoolListComponent', () => { }); }); + describe('custom row comparators', () => { + const expectCorrectComparator = (statsAttribute: string) => { + const mockPool = (v) => ({ stats: { [statsAttribute]: { latest: v } } }); + const columnDefinition = _.find( + component.columns, + (column) => column.prop === `stats.${statsAttribute}.rates` + ); + expect(columnDefinition.comparator(undefined, undefined, mockPool(2), mockPool(1))).toBe(1); + expect(columnDefinition.comparator(undefined, undefined, mockPool(1), mockPool(2))).toBe(-1); + }; + + it('compares read bytes correctly', () => { + expectCorrectComparator('rd_bytes'); + }); + + it('compares write bytes correctly', () => { + expectCorrectComparator('wr_bytes'); + }); + }); + describe('transformPoolsData', () => { it('transforms pools data correctly', () => { const pools = [ { stats: { - bytes_used: { latest: 5, rate: 0, series: [] }, - max_avail: { latest: 15, rate: 0, series: [] }, - rd_bytes: { latest: 6, rate: 4, series: [[0, 2], [1, 6]] } + bytes_used: { latest: 5, rate: 0, rates: [] }, + max_avail: { latest: 15, rate: 0, rates: [] }, + rd_bytes: { latest: 6, rate: 4, rates: [[0, 2], [1, 6]] } }, pg_status: { 'active+clean': 8, down: 2 } } @@ -274,12 +295,12 @@ describe('PoolListComponent', () => { cdIsBinary: true, pg_status: '8 active+clean, 2 down', stats: { - bytes_used: { latest: 5, rate: 0, series: [] }, - max_avail: { latest: 15, rate: 0, series: [] }, - rd: { latest: 0, rate: 0, series: [] }, - rd_bytes: { latest: 6, rate: 4, series: [2, 6] }, - wr: { latest: 0, rate: 0, series: [] }, - wr_bytes: { latest: 0, rate: 0, series: [] } + bytes_used: { latest: 5, rate: 0, rates: [] }, + max_avail: { latest: 15, rate: 0, rates: [] }, + rd: { latest: 0, rate: 0, rates: [] }, + rd_bytes: { latest: 6, rate: 4, rates: [2, 6] }, + wr: { latest: 0, rate: 0, rates: [] }, + wr_bytes: { latest: 0, rate: 0, rates: [] } }, usage: 0.25 } @@ -294,12 +315,12 @@ describe('PoolListComponent', () => { cdIsBinary: true, pg_status: '', stats: { - bytes_used: { latest: 0, rate: 0, series: [] }, - max_avail: { latest: 0, rate: 0, series: [] }, - rd: { latest: 0, rate: 0, series: [] }, - rd_bytes: { latest: 0, rate: 0, series: [] }, - wr: { latest: 0, rate: 0, series: [] }, - wr_bytes: { latest: 0, rate: 0, series: [] } + bytes_used: { latest: 0, rate: 0, rates: [] }, + max_avail: { latest: 0, rate: 0, rates: [] }, + rd: { latest: 0, rate: 0, rates: [] }, + rd_bytes: { latest: 0, rate: 0, rates: [] }, + wr: { latest: 0, rate: 0, rates: [] }, + wr_bytes: { latest: 0, rate: 0, rates: [] } }, usage: 0 } diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-list/pool-list.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-list/pool-list.component.ts index 78586e69bbd0c..e8dd9fdc00426 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-list/pool-list.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-list/pool-list.component.ts @@ -106,6 +106,8 @@ export class PoolListComponent implements OnInit { } ngOnInit() { + const compare = (prop: string, pool1: Pool, pool2: Pool) => + _.get(pool1, prop) > _.get(pool2, prop) ? 1 : -1; this.columns = [ { prop: 'pool_name', @@ -160,14 +162,18 @@ export class PoolListComponent implements OnInit { flexGrow: 3 }, { - prop: 'stats.rd_bytes.series', + prop: 'stats.rd_bytes.rates', name: this.i18n('Read bytes'), + comparator: (_valueA, _valueB, rowA: Pool, rowB: Pool) => + compare('stats.rd_bytes.latest', rowA, rowB), cellTransformation: CellTemplate.sparkline, flexGrow: 3 }, { - prop: 'stats.wr_bytes.series', + prop: 'stats.wr_bytes.rates', name: this.i18n('Write bytes'), + comparator: (_valueA, _valueB, rowA: Pool, rowB: Pool) => + compare('stats.wr_bytes.latest', rowA, rowB), cellTransformation: CellTemplate.sparkline, flexGrow: 3 }, @@ -229,7 +235,7 @@ export class PoolListComponent implements OnInit { transformPoolsData(pools: any) { const requiredStats = ['bytes_used', 'max_avail', 'rd_bytes', 'wr_bytes', 'rd', 'wr']; - const emptyStat = { latest: 0, rate: 0, series: [] }; + const emptyStat = { latest: 0, rate: 0, rates: [] }; _.forEach(pools, (pool: Pool) => { pool['pg_status'] = this.transformPgStatus(pool['pg_status']); @@ -242,7 +248,7 @@ export class PoolListComponent implements OnInit { pool['usage'] = avail > 0 ? stats.bytes_used.latest / avail : avail; ['rd_bytes', 'wr_bytes'].forEach((stat) => { - pool.stats[stat].series = pool.stats[stat].series.map((point) => point[1]); + pool.stats[stat].rates = pool.stats[stat].rates.map((point) => point[1]); }); pool.cdIsBinary = true; }); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-stat.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-stat.ts index ea2ea0b215edf..f746bf3866ea5 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-stat.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-stat.ts @@ -1,7 +1,7 @@ export class PoolStat { latest: number; rate: number; - series: number[]; + rates: number[]; } export class PoolStats { diff --git a/src/pybind/mgr/dashboard/services/ceph_service.py b/src/pybind/mgr/dashboard/services/ceph_service.py index 4e14dd84fb6ff..2c96bab16fd10 100644 --- a/src/pybind/mgr/dashboard/services/ceph_service.py +++ b/src/pybind/mgr/dashboard/services/ceph_service.py @@ -121,7 +121,7 @@ class CephService(object): s[stat_name] = { 'latest': stat_series[0][1], 'rate': get_rate(stat_series), - 'series': [i for i in stat_series] + 'rates': get_rates_from_data(stat_series) } pool['stats'] = s pools_w_stats.append(pool) @@ -179,11 +179,7 @@ class CephService(object): :return: the derivative of mgr.get_counter() :rtype: list[tuple[int, float]]""" data = mgr.get_counter(svc_type, svc_name, path)[path] - if not data: - return [(0, 0.0)] - if len(data) == 1: - return [(data[0][0], 0.0)] - return [(data2[0], differentiate(data1, data2)) for data1, data2 in pairwise(data)] + return get_rates_from_data(data) @classmethod def get_rate(cls, svc_type, svc_name, path): @@ -260,11 +256,31 @@ class CephService(object): } +def get_rates_from_data(data): + """ + >>> get_rates_from_data([]) + [(0, 0.0)] + >>> get_rates_from_data([[1, 42]]) + [(1, 0.0)] + >>> get_rates_from_data([[0, 100], [2, 101], [3, 100], [4, 100]]) + [(2, 0.5), (3, 1.0), (4, 0.0)] + """ + if not data: + return [(0, 0.0)] + if len(data) == 1: + return [(data[0][0], 0.0)] + return [(data2[0], differentiate(data1, data2)) for data1, data2 in pairwise(data)] + + def differentiate(data1, data2): """ >>> times = [0, 2] >>> values = [100, 101] >>> differentiate(*zip(times, values)) 0.5 + >>> times = [0, 2] + >>> values = [100, 99] + >>> differentiate(*zip(times, values)) + 0.5 """ - return (data2[1] - data1[1]) / float(data2[0] - data1[0]) + return abs((data2[1] - data1[1]) / float(data2[0] - data1[0])) -- 2.39.5