From 24e5ee11a8c0950dfafc504db6da05a7b9644fd6 Mon Sep 17 00:00:00 2001 From: Tiago Melo Date: Mon, 19 Feb 2018 10:16:03 +0000 Subject: [PATCH] mgr/dashboard_v2: add mirroring page Signed-off-by: Tiago Melo --- .../frontend/src/app/app-routing.module.ts | 2 + .../src/app/ceph/block/block.module.ts | 10 +- .../block/mirror-health-color.pipe.spec.ts | 8 + .../ceph/block/mirror-health-color.pipe.ts | 17 ++ .../block/mirroring/mirroring.component.html | 89 +++++++++++ .../block/mirroring/mirroring.component.scss | 0 .../mirroring/mirroring.component.spec.ts | 50 ++++++ .../block/mirroring/mirroring.component.ts | 146 ++++++++++++++++++ .../navigation/navigation.component.html | 16 +- .../navigation/navigation.component.ts | 10 ++ .../services/rbd-mirroring.service.spec.ts | 18 +++ .../shared/services/rbd-mirroring.service.ts | 11 ++ .../app/shared/services/services.module.ts | 13 +- 13 files changed, 383 insertions(+), 7 deletions(-) create mode 100644 src/pybind/mgr/dashboard_v2/frontend/src/app/ceph/block/mirror-health-color.pipe.spec.ts create mode 100644 src/pybind/mgr/dashboard_v2/frontend/src/app/ceph/block/mirror-health-color.pipe.ts create mode 100644 src/pybind/mgr/dashboard_v2/frontend/src/app/ceph/block/mirroring/mirroring.component.html create mode 100644 src/pybind/mgr/dashboard_v2/frontend/src/app/ceph/block/mirroring/mirroring.component.scss create mode 100644 src/pybind/mgr/dashboard_v2/frontend/src/app/ceph/block/mirroring/mirroring.component.spec.ts create mode 100644 src/pybind/mgr/dashboard_v2/frontend/src/app/ceph/block/mirroring/mirroring.component.ts create mode 100644 src/pybind/mgr/dashboard_v2/frontend/src/app/shared/services/rbd-mirroring.service.spec.ts create mode 100644 src/pybind/mgr/dashboard_v2/frontend/src/app/shared/services/rbd-mirroring.service.ts diff --git a/src/pybind/mgr/dashboard_v2/frontend/src/app/app-routing.module.ts b/src/pybind/mgr/dashboard_v2/frontend/src/app/app-routing.module.ts index 7e7e84106cb63..8883796d367db 100644 --- a/src/pybind/mgr/dashboard_v2/frontend/src/app/app-routing.module.ts +++ b/src/pybind/mgr/dashboard_v2/frontend/src/app/app-routing.module.ts @@ -2,6 +2,7 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { IscsiComponent } from './ceph/block/iscsi/iscsi.component'; +import { MirroringComponent } from './ceph/block/mirroring/mirroring.component'; import { PoolDetailComponent } from './ceph/block/pool-detail/pool-detail.component'; import { CephfsComponent } from './ceph/cephfs/cephfs/cephfs.component'; import { ClientsComponent } from './ceph/cephfs/clients/clients.component'; @@ -40,6 +41,7 @@ const routes: Routes = [ { path: 'cephfs/:id/clients', component: ClientsComponent, canActivate: [AuthGuardService] }, { path: 'cephfs/:id', component: CephfsComponent, canActivate: [AuthGuardService] }, { path: 'configuration', component: ConfigurationComponent, canActivate: [AuthGuardService] }, + { path: 'mirroring', component: MirroringComponent, canActivate: [AuthGuardService] }, { path: '404', component: NotFoundComponent }, { path: 'osd', component: OsdListComponent, canActivate: [AuthGuardService] }, { path: '**', redirectTo: '/404'} diff --git a/src/pybind/mgr/dashboard_v2/frontend/src/app/ceph/block/block.module.ts b/src/pybind/mgr/dashboard_v2/frontend/src/app/ceph/block/block.module.ts index b1c71c52a7cdf..6e094fa04d6ab 100644 --- a/src/pybind/mgr/dashboard_v2/frontend/src/app/ceph/block/block.module.ts +++ b/src/pybind/mgr/dashboard_v2/frontend/src/app/ceph/block/block.module.ts @@ -2,13 +2,16 @@ import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; import { FormsModule } from '@angular/forms'; -import { TabsModule } from 'ngx-bootstrap'; +import { ProgressbarModule } from 'ngx-bootstrap/progressbar'; +import { TabsModule } from 'ngx-bootstrap/tabs'; import { ComponentsModule } from '../../shared/components/components.module'; import { PipesModule } from '../../shared/pipes/pipes.module'; import { ServicesModule } from '../../shared/services/services.module'; import { SharedModule } from '../../shared/shared.module'; import { IscsiComponent } from './iscsi/iscsi.component'; +import { MirrorHealthColorPipe } from './mirror-health-color.pipe'; +import { MirroringComponent } from './mirroring/mirroring.component'; import { PoolDetailComponent } from './pool-detail/pool-detail.component'; @NgModule({ @@ -16,6 +19,7 @@ import { PoolDetailComponent } from './pool-detail/pool-detail.component'; CommonModule, FormsModule, TabsModule.forRoot(), + ProgressbarModule.forRoot(), SharedModule, ComponentsModule, PipesModule, @@ -23,7 +27,9 @@ import { PoolDetailComponent } from './pool-detail/pool-detail.component'; ], declarations: [ PoolDetailComponent, - IscsiComponent + IscsiComponent, + MirroringComponent, + MirrorHealthColorPipe ] }) export class BlockModule { } diff --git a/src/pybind/mgr/dashboard_v2/frontend/src/app/ceph/block/mirror-health-color.pipe.spec.ts b/src/pybind/mgr/dashboard_v2/frontend/src/app/ceph/block/mirror-health-color.pipe.spec.ts new file mode 100644 index 0000000000000..f22bcf2a599b3 --- /dev/null +++ b/src/pybind/mgr/dashboard_v2/frontend/src/app/ceph/block/mirror-health-color.pipe.spec.ts @@ -0,0 +1,8 @@ +import { MirrorHealthColorPipe } from './mirror-health-color.pipe'; + +describe('MirrorHealthColorPipe', () => { + it('create an instance', () => { + const pipe = new MirrorHealthColorPipe(); + expect(pipe).toBeTruthy(); + }); +}); diff --git a/src/pybind/mgr/dashboard_v2/frontend/src/app/ceph/block/mirror-health-color.pipe.ts b/src/pybind/mgr/dashboard_v2/frontend/src/app/ceph/block/mirror-health-color.pipe.ts new file mode 100644 index 0000000000000..43d880ffb1aa8 --- /dev/null +++ b/src/pybind/mgr/dashboard_v2/frontend/src/app/ceph/block/mirror-health-color.pipe.ts @@ -0,0 +1,17 @@ +import { Pipe, PipeTransform } from '@angular/core'; + +@Pipe({ + name: 'mirrorHealthColor' +}) +export class MirrorHealthColorPipe implements PipeTransform { + transform(value: any, args?: any): any { + if (value === 'warning') { + return 'label label-warning'; + } else if (value === 'error') { + return 'label label-danger'; + } else if (value === 'success') { + return 'label label-success'; + } + return 'label label-info'; + } +} diff --git a/src/pybind/mgr/dashboard_v2/frontend/src/app/ceph/block/mirroring/mirroring.component.html b/src/pybind/mgr/dashboard_v2/frontend/src/app/ceph/block/mirroring/mirroring.component.html new file mode 100644 index 0000000000000..405889e4bda49 --- /dev/null +++ b/src/pybind/mgr/dashboard_v2/frontend/src/app/ceph/block/mirroring/mirroring.component.html @@ -0,0 +1,89 @@ + + + + +
+
+
+ Daemons + + + +
+
+ +
+
+ Pools + + + +
+
+
+ +
+
+
+ Images + + + + + + + + + + + + + + +
+
+
+ + + {{ value }} + + + + {{ value }} + + + + Syncing + + + + + + diff --git a/src/pybind/mgr/dashboard_v2/frontend/src/app/ceph/block/mirroring/mirroring.component.scss b/src/pybind/mgr/dashboard_v2/frontend/src/app/ceph/block/mirroring/mirroring.component.scss new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/pybind/mgr/dashboard_v2/frontend/src/app/ceph/block/mirroring/mirroring.component.spec.ts b/src/pybind/mgr/dashboard_v2/frontend/src/app/ceph/block/mirroring/mirroring.component.spec.ts new file mode 100644 index 0000000000000..accc564181950 --- /dev/null +++ b/src/pybind/mgr/dashboard_v2/frontend/src/app/ceph/block/mirroring/mirroring.component.spec.ts @@ -0,0 +1,50 @@ +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ProgressbarModule } from 'ngx-bootstrap/progressbar'; +import { TabsModule } from 'ngx-bootstrap/tabs'; +import { Observable } from 'rxjs/Observable'; + +import { RbdMirroringService } from '../../../shared/services/rbd-mirroring.service'; +import { SharedModule } from '../../../shared/shared.module'; +import { BlockModule } from '../block.module'; +import { MirrorHealthColorPipe } from '../mirror-health-color.pipe'; +import { MirroringComponent } from './mirroring.component'; + +describe('MirroringComponent', () => { + let component: MirroringComponent; + let fixture: ComponentFixture; + + const fakeService = { + get: (service_type: string, service_id: string) => { + return Observable.create(observer => { + return () => console.log('disposed'); + }); + } + }; + + beforeEach( + async(() => { + TestBed.configureTestingModule({ + declarations: [MirroringComponent, MirrorHealthColorPipe], + imports: [ + SharedModule, + TabsModule.forRoot(), + ProgressbarModule.forRoot(), + HttpClientTestingModule + ], + providers: [{ provide: RbdMirroringService, useValue: fakeService }] + }).compileComponents(); + }) + ); + + beforeEach(() => { + fixture = TestBed.createComponent(MirroringComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/pybind/mgr/dashboard_v2/frontend/src/app/ceph/block/mirroring/mirroring.component.ts b/src/pybind/mgr/dashboard_v2/frontend/src/app/ceph/block/mirroring/mirroring.component.ts new file mode 100644 index 0000000000000..b89a23f825151 --- /dev/null +++ b/src/pybind/mgr/dashboard_v2/frontend/src/app/ceph/block/mirroring/mirroring.component.ts @@ -0,0 +1,146 @@ +import { HttpClient } from '@angular/common/http'; +import { Component, OnDestroy, OnInit, TemplateRef, ViewChild } from '@angular/core'; + +import * as _ from 'lodash'; + +import { ViewCacheStatus } from '../../../shared/enum/view-cache-status.enum'; +import { CephShortVersionPipe } from '../../../shared/pipes/ceph-short-version.pipe'; +import { RbdMirroringService } from '../../../shared/services/rbd-mirroring.service'; + +@Component({ + selector: 'cd-mirroring', + templateUrl: './mirroring.component.html', + styleUrls: ['./mirroring.component.scss'] +}) +export class MirroringComponent implements OnInit, OnDestroy { + @ViewChild('healthTmpl') healthTmpl: TemplateRef; + @ViewChild('stateTmpl') stateTmpl: TemplateRef; + @ViewChild('syncTmpl') syncTmpl: TemplateRef; + @ViewChild('progressTmpl') progressTmpl: TemplateRef; + + contentData: any; + interval: any; + + status: ViewCacheStatus; + daemons = { + data: [], + columns: [] + }; + pools = { + data: [], + columns: {} + }; + image_error = { + data: [], + columns: {} + }; + image_syncing = { + data: [], + columns: {} + }; + image_ready = { + data: [], + columns: {} + }; + + constructor( + private http: HttpClient, + private rbdMirroringService: RbdMirroringService, + private cephShortVersionPipe: CephShortVersionPipe + ) { } + + ngOnInit() { + this.daemons.columns = [ + { prop: 'instance_id', name: 'Instance', flexGrow: 2 }, + { prop: 'id', name: 'ID', flexGrow: 2 }, + { prop: 'server_hostname', name: 'Hostname', flexGrow: 2 }, + { + prop: 'server_hostname', + name: 'Version', + pipe: this.cephShortVersionPipe, + flexGrow: 2 + }, + { + prop: 'health', + name: 'Health', + cellTemplate: this.healthTmpl, + flexGrow: 1 + } + ]; + + this.pools.columns = [ + { prop: 'name', name: 'Name', flexGrow: 2 }, + { prop: 'mirror_mode', name: 'Mode', flexGrow: 2 }, + { prop: 'leader_id', name: 'Leader', flexGrow: 2 }, + { prop: 'image_local_count', name: '# Local', flexGrow: 2 }, + { prop: 'image_remote_count', name: '# Remote', flexGrow: 2 }, + { + prop: 'health', + name: 'Health', + cellTemplate: this.healthTmpl, + flexGrow: 1 + } + ]; + + this.image_error.columns = [ + { prop: 'pool_name', name: 'Pool', flexGrow: 2 }, + { prop: 'name', name: 'Image', flexGrow: 2 }, + { prop: 'description', name: 'Issue', flexGrow: 4 }, + { + prop: 'state', + name: 'State', + cellTemplate: this.stateTmpl, + flexGrow: 1 + } + ]; + + this.image_syncing.columns = [ + { prop: 'pool_name', name: 'Pool', flexGrow: 2 }, + { prop: 'name', name: 'Image', flexGrow: 2 }, + { + prop: 'progress', + name: 'Progress', + cellTemplate: this.progressTmpl, + flexGrow: 2 + }, + { + prop: 'state', + name: 'State', + cellTemplate: this.syncTmpl, + flexGrow: 1 + } + ]; + + this.image_ready.columns = [ + { prop: 'pool_name', name: 'Pool', flexGrow: 2 }, + { prop: 'name', name: 'Image', flexGrow: 2 }, + { prop: 'description', name: 'Description', flexGrow: 4 }, + { + prop: 'state', + name: 'State', + cellTemplate: this.stateTmpl, + flexGrow: 1 + } + ]; + + setTimeout(() => { + this.interval = this.refresh(); + }, 30000); + } + + ngOnDestroy() { + clearInterval(this.interval); + } + + refresh() { + this.rbdMirroringService.get().subscribe((data: any) => { + this.daemons.data = data.content_data.daemons; + this.pools.data = data.content_data.pools; + this.image_error.data = data.content_data.image_error; + this.image_syncing.data = data.content_data.image_syncing; + this.image_ready.data = data.content_data.image_ready; + + this.status = data.status; + }); + } +} diff --git a/src/pybind/mgr/dashboard_v2/frontend/src/app/core/navigation/navigation/navigation.component.html b/src/pybind/mgr/dashboard_v2/frontend/src/app/core/navigation/navigation/navigation.component.html index 378e70a95714f..e8981802eed3c 100644 --- a/src/pybind/mgr/dashboard_v2/frontend/src/app/core/navigation/navigation/navigation.component.html +++ b/src/pybind/mgr/dashboard_v2/frontend/src/app/core/navigation/navigation/navigation.component.html @@ -86,17 +86,31 @@ class="dropdown tc_menuitem tc_menuitem_block"> + data-toggle="dropdown" + [ngStyle]="blockHealthColor()"> Block