]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/dashboard: Pool list shows current r/w byte usage in graph 29352/head
authorStephan Müller <smueller@suse.com>
Mon, 27 May 2019 13:10:58 +0000 (15:10 +0200)
committerStephan Müller <smueller@suse.com>
Tue, 13 Aug 2019 13:56:12 +0000 (15:56 +0200)
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 <smueller@suse.com>
(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
src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-list/pool-list.component.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-list/pool-list.component.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-stat.ts
src/pybind/mgr/dashboard/services/ceph_service.py

index 590c351607352cde1633ebcf3938406a76e3a89b..82acd606e09b16fac5ca08efc714dd02e8c3021d 100644 (file)
@@ -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={
index 5ba17640fc04a9d9a68a139144c51b63a81e7ec1..bb7d86d348198a124ad9d98dbf0eb3299ac5e2d9 100644 (file)
@@ -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
         }
index 78586e69bbd0c4142885a80536cedd7e0ed60613..e8dd9fdc0042603e5f5e1abc742aac6a4bd9291f 100644 (file)
@@ -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;
     });
index ea2ea0b215edfb06e967f355f85ed1fb19d02d6b..f746bf3866ea52d1c95cadc5246e5c584878baf1 100644 (file)
@@ -1,7 +1,7 @@
 export class PoolStat {
   latest: number;
   rate: number;
-  series: number[];
+  rates: number[];
 }
 
 export class PoolStats {
index 4e14dd84fb6ff4fdffeeb50e14291d8f4ed55940..2c96bab16fd101c73084c73012af014ef58cca44 100644 (file)
@@ -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]))