From: Jason Dillaman Date: Tue, 22 Oct 2019 17:58:31 +0000 (-0400) Subject: mgr/dashboard: integrate site name into block mirroring overview X-Git-Tag: v15.1.0~633^2~1 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=51a19c19eee188b82027ae28ab86aa0291323812;p=ceph.git mgr/dashboard: integrate site name into block mirroring overview The site name is used to differentiate clusters for RBD mirroring. The local site name is now displayed on the block mirroring overview page. It also includes a new modal window to edit the site name. Signed-off-by: Jason Dillaman --- diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/mirroring/edit-site-name-modal/edit-site-name-modal.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/mirroring/edit-site-name-modal/edit-site-name-modal.component.html new file mode 100644 index 00000000000..64ea83adb81 --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/mirroring/edit-site-name-modal/edit-site-name-modal.component.html @@ -0,0 +1,47 @@ + + Edit site name + + +
+ + + +
+
+
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/mirroring/edit-site-name-modal/edit-site-name-modal.component.scss b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/mirroring/edit-site-name-modal/edit-site-name-modal.component.scss new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/mirroring/edit-site-name-modal/edit-site-name-modal.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/mirroring/edit-site-name-modal/edit-site-name-modal.component.spec.ts new file mode 100644 index 00000000000..227964ac2c6 --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/mirroring/edit-site-name-modal/edit-site-name-modal.component.spec.ts @@ -0,0 +1,73 @@ +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { ReactiveFormsModule } from '@angular/forms'; +import { RouterTestingModule } from '@angular/router/testing'; + +import { BsModalRef, BsModalService } from 'ngx-bootstrap/modal'; +import { ToastrModule } from 'ngx-toastr'; +import { of } from 'rxjs'; + +import { configureTestBed, i18nProviders } from '../../../../../testing/unit-test-helper'; +import { RbdMirroringService } from '../../../../shared/api/rbd-mirroring.service'; +import { NotificationService } from '../../../../shared/services/notification.service'; +import { SharedModule } from '../../../../shared/shared.module'; +import { EditSiteNameModalComponent } from './edit-site-name-modal.component'; + +describe('EditSiteNameModalComponent', () => { + let component: EditSiteNameModalComponent; + let fixture: ComponentFixture; + let notificationService: NotificationService; + let rbdMirroringService: RbdMirroringService; + + configureTestBed({ + declarations: [EditSiteNameModalComponent], + imports: [ + HttpClientTestingModule, + ReactiveFormsModule, + RouterTestingModule, + SharedModule, + ToastrModule.forRoot() + ], + providers: [BsModalRef, BsModalService, i18nProviders] + }); + + beforeEach(() => { + fixture = TestBed.createComponent(EditSiteNameModalComponent); + component = fixture.componentInstance; + component.siteName = 'site-A'; + + notificationService = TestBed.get(NotificationService); + spyOn(notificationService, 'show').and.stub(); + + rbdMirroringService = TestBed.get(RbdMirroringService); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + describe('edit site name', () => { + beforeEach(() => { + spyOn(rbdMirroringService, 'getSiteName').and.callFake(() => of({ site_name: 'site-A' })); + spyOn(rbdMirroringService, 'refresh').and.stub(); + spyOn(component.modalRef, 'hide').and.callThrough(); + fixture.detectChanges(); + }); + + afterEach(() => { + expect(rbdMirroringService.getSiteName).toHaveBeenCalledTimes(1); + expect(rbdMirroringService.refresh).toHaveBeenCalledTimes(1); + expect(component.modalRef.hide).toHaveBeenCalledTimes(1); + }); + + it('should call setSiteName', () => { + spyOn(rbdMirroringService, 'setSiteName').and.callFake(() => of({ site_name: 'new-site-A' })); + + component.editSiteNameForm.patchValue({ + siteName: 'new-site-A' + }); + component.update(); + expect(rbdMirroringService.setSiteName).toHaveBeenCalledWith('new-site-A'); + }); + }); +}); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/mirroring/edit-site-name-modal/edit-site-name-modal.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/mirroring/edit-site-name-modal/edit-site-name-modal.component.ts new file mode 100644 index 00000000000..e7e256bd393 --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/mirroring/edit-site-name-modal/edit-site-name-modal.component.ts @@ -0,0 +1,57 @@ +import { Component, OnInit } from '@angular/core'; +import { FormControl } from '@angular/forms'; + +import { BsModalRef } from 'ngx-bootstrap/modal'; + +import { RbdMirroringService } from '../../../../shared/api/rbd-mirroring.service'; +import { CdFormGroup } from '../../../../shared/forms/cd-form-group'; +import { FinishedTask } from '../../../../shared/models/finished-task'; +import { TaskWrapperService } from '../../../../shared/services/task-wrapper.service'; + +@Component({ + selector: 'cd-edit-site-mode-modal', + templateUrl: './edit-site-name-modal.component.html', + styleUrls: ['./edit-site-name-modal.component.scss'] +}) +export class EditSiteNameModalComponent implements OnInit { + siteName: string; + + editSiteNameForm: CdFormGroup; + + constructor( + public modalRef: BsModalRef, + private rbdMirroringService: RbdMirroringService, + private taskWrapper: TaskWrapperService + ) { + this.createForm(); + } + + createForm() { + this.editSiteNameForm = new CdFormGroup({ + siteName: new FormControl('', {}) + }); + } + + ngOnInit() { + this.editSiteNameForm.get('siteName').setValue(this.siteName); + this.rbdMirroringService.getSiteName().subscribe((response: any) => { + this.editSiteNameForm.get('siteName').setValue(response.site_name); + }); + } + + update() { + const action = this.taskWrapper.wrapTaskAroundCall({ + task: new FinishedTask('rbd/mirroring/site_name/edit', {}), + call: this.rbdMirroringService.setSiteName(this.editSiteNameForm.getValue('siteName')) + }); + + action.subscribe( + undefined, + () => this.editSiteNameForm.setErrors({ cdSubmitButton: true }), + () => { + this.rbdMirroringService.refresh(); + this.modalRef.hide(); + } + ); + } +} diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/mirroring/mirroring.module.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/mirroring/mirroring.module.ts index 0e8f48280fc..9df7e83db1e 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/mirroring/mirroring.module.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/mirroring/mirroring.module.ts @@ -15,6 +15,7 @@ import { TooltipModule } from 'ngx-bootstrap/tooltip'; import { SharedModule } from '../../../shared/shared.module'; import { DaemonListComponent } from './daemon-list/daemon-list.component'; +import { EditSiteNameModalComponent } from './edit-site-name-modal/edit-site-name-modal.component'; import { ImageListComponent } from './image-list/image-list.component'; import { MirrorHealthColorPipe } from './mirror-health-color.pipe'; import { OverviewComponent } from './overview/overview.component'; @@ -23,7 +24,12 @@ import { PoolEditPeerModalComponent } from './pool-edit-peer-modal/pool-edit-pee import { PoolListComponent } from './pool-list/pool-list.component'; @NgModule({ - entryComponents: [OverviewComponent, PoolEditModeModalComponent, PoolEditPeerModalComponent], + entryComponents: [ + EditSiteNameModalComponent, + OverviewComponent, + PoolEditModeModalComponent, + PoolEditPeerModalComponent + ], imports: [ CommonModule, TabsModule.forRoot(), @@ -41,6 +47,7 @@ import { PoolListComponent } from './pool-list/pool-list.component'; ], declarations: [ DaemonListComponent, + EditSiteNameModalComponent, ImageListComponent, OverviewComponent, PoolEditModeModalComponent, diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/mirroring/overview/overview.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/mirroring/overview/overview.component.html index 6f598e75068..38e7fa8d0a7 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/mirroring/overview/overview.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/mirroring/overview/overview.component.html @@ -1,5 +1,16 @@ +
+
+ Site Name: {{siteName}} + + +
+
+
Daemons diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/mirroring/overview/overview.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/mirroring/overview/overview.component.ts index 1e285339591..b2047f1c385 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/mirroring/overview/overview.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/mirroring/overview/overview.component.ts @@ -1,9 +1,17 @@ import { Component, OnDestroy, OnInit } from '@angular/core'; +import { I18n } from '@ngx-translate/i18n-polyfill'; +import { BsModalRef, BsModalService } from 'ngx-bootstrap/modal'; import { Subscription } from 'rxjs'; import { RbdMirroringService } from '../../../../shared/api/rbd-mirroring.service'; +import { Icons } from '../../../../shared/enum/icons.enum'; import { ViewCacheStatus } from '../../../../shared/enum/view-cache-status.enum'; +import { CdTableAction } from '../../../../shared/models/cd-table-action'; +import { CdTableSelection } from '../../../../shared/models/cd-table-selection'; +import { Permission } from '../../../../shared/models/permissions'; +import { AuthStorageService } from '../../../../shared/services/auth-storage.service'; +import { EditSiteNameModalComponent } from '../edit-site-name-modal/edit-site-name-modal.component'; @Component({ selector: 'cd-mirroring', @@ -11,11 +19,36 @@ import { ViewCacheStatus } from '../../../../shared/enum/view-cache-status.enum' styleUrls: ['./overview.component.scss'] }) export class OverviewComponent implements OnInit, OnDestroy { + permission: Permission; + tableActions: CdTableAction[]; + selection = new CdTableSelection(); + subs: Subscription; + modalRef: BsModalRef; + + peersExist = true; + siteName: any; status: ViewCacheStatus; - constructor(private rbdMirroringService: RbdMirroringService) {} + constructor( + private authStorageService: AuthStorageService, + private rbdMirroringService: RbdMirroringService, + private modalService: BsModalService, + private i18n: I18n + ) { + this.permission = this.authStorageService.getPermissions().rbdMirroring; + + const editSiteNameAction: CdTableAction = { + permission: 'update', + icon: Icons.edit, + click: () => this.editSiteNameModal(), + name: this.i18n('Edit Site Name'), + canBePrimary: () => true, + disable: () => false + }; + this.tableActions = [editSiteNameAction]; + } ngOnInit() { this.subs = this.rbdMirroringService.subscribeSummary((data: any) => { @@ -23,10 +56,20 @@ export class OverviewComponent implements OnInit, OnDestroy { return; } this.status = data.content_data.status; + this.siteName = data.site_name; + + this.peersExist = !!data.content_data.pools.find((o) => o['peer_uuids'].length > 0); }); } ngOnDestroy(): void { this.subs.unsubscribe(); } + + editSiteNameModal() { + const initialState = { + siteName: this.siteName + }; + this.modalRef = this.modalService.show(EditSiteNameModalComponent, { initialState }); + } } diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/services/task-message.service.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/services/task-message.service.spec.ts index bff7cff540c..df2281283f6 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/shared/services/task-message.service.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/services/task-message.service.spec.ts @@ -252,6 +252,10 @@ describe('TaskManagerMessageService', () => { peerMsg = `mirror peer for pool '${metadata.pool_name}'`; finishedTask.metadata = metadata; }); + it('tests rbd/mirroring/site_name/edit messages', () => { + finishedTask.name = 'rbd/mirroring/site_name/edit'; + testUpdate('mirroring site name'); + }); it('tests rbd/mirroring/pool/edit messages', () => { finishedTask.name = 'rbd/mirroring/pool/edit'; testUpdate(modeMsg); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/services/task-message.service.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/services/task-message.service.ts index c4ce8834cda..d1ede98fc29 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/shared/services/task-message.service.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/services/task-message.service.ts @@ -139,6 +139,7 @@ export class TaskMessageService { }; rbd_mirroring = { + site_name: () => this.i18n('mirroring site name'), pool: (metadata) => this.i18n(`mirror mode for pool '{{id}}'`, { id: `${metadata.pool_name}` @@ -327,6 +328,11 @@ export class TaskMessageService { } ), // RBD mirroring tasks + 'rbd/mirroring/site_name/edit': this.newTaskMessage( + this.commonOperations.update, + this.rbd_mirroring.site_name, + () => ({}) + ), 'rbd/mirroring/pool/edit': this.newTaskMessage( this.commonOperations.update, this.rbd_mirroring.pool,