From: Stephan Müller Date: Mon, 27 May 2019 13:10:58 +0000 (+0200) Subject: mgr/dashboard: Pool list shows current r/w byte usage in graph X-Git-Tag: v14.2.3~22^2 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=87618ccd20b413da554f8d55ac12971eb8e06b6f;p=ceph.git mgr/dashboard: Pool list shows current r/w byte usage in graph 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 --- diff --git a/qa/tasks/mgr/dashboard/test_pool.py b/qa/tasks/mgr/dashboard/test_pool.py index 590c35160735..82acd606e09b 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 5ba17640fc04..bb7d86d34819 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 78586e69bbd0..e8dd9fdc0042 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 ea2ea0b215ed..f746bf3866ea 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 4e14dd84fb6f..2c96bab16fd1 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]))