mgr/dashboard: add button toggle to switch between new and old landing page

Signed-off-by: Pedro Gonzalez Gomez <pegonzal@redhat.com>
This commit is contained in:
Pedro Gonzalez Gomez 2023-02-14 11:21:57 +01:00
parent 313921afcf
commit 21dc93ae11
45 changed files with 109 additions and 98 deletions

BIN
doc/mgr/capacity-card.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 KiB

View File

@ -127,62 +127,67 @@ The Ceph Dashboard offers the following monitoring and management capabilities:
Overview of the Dashboard Landing Page
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Displays overall cluster status, performance, and capacity metrics. Shows instant
feedback for changes in the cluster and provides easy access to subpages of the
dashboard.
The landing page of Ceph Dashboard serves as the home page and features metrics
such as the overall cluster status, performance, and capacity. It provides real-time
updates on any changes in the cluster and allows quick access to other sections of the dashboard.
.. image:: dashboard-landing-page.png
.. note::
You can change the landing page to the previous version from:
``Cluster >> Manager Modules >> Dashboard >> Edit``.
Editing the ``FEATURE_TOGGLE_DASHBOARD`` option will change the landing page, from one view to another.
Note that the previous version of the landing page will be disabled in future releases.
.. _dashboard-landing-page-details:
Details
"""""""
Provides an overview of the cluster configuration, displaying various critical aspects of the cluster.
.. image:: details-card.png
.. _dashboard-landing-page-status:
Status
""""""
Provides a visual indication of cluster health, and displays cluster alerts grouped by severity.
* **Cluster Status**: Displays overall cluster health. In case of any error it
displays a short description of the error and provides a link to the logs.
* **Hosts**: Displays the total number of hosts associated to the cluster and
links to a subpage that lists and describes each.
* **Monitors**: Displays mons and their quorum status and
open sessions. Links to a subpage that lists and describes each.
* **OSDs**: Displays object storage daemons (ceph-osds) and
the numbers of OSDs running (up), in service
(in), and out of the cluster (out). Provides links to
subpages providing a list of all OSDs and related management actions.
* **Managers**: Displays active and standby Ceph Manager
daemons (ceph-mgr).
* **Object Gateway**: Displays active object gateways (RGWs) and
provides links to subpages that list all object gateway daemons.
* **Metadata Servers**: Displays active and standby CephFS metadata
service daemons (ceph-mds).
* **iSCSI Gateways**: Display iSCSI gateways available,
active (up), and inactive (down). Provides a link to a subpage
showing a list of all iSCSI Gateways.
.. image:: status-card-open.png
.. _dashboard-landing-page-capacity:
Capacity
""""""""
* **Used**: Displays the used capacity out of the total physical capacity providedd by storage nodes (OSDs)
* **Warning**: Displays the `nearfull` threshold of the OSDs
* **Danger**: Displays the `full` threshold of the OSDs
* **Raw Capacity**: Displays the capacity used out of the total
physical capacity provided by storage nodes (OSDs).
* **Objects**: Displays the number and status of RADOS objects
including the percentages of healthy, misplaced, degraded, and unfound
objects.
* **PG Status**: Displays the total number of placement groups and
their status, including the percentage clean, working,
warning, and unknown.
* **Pools**: Displays pools and links to a subpage listing details.
* **PGs per OSD**: Displays the number of placement groups assigned to
object storage daemons.
.. image:: capacity-card.png
.. _dashboard-landing-page-inventory:
Inventory
"""""""""
An inventory for all assets within the cluster.
Provides direct access to subpages of the dashboard from each item of this card.
.. image:: inventory-card.png
.. _dashboard-landing-page-performance:
Performance
"""""""""""
Cluster Utilization
"""""""""""""""""""
* **Used Capacity**: Total capacity used of the cluster. The maximum value of the chart is the maximum capacity of the cluster.
* **IOPS (Input/Output Operations Per Second)**: Number of read and write operations.
* **Latency**: Amount of time that it takes to process a read or a write request.
* **Client Throughput**: Amount of data that clients read or write to the cluster.
* **Recovery Throughput**: Amount of recovery data that clients read or write to the cluster.
* **Client READ/Write**: Displays an overview of
client input and output operations.
* **Client Throughput**: Displays the data transfer rates to and from Ceph clients.
* **Recovery throughput**: Displays rate of cluster healing and balancing operations.
* **Scrubbing**: Displays light and deep scrub status.
.. image:: cluster-utilization-card.png
Supported Browsers
^^^^^^^^^^^^^^^^^^

BIN
doc/mgr/details-card.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
doc/mgr/inventory-card.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

View File

@ -24,8 +24,7 @@ import { SilenceListComponent } from './ceph/cluster/prometheus/silence-list/sil
import { ServiceFormComponent } from './ceph/cluster/services/service-form/service-form.component';
import { ServicesComponent } from './ceph/cluster/services/services.component';
import { TelemetryComponent } from './ceph/cluster/telemetry/telemetry.component';
import { DeprecatedDashboardComponent } from './ceph/dashboard/dashboard/dashboard.component';
import { DashboardComponent } from './ceph/new-dashboard/dashboard/dashboard.component';
import { DashboardComponent } from './ceph/dashboard/dashboard/dashboard.component';
import { NfsFormComponent } from './ceph/nfs/nfs-form/nfs-form.component';
import { NfsListComponent } from './ceph/nfs/nfs-list/nfs-list.component';
import { PerformanceCounterComponent } from './ceph/performance-counter/performance-counter/performance-counter.component';
@ -89,8 +88,7 @@ const routes: Routes = [
canActivate: [AuthGuardService, ChangePasswordGuardService],
canActivateChild: [AuthGuardService, ChangePasswordGuardService],
children: [
{ path: 'dashboard', component: DeprecatedDashboardComponent },
{ path: 'dashboard_3', component: DashboardComponent },
{ path: 'dashboard', component: DashboardComponent },
{ path: 'error', component: ErrorComponent },
// Cluster

View File

@ -5,7 +5,6 @@ import { SharedModule } from '../shared/shared.module';
import { CephfsModule } from './cephfs/cephfs.module';
import { ClusterModule } from './cluster/cluster.module';
import { DashboardModule } from './dashboard/dashboard.module';
import { NewDashboardModule } from './new-dashboard/dashboard.module';
import { NfsModule } from './nfs/nfs.module';
import { PerformanceCounterModule } from './performance-counter/performance-counter.module';
@ -14,7 +13,6 @@ import { PerformanceCounterModule } from './performance-counter/performance-coun
CommonModule,
ClusterModule,
DashboardModule,
NewDashboardModule,
PerformanceCounterModule,
CephfsModule,
NfsModule,

View File

@ -13,7 +13,7 @@ import { CardComponent } from './card/card.component';
import { DashboardAreaChartComponent } from './dashboard-area-chart/dashboard-area-chart.component';
import { DashboardPieComponent } from './dashboard-pie/dashboard-pie.component';
import { DashboardTimeSelectorComponent } from './dashboard-time-selector/dashboard-time-selector.component';
import { DashboardComponent } from './dashboard/dashboard.component';
import { DashboardV3Component } from './dashboard/dashboard-v3.component';
import { CardRowComponent } from './card-row/card-row.component';
import { PgSummaryPipe } from './pg-summary.pipe';
@ -33,13 +33,15 @@ import { PgSummaryPipe } from './pg-summary.pipe';
],
declarations: [
DashboardComponent,
DashboardV3Component,
CardComponent,
DashboardPieComponent,
CardRowComponent,
PgSummaryPipe,
DashboardAreaChartComponent,
DashboardTimeSelectorComponent
]
],
exports: [DashboardV3Component]
})
export class NewDashboardModule {}
export class DashboardV3Module {}

View File

@ -24,7 +24,7 @@ import { CardRowComponent } from '../card-row/card-row.component';
import { CardComponent } from '../card/card.component';
import { DashboardPieComponent } from '../dashboard-pie/dashboard-pie.component';
import { PgSummaryPipe } from '../pg-summary.pipe';
import { DashboardComponent } from './dashboard.component';
import { DashboardV3Component } from './dashboard-v3.component';
export class SummaryServiceMock {
summaryDataSource = new BehaviorSubject({
@ -40,8 +40,8 @@ export class SummaryServiceMock {
}
describe('Dashbord Component', () => {
let component: DashboardComponent;
let fixture: ComponentFixture<DashboardComponent>;
let component: DashboardV3Component;
let fixture: ComponentFixture<DashboardV3Component>;
let configurationService: ConfigurationService;
let orchestratorService: MgrModuleService;
let getHealthSpy: jasmine.Spy;
@ -153,7 +153,7 @@ describe('Dashbord Component', () => {
configureTestBed({
imports: [RouterTestingModule, HttpClientTestingModule, ToastrModule.forRoot(), SharedModule],
declarations: [
DashboardComponent,
DashboardV3Component,
CardComponent,
DashboardPieComponent,
CardRowComponent,
@ -184,7 +184,7 @@ describe('Dashbord Component', () => {
rgw: true
})
);
fixture = TestBed.createComponent(DashboardComponent);
fixture = TestBed.createComponent(DashboardV3Component);
component = fixture.componentInstance;
configurationService = TestBed.inject(ConfigurationService);
orchestratorService = TestBed.inject(MgrModuleService);
@ -306,7 +306,7 @@ describe('Dashbord Component', () => {
rgw: false
})
);
fixture = TestBed.createComponent(DashboardComponent);
fixture = TestBed.createComponent(DashboardV3Component);
component = fixture.componentInstance;
});

View File

@ -27,11 +27,11 @@ import { PrometheusListHelper } from '~/app/shared/helpers/prometheus-list-helpe
import { PrometheusAlertService } from '~/app/shared/services/prometheus-alert.service';
@Component({
selector: 'cd-dashboard',
templateUrl: './dashboard.component.html',
styleUrls: ['./dashboard.component.scss']
selector: 'cd-dashboard-v3',
templateUrl: './dashboard-v3.component.html',
styleUrls: ['./dashboard-v3.component.scss']
})
export class DashboardComponent extends PrometheusListHelper implements OnInit, OnDestroy {
export class DashboardV3Component extends PrometheusListHelper implements OnInit, OnDestroy {
detailsCardData: DashboardDetails = {};
osdSettingsService: any;
osdSettings: any;
@ -105,6 +105,9 @@ export class DashboardComponent extends PrometheusListHelper implements OnInit,
ngOnDestroy() {
this.interval.unsubscribe();
if (this.timerGetPrometheusDataSub) {
this.timerGetPrometheusDataSub.unsubscribe();
}
}
getHealth() {

View File

@ -7,9 +7,10 @@ import { NgbNavModule, NgbPopoverModule } from '@ng-bootstrap/ng-bootstrap';
import { ChartsModule } from 'ng2-charts';
import { SharedModule } from '~/app/shared/shared.module';
import { DashboardV3Module } from '../dashboard-v3/dashboard-v3.module';
import { CephSharedModule } from '../shared/ceph-shared.module';
import { FeedbackComponent } from '../shared/feedback/feedback.component';
import { DeprecatedDashboardComponent } from './dashboard/dashboard.component';
import { DashboardComponent } from './dashboard/dashboard.component';
import { HealthPieComponent } from './health-pie/health-pie.component';
import { HealthComponent } from './health/health.component';
import { InfoCardComponent } from './info-card/info-card.component';
@ -29,12 +30,13 @@ import { OsdSummaryPipe } from './osd-summary.pipe';
RouterModule,
NgbPopoverModule,
FormsModule,
ReactiveFormsModule
ReactiveFormsModule,
DashboardV3Module
],
declarations: [
HealthComponent,
DeprecatedDashboardComponent,
DashboardComponent,
MonSummaryPipe,
OsdSummaryPipe,
MgrSummaryPipe,

View File

@ -1,32 +1,15 @@
<main aria-label="Dashboard" >
<main aria-label="Dashboard">
<a href="#main"
class="sr-only">skip to content</a>
<cd-refresh-selector></cd-refresh-selector>
<ng-container *ngIf="(enabledFeature$ | async)?.dashboard === false; else dashboardV3"
class="main-padding">
<cd-refresh-selector></cd-refresh-selector>
<ng-container *ngIf="hasGrafana">
<nav ngbNav
#nav="ngbNav"
class="nav-tabs">
<ng-container ngbNavItem>
<a ngbNavLink
i18n>Health</a>
<ng-template ngbNavContent>
<cd-health></cd-health>
</ng-template>
</ng-container>
<ng-container ngbNavItem>
<a ngbNavLink
i18n>Statistics</a>
<ng-template ngbNavContent>
</ng-template>
</ng-container>
</nav>
<div [ngbNavOutlet]="nav"></div>
<cd-health id="main"></cd-health>
</ng-container>
<cd-health id="main"
*ngIf="!hasGrafana"></cd-health>
<ng-template #dashboardV3>
<cd-dashboard-v3></cd-dashboard-v3>
</ng-template>
</main>

View File

@ -1,3 +1,3 @@
main {
main:has(cd-health) {
padding-top: 20px;
}

View File

@ -1,23 +1,26 @@
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap';
import { FeatureTogglesService } from '~/app/shared/services/feature-toggles.service';
import { configureTestBed } from '~/testing/unit-test-helper';
import { DeprecatedDashboardComponent } from './dashboard.component';
import { DashboardComponent } from './dashboard.component';
describe('DashboardComponent', () => {
let component: DeprecatedDashboardComponent;
let fixture: ComponentFixture<DeprecatedDashboardComponent>;
let component: DashboardComponent;
let fixture: ComponentFixture<DashboardComponent>;
configureTestBed({
imports: [NgbNavModule],
declarations: [DeprecatedDashboardComponent],
imports: [NgbNavModule, HttpClientTestingModule],
declarations: [DashboardComponent],
providers: [FeatureTogglesService],
schemas: [NO_ERRORS_SCHEMA]
});
beforeEach(() => {
fixture = TestBed.createComponent(DeprecatedDashboardComponent);
fixture = TestBed.createComponent(DashboardComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

View File

@ -1,10 +1,16 @@
import { Component } from '@angular/core';
import { Observable } from 'rxjs';
import { FeatureTogglesService } from '~/app/shared/services/feature-toggles.service';
@Component({
selector: 'cd-dashboard',
templateUrl: './dashboard.component.html',
styleUrls: ['./dashboard.component.scss']
})
export class DeprecatedDashboardComponent {
hasGrafana = false; // TODO: Temporary var, remove when grafana is implemented
export class DashboardComponent {
enabledFeature$: Observable<Object>;
constructor(private featureToggles: FeatureTogglesService) {
this.enabledFeature$ = this.featureToggles.get();
}
}

View File

@ -100,6 +100,10 @@
color: vv.$gray-200;
}
.no-hover:hover {
background-color: vv.$secondary !important;
}
@media (min-width: vv.$screen-md-min) {
.cd-navbar-utility {
border-bottom: 0;

View File

@ -12,6 +12,7 @@ export class FeatureTogglesMap {
cephfs = true;
rgw = true;
nfs = true;
dashboardV3 = true;
}
export type Features = keyof FeatureTogglesMap;
export type FeatureTogglesMap$ = Observable<FeatureTogglesMap>;

View File

@ -2858,6 +2858,9 @@ paths:
cephfs:
description: ''
type: boolean
dashboard:
description: ''
type: boolean
iscsi:
description: ''
type: boolean
@ -2880,6 +2883,7 @@ paths:
- cephfs
- rgw
- nfs
- dashboard
type: object
description: OK
'400':

View File

@ -25,6 +25,7 @@ class Features(Enum):
CEPHFS = 'cephfs'
RGW = 'rgw'
NFS = 'nfs'
DASHBOARD = 'dashboard'
PREDISABLED_FEATURES = set() # type: Set[str]
@ -140,7 +141,8 @@ class FeatureToggles(I.CanMgr, I.Setupable, I.HasOptions,
"iscsi": (bool, ''),
"cephfs": (bool, ''),
"rgw": (bool, ''),
"nfs": (bool, '')
"nfs": (bool, ''),
"dashboard": (bool, '')
}
@APIRouter('/feature_toggles')