From: Tatjana Dehler Date: Tue, 28 May 2019 06:51:06 +0000 (+0200) Subject: mgr/dashboard: consider 'mon_allow_pool_delete' flag X-Git-Tag: v14.2.3~55^2 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=2c0dd7eabc8cc62e3bed67a81c6e66d8ea04c244;p=ceph.git mgr/dashboard: consider 'mon_allow_pool_delete' flag Check if the 'mon_allow_pool_delete' flag is set. If not, disable the menu item to delete a pool. Show also a description why the item is disabled. Fixes: https://tracker.ceph.com/issues/39533 Signed-off-by: Tatjana Dehler (cherry picked from commit b5eb71dbd2f853b86a901f413b685ca235b8bc1b) --- diff --git a/qa/tasks/mgr/dashboard/test_cluster_configuration.py b/qa/tasks/mgr/dashboard/test_cluster_configuration.py index 450d9021106..798afe9c19c 100644 --- a/qa/tasks/mgr/dashboard/test_cluster_configuration.py +++ b/qa/tasks/mgr/dashboard/test_cluster_configuration.py @@ -286,7 +286,6 @@ class ClusterConfigurationTest(DashboardTestCase): """ This test case is intended to check the existence of all hard coded config options used by the dashboard. - If you include further hard coded options in the dashboard, feel free to add them to the list. """ @@ -326,7 +325,8 @@ class ClusterConfigurationTest(DashboardTestCase): 'osd_scrub_interval_randomize_ratio', # osd-pg-scrub 'osd_scrub_invalid_stats', # osd-pg-scrub 'osd_scrub_load_threshold', # osd-pg-scrub - 'osd_scrub_max_preemptions' # osd-pg-scrub + 'osd_scrub_max_preemptions', # osd-pg-scrub + 'mon_allow_pool_delete' # pool-list ] for config_option in hard_coded_options: diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-list/pool-list.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-list/pool-list.component.spec.ts index 21e553ba147..1ea2c6579bb 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-list/pool-list.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-list/pool-list.component.spec.ts @@ -8,6 +8,7 @@ import { TabsModule } from 'ngx-bootstrap/tabs'; import { of } from 'rxjs'; import { configureTestBed, i18nProviders } from '../../../../testing/unit-test-helper'; +import { ConfigurationService } from '../../../shared/api/configuration.service'; import { PoolService } from '../../../shared/api/pool.service'; import { CriticalConfirmationModalComponent } from '../../../shared/components/critical-confirmation-modal/critical-confirmation-modal.component'; import { ExecutingTask } from '../../../shared/models/executing-task'; @@ -67,6 +68,56 @@ describe('PoolListComponent', () => { expect(component.columns.every((column) => Boolean(column.prop))).toBeTruthy(); }); + describe('monAllowPoolDelete', () => { + let configurationService: ConfigurationService; + + beforeEach(() => { + configurationService = TestBed.get(ConfigurationService); + }); + + it('should set value correctly if mon_allow_pool_delete flag is set to true', () => { + const configOption = { + name: 'mon_allow_pool_delete', + value: [ + { + section: 'mon', + value: 'true' + } + ] + }; + spyOn(configurationService, 'get').and.returnValue(of(configOption)); + fixture = TestBed.createComponent(PoolListComponent); + component = fixture.componentInstance; + expect(component.monAllowPoolDelete).toBe(true); + }); + + it('should set value correctly if mon_allow_pool_delete flag is set to false', () => { + const configOption = { + name: 'mon_allow_pool_delete', + value: [ + { + section: 'mon', + value: 'false' + } + ] + }; + spyOn(configurationService, 'get').and.returnValue(of(configOption)); + fixture = TestBed.createComponent(PoolListComponent); + component = fixture.componentInstance; + expect(component.monAllowPoolDelete).toBe(false); + }); + + it('should set value correctly if mon_allow_pool_delete flag is not set', () => { + const configOption = { + name: 'mon_allow_pool_delete' + }; + spyOn(configurationService, 'get').and.returnValue(of(configOption)); + fixture = TestBed.createComponent(PoolListComponent); + component = fixture.componentInstance; + expect(component.monAllowPoolDelete).toBe(false); + }); + }); + describe('pool deletion', () => { let taskWrapper: TaskWrapperService; @@ -348,4 +399,18 @@ describe('PoolListComponent', () => { expect(component.selectionCacheTiers).toEqual([]); }); }); + + describe('getDisableDesc', () => { + it('should return message if mon_allow_pool_delete flag is set to false', () => { + component.monAllowPoolDelete = false; + expect(component.getDisableDesc()).toBe( + 'Pool deletion is disabled by the mon_allow_pool_delete configuration setting.' + ); + }); + + it('should return undefined if mon_allow_pool_delete flag is set to true', () => { + component.monAllowPoolDelete = true; + expect(component.getDisableDesc()).toBeUndefined(); + }); + }); }); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-list/pool-list.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-list/pool-list.component.ts index 91e9b0ec2a8..78586e69bbd 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-list/pool-list.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-list/pool-list.component.ts @@ -4,6 +4,7 @@ import { I18n } from '@ngx-translate/i18n-polyfill'; import * as _ from 'lodash'; import { BsModalRef, BsModalService } from 'ngx-bootstrap/modal'; +import { ConfigurationService } from '../../../shared/api/configuration.service'; import { PoolService } from '../../../shared/api/pool.service'; import { CriticalConfirmationModalComponent } from '../../../shared/components/critical-confirmation-modal/critical-confirmation-modal.component'; import { ActionLabelsI18n, URLVerbs } from '../../../shared/constants/app.constants'; @@ -54,6 +55,7 @@ export class PoolListComponent implements OnInit { tableActions: CdTableAction[]; viewCacheStatusList: any[]; selectionCacheTiers: any[] = []; + monAllowPoolDelete = false; constructor( private poolService: PoolService, @@ -65,6 +67,7 @@ export class PoolListComponent implements OnInit { private pgCategoryService: PgCategoryService, private dimlessPipe: DimlessPipe, private urlBuilder: URLBuilderService, + private configurationService: ConfigurationService, public actionLabels: ActionLabelsI18n ) { this.permissions = this.authStorageService.getPermissions(); @@ -86,9 +89,20 @@ export class PoolListComponent implements OnInit { permission: 'delete', icon: 'fa-trash-o', click: () => this.deletePoolModal(), - name: this.actionLabels.DELETE + name: this.actionLabels.DELETE, + disable: () => !this.selection.first() || !this.monAllowPoolDelete, + disableDesc: () => this.getDisableDesc() } ]; + + this.configurationService.get('mon_allow_pool_delete').subscribe((data: any) => { + if (_.has(data, 'value')) { + const monSection = _.find(data.value, (v) => { + return v.section === 'mon'; + }) || { value: false }; + this.monAllowPoolDelete = monSection.value === 'true' ? true : false; + } + }); } ngOnInit() { @@ -253,4 +267,12 @@ export class PoolListComponent implements OnInit { const cacheTierIds = this.selection.hasSingleSelection ? this.selection.first()['tiers'] : []; this.selectionCacheTiers = this.pools.filter((pool) => cacheTierIds.includes(pool.pool)); } + + getDisableDesc(): string | undefined { + if (!this.monAllowPoolDelete) { + return this.i18n( + 'Pool deletion is disabled by the mon_allow_pool_delete configuration setting.' + ); + } + } }