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 000000000000..64ea83adb817 --- /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 000000000000..e69de29bb2d1 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 000000000000..227964ac2c6f --- /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 000000000000..e7e256bd3935 --- /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 0e8f48280fc8..9df7e83db1ea 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 6f598e750688..38e7fa8d0a79 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 1e2853395914..b2047f1c385d 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 bff7cff540ce..df2281283f63 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 c4ce8834cda2..d1ede98fc299 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,