]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
xmgr/dashboard: dashboard-v3: capacity card
authorPedro Gonzalez Gomez <pegonzal@redhat.com>
Mon, 1 Aug 2022 20:17:29 +0000 (22:17 +0200)
committerNizamudeen A <nia@redhat.com>
Tue, 21 Mar 2023 16:07:48 +0000 (21:37 +0530)
tracker: https://tracker.ceph.com/issues/57862
Signed-off-by: Pedro Gonzalez Gomez <pegonzal@redhat.com>
mgr/dashboard: fix openapi tox error

Signed-off-by: Pedro Gonzalez Gomez <pegonzal@redhat.com>
mgr/dashboard: capacity-card set interval and display bytes used

Signed-off-by: Pedro Gonzalez Gomez <pegonzal@redhat.com>
mgr/dashboard: Landing Page v3

Signed-off-by: Pedro Gonzalez Gomez <pegonzal@redhat.com>
(cherry picked from commit 95e20d27b3d0e861ff572481e9426f09e386c6d8)

12 files changed:
src/pybind/mgr/dashboard/controllers/cluster.py
src/pybind/mgr/dashboard/frontend/src/app/ceph/new-dashboard/dashboard-pie/dashboard-pie.component.html [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/ceph/new-dashboard/dashboard-pie/dashboard-pie.component.scss [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/ceph/new-dashboard/dashboard-pie/dashboard-pie.component.spec.ts [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/ceph/new-dashboard/dashboard.module.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/new-dashboard/dashboard/dashboard.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/new-dashboard/dashboard/dashboard.component.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/new-dashboard/dashboard/dashboard.component.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/api/cluster.service.ts
src/pybind/mgr/dashboard/frontend/src/styles/defaults/_bootstrap-defaults.scss
src/pybind/mgr/dashboard/openapi.yaml
src/pybind/mgr/dashboard/services/cluster.py

index d8170e672e9929fc7a80b381f352cc603cf4eb56..5d776e063513ddea8a7c4fbbaf58829410c2e224 100644 (file)
@@ -19,3 +19,7 @@ class Cluster(RESTController):
                  parameters={'status': (str, 'Cluster Status')})
     def singleton_set(self, status: str):
         ClusterModel(status).to_db()
+
+    @RESTController.Collection('GET', 'capacity')
+    def get_capacity(self):
+        return ClusterModel.get_capacity()
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/new-dashboard/dashboard-pie/dashboard-pie.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/new-dashboard/dashboard-pie/dashboard-pie.component.html
new file mode 100644 (file)
index 0000000..ba8176b
--- /dev/null
@@ -0,0 +1,16 @@
+<div class="chart-container">
+  <canvas baseChart
+          #chartCanvas
+          [datasets]="chartConfig.dataset"
+          [chartType]="chartConfig.chartType"
+          [options]="chartConfig.options"
+          [labels]="chartConfig.labels"
+          [colors]="chartConfig.colors"
+          [plugins]="doughnutChartPlugins"
+          class="chart-canvas">
+  </canvas>
+  <div class="chartjs-tooltip"
+       #chartTooltip>
+    <table></table>
+  </div>
+</div>
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/new-dashboard/dashboard-pie/dashboard-pie.component.scss b/src/pybind/mgr/dashboard/frontend/src/app/ceph/new-dashboard/dashboard-pie/dashboard-pie.component.scss
new file mode 100644 (file)
index 0000000..64e7a98
--- /dev/null
@@ -0,0 +1,22 @@
+@use './src/styles/chart-tooltip';
+
+$canvas-width: 100%;
+$canvas-height: 100%;
+
+.chart-container {
+  height: $canvas-height;
+  margin-left: auto;
+  margin-right: auto;
+  position: unset;
+  width: $canvas-width;
+}
+
+.chart-canvas {
+  height: $canvas-height;
+  margin-left: auto;
+  margin-right: auto;
+  max-height: $canvas-height;
+  max-width: $canvas-width;
+  position: unset;
+  width: $canvas-width;
+}
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/new-dashboard/dashboard-pie/dashboard-pie.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/new-dashboard/dashboard-pie/dashboard-pie.component.spec.ts
new file mode 100644 (file)
index 0000000..892913d
--- /dev/null
@@ -0,0 +1,27 @@
+import { NO_ERRORS_SCHEMA } from '@angular/core';
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { CssHelper } from '~/app/shared/classes/css-helper';
+import { DimlessBinaryPipe } from '~/app/shared/pipes/dimless-binary.pipe';
+import { configureTestBed } from '~/testing/unit-test-helper';
+import { DashboardPieComponent } from './dashboard-pie.component';
+
+describe('DashboardPieComponent', () => {
+  let component: DashboardPieComponent;
+  let fixture: ComponentFixture<DashboardPieComponent>;
+
+  configureTestBed({
+    schemas: [NO_ERRORS_SCHEMA],
+    declarations: [DashboardPieComponent],
+    providers: [CssHelper, DimlessBinaryPipe]
+  });
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(DashboardPieComponent);
+    component = fixture.componentInstance;
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
index 27ed0f2d760549c02101fe6bf6295ac1ddae4554..34d41ddb31adbf1cb23e5db3daf13173f82eabf0 100644 (file)
@@ -9,6 +9,7 @@ import { ChartsModule } from 'ng2-charts';
 import { SharedModule } from '~/app/shared/shared.module';
 import { CephSharedModule } from '../shared/ceph-shared.module';
 import { CardComponent } from './card/card.component';
+import { DashboardPieComponent } from './dashboard-pie/dashboard-pie.component';
 import { DashboardComponent } from './dashboard/dashboard.component';
 
 @NgModule({
@@ -24,6 +25,6 @@ import { DashboardComponent } from './dashboard/dashboard.component';
     ReactiveFormsModule
   ],
 
-  declarations: [DashboardComponent, CardComponent]
+  declarations: [DashboardComponent, CardComponent, DashboardPieComponent]
 })
 export class NewDashboardModule {}
index 8065bb860c6bad912fa2b4f4ea018889c76bfa00..b910f2f856b79086f8f42230fd2b495c78d5ba38 100644 (file)
              class="col-sm-3 px-3"
              [ngClass]="{'d-flex': flexHeight}">
       <ng-container class="ms-4 me-4"
-                    *ngIf="{osdSettings: osdSettings$ | async, capacity: capacity$ | async} as values">
-        <ng-container *ngIf="values.osdSettings && values.capacity">
-          <cd-dashboard-pie [data]="{max: values.capacity.total_bytes, current: values.capacity.total_used_raw_bytes}"
-                            [lowThreshold]="values.osdSettings.nearfull_ratio"
-                            [highThreshold]="values.osdSettings.full_ratio">
-          </cd-dashboard-pie>
-        </ng-container>
+                    *ngIf="capacity && osdSettings">
+        <cd-dashboard-pie [data]="{max: capacity.total_bytes, current: capacity.total_used_raw_bytes}"
+                          [lowThreshold]="osdSettings.nearfull_ratio"
+                          [highThreshold]="osdSettings.full_ratio">
+        </cd-dashboard-pie>
       </ng-container>
     </cd-card>
   </div>
index 8e981a933cc1b52edc76f36eab46e0553963a327..113ac8cfe956fbbd55921ad8d183611f58a1f7e0 100644 (file)
@@ -1,13 +1,18 @@
 import { HttpClientTestingModule } from '@angular/common/http/testing';
+import { NO_ERRORS_SCHEMA } from '@angular/core';
 import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { RouterTestingModule } from '@angular/router/testing';
 
 import { BehaviorSubject, of } from 'rxjs';
 
 import { ConfigurationService } from '~/app/shared/api/configuration.service';
 import { MgrModuleService } from '~/app/shared/api/mgr-module.service';
+import { CssHelper } from '~/app/shared/classes/css-helper';
+import { DimlessBinaryPipe } from '~/app/shared/pipes/dimless-binary.pipe';
 import { SummaryService } from '~/app/shared/services/summary.service';
 import { configureTestBed } from '~/testing/unit-test-helper';
 import { CardComponent } from '../card/card.component';
+import { DashboardPieComponent } from '../dashboard-pie/dashboard-pie.component';
 import { DashboardComponent } from './dashboard.component';
 
 export class SummaryServiceMock {
@@ -47,9 +52,14 @@ describe('CardComponent', () => {
   };
 
   configureTestBed({
-    imports: [HttpClientTestingModule],
-    declarations: [DashboardComponent, CardComponent],
-    providers: [{ provide: SummaryService, useClass: SummaryServiceMock }]
+    imports: [RouterTestingModule, HttpClientTestingModule],
+    declarations: [DashboardComponent, CardComponent, DashboardPieComponent],
+    schemas: [NO_ERRORS_SCHEMA],
+    providers: [
+      CssHelper,
+      DimlessBinaryPipe,
+      { provide: SummaryService, useClass: SummaryServiceMock }
+    ]
   });
 
   beforeEach(() => {
index a43b319e00ad68f51fa6748856e433af3b572d9d..1864c0e729a24335b46aef6fbcba0dbfde5b148f 100644 (file)
@@ -1,8 +1,20 @@
-import { Component, OnInit } from '@angular/core';
+import { Component, OnDestroy, OnInit } from '@angular/core';
 
+import _ from 'lodash';
+import { Observable, Subscription } from 'rxjs';
+import { take } from 'rxjs/operators';
+
+import { ClusterService } from '~/app/shared/api/cluster.service';
 import { ConfigurationService } from '~/app/shared/api/configuration.service';
 import { MgrModuleService } from '~/app/shared/api/mgr-module.service';
+import { OsdService } from '~/app/shared/api/osd.service';
 import { DashboardDetails } from '~/app/shared/models/cd-details';
+import { Permissions } from '~/app/shared/models/permissions';
+import { AuthStorageService } from '~/app/shared/services/auth-storage.service';
+import {
+  FeatureTogglesMap$,
+  FeatureTogglesService
+} from '~/app/shared/services/feature-toggles.service';
 import { SummaryService } from '~/app/shared/services/summary.service';
 
 @Component({
index 6b435d6ffed1dc15a7c5a4de19e9a2d650a7acfe..f5b8e4d7cc11851852bc8409d78a266724494612 100644 (file)
@@ -24,4 +24,8 @@ export class ClusterService {
       { headers: { Accept: 'application/vnd.ceph.api.v0.1+json' } }
     );
   }
+
+  getCapacity() {
+    return this.http.get(`${this.baseURL}/capacity`, {});
+  }
 }
index 941f639a363c14a022aa109a3fe0959b53df1977..f871b4ff80f8ed6f84e87405925f418536761645 100644 (file)
@@ -67,7 +67,6 @@ $body-color-bright: $light !default;
 $body-bg: $white !default;
 $body-color: $gray-900 !default;
 $body-bg-alt: $gray-200 !default;
-
 // Health colors.
 $health-color-error: $red !default;
 $health-color-healthy: $green !default;
@@ -82,7 +81,12 @@ $chart-color-yellow: #f6d173 !default;
 $chart-color-green: $green !default;
 $chart-color-gray: #ededed !default;
 $chart-color-cyan: $primary-500 !default;
+$chart-color-light-gray: #f0f0f0 !default;
+$chart-color-slight-dark-gray: #d7d7d7 !default;
+$chart-color-dark-gray: #afafaf !default;
+$chart-color-cyan: #73c5c5 !default;
 $chart-color-purple: #3c3d99 !default;
+$chart-color-white: #fff !default;
 $chart-color-center-text: #151515 !default;
 $chart-color-center-text-description: #72767b !default;
 $chart-color-tooltip-background: $black !default;
index 288d0beb3df79daa2b2dd2f34e7708e05c541e60..eda3c2925bf23c5a04aa1a44baa83df6865a904a 100644 (file)
@@ -2137,6 +2137,28 @@ paths:
       summary: Update the cluster status
       tags:
       - Cluster
+  /api/cluster/capacity:
+    get:
+      parameters: []
+      responses:
+        '200':
+          content:
+            application/vnd.ceph.api.v1.0+json:
+              type: object
+          description: OK
+        '400':
+          description: Operation exception. Please check the response body for details.
+        '401':
+          description: Unauthenticated access. Please login first.
+        '403':
+          description: Unauthorized access. Please check your permissions.
+        '500':
+          description: Unexpected error. Please check the response body for the stack
+            trace.
+      security:
+      - jwt: []
+      tags:
+      - Cluster
   /api/cluster_conf:
     get:
       parameters: []
index a057f24381f78a406979c88de03651aa39fb473f..fbb00bc7370703c5a3095aa0b470e98fc79fd0e9 100644 (file)
@@ -1,9 +1,16 @@
 # -*- coding: utf-8 -*-
 from enum import Enum
+from typing import NamedTuple
 
 from .. import mgr
 
 
+class ClusterCapacity(NamedTuple):
+    total_avail_bytes: int
+    total_bytes: int
+    total_used_raw_bytes: int
+
+
 class ClusterModel:
 
     class Status(Enum):
@@ -33,3 +40,10 @@ class ClusterModel:
         If the status is not set, assume it is already fully functional.
         """
         return cls(status=mgr.get_store('cluster/status', cls.Status.POST_INSTALLED.name))
+
+    @classmethod
+    def get_capacity(cls) -> ClusterCapacity:
+        df = mgr.get('df')
+        return ClusterCapacity(total_avail_bytes=df['stats']['total_avail_bytes'],
+                               total_bytes=df['stats']['total_bytes'],
+                               total_used_raw_bytes=df['stats']['total_used_raw_bytes'])._asdict()