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 <dillaman@redhat.com>
--- /dev/null
+<cd-modal [modalRef]="modalRef">
+ <ng-container i18n
+ class="modal-title">Edit site name</ng-container>
+
+ <ng-container class="modal-content">
+ <form name="editSiteNameForm"
+ class="form"
+ #formDir="ngForm"
+ [formGroup]="editSiteNameForm"
+ novalidate>
+ <div class="modal-body">
+ <p>
+ <ng-container i18n>Edit the site name and click
+ <kbd>Update</kbd>.</ng-container>
+ </p>
+
+ <div class="form-group">
+ <label class="col-form-label"
+ for="siteName">
+ <span i18n>Site Name</span>
+ <span class="required"></span>
+ </label>
+ <input class="form-control"
+ type="text"
+ placeholder="Name..."
+ i18n-placeholder
+ id="siteName"
+ name="siteName"
+ formControlName="siteName"
+ autofocus>
+ </div>
+ </div>
+
+ <div class="modal-footer">
+ <div class="button-group text-right">
+ <cd-submit-button i18n
+ [form]="editSiteNameForm"
+ (submitAction)="update()">Update</cd-submit-button>
+ <cd-back-button [back]="modalRef.hide"
+ name="Cancel"
+ i18n-name>
+ </cd-back-button>
+ </div>
+ </div>
+ </form>
+ </ng-container>
+</cd-modal>
--- /dev/null
+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<EditSiteNameModalComponent>;
+ 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');
+ });
+ });
+});
--- /dev/null
+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();
+ }
+ );
+ }
+}
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';
import { PoolListComponent } from './pool-list/pool-list.component';
@NgModule({
- entryComponents: [OverviewComponent, PoolEditModeModalComponent, PoolEditPeerModalComponent],
+ entryComponents: [
+ EditSiteNameModalComponent,
+ OverviewComponent,
+ PoolEditModeModalComponent,
+ PoolEditPeerModalComponent
+ ],
imports: [
CommonModule,
TabsModule.forRoot(),
],
declarations: [
DaemonListComponent,
+ EditSiteNameModalComponent,
ImageListComponent,
OverviewComponent,
PoolEditModeModalComponent,
<cd-view-cache [status]="status"></cd-view-cache>
+<div class="row">
+ <div class="col-md-12">
+ <span><strong i18n>Site Name:</strong> {{siteName}}</span>
+ <cd-table-actions class="table-actions float-right"
+ [permission]="permission"
+ [selection]="selection"
+ [tableActions]="tableActions">
+ </cd-table-actions>
+ </div>
+</div>
+
<div class="row">
<div class="col-sm-6">
<legend i18n>Daemons</legend>
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',
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) => {
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 });
+ }
}
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);
};
rbd_mirroring = {
+ site_name: () => this.i18n('mirroring site name'),
pool: (metadata) =>
this.i18n(`mirror mode for pool '{{id}}'`, {
id: `${metadata.pool_name}`
}
),
// 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,