From dae8168e29bc864058000256e01d1ce514c6f068 Mon Sep 17 00:00:00 2001 From: Pedro Gonzalez Gomez Date: Mon, 20 Mar 2023 19:53:06 +0100 Subject: [PATCH] mgr/dashboard: delete rgw multisite Signed-off-by: Pedro Gonzalez Gomez --- src/pybind/mgr/dashboard/controllers/rgw.py | 24 ++++ ...ultisite-zone-deletion-form.component.html | 55 ++++++++ ...ultisite-zone-deletion-form.component.scss | 9 ++ ...isite-zone-deletion-form.component.spec.ts | 30 +++++ ...-multisite-zone-deletion-form.component.ts | 60 +++++++++ ...ite-zonegroup-deletion-form.component.html | 58 +++++++++ ...ite-zonegroup-deletion-form.component.scss | 9 ++ ...-zonegroup-deletion-form.component.spec.ts | 30 +++++ ...isite-zonegroup-deletion-form.component.ts | 55 ++++++++ .../rgw-multisite-details.component.html | 7 + .../rgw-multisite-details.component.spec.ts | 3 +- .../rgw-multisite-details.component.ts | 40 +++++- .../frontend/src/app/ceph/rgw/rgw.module.ts | 6 +- .../src/app/shared/api/rgw-realm.service.ts | 9 ++ .../src/app/shared/api/rgw-zone.service.ts | 11 ++ .../app/shared/api/rgw-zonegroup.service.ts | 10 ++ src/pybind/mgr/dashboard/openapi.yaml | 123 ++++++++++++++++++ .../mgr/dashboard/services/rgw_client.py | 76 +++++++++++ 18 files changed, 612 insertions(+), 3 deletions(-) create mode 100644 src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/models/rgw-multisite-zone-deletion-form/rgw-multisite-zone-deletion-form.component.html create mode 100644 src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/models/rgw-multisite-zone-deletion-form/rgw-multisite-zone-deletion-form.component.scss create mode 100644 src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/models/rgw-multisite-zone-deletion-form/rgw-multisite-zone-deletion-form.component.spec.ts create mode 100644 src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/models/rgw-multisite-zone-deletion-form/rgw-multisite-zone-deletion-form.component.ts create mode 100644 src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/models/rgw-multisite-zonegroup-deletion-form/rgw-multisite-zonegroup-deletion-form.component.html create mode 100644 src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/models/rgw-multisite-zonegroup-deletion-form/rgw-multisite-zonegroup-deletion-form.component.scss create mode 100644 src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/models/rgw-multisite-zonegroup-deletion-form/rgw-multisite-zonegroup-deletion-form.component.spec.ts create mode 100644 src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/models/rgw-multisite-zonegroup-deletion-form/rgw-multisite-zonegroup-deletion-form.component.ts diff --git a/src/pybind/mgr/dashboard/controllers/rgw.py b/src/pybind/mgr/dashboard/controllers/rgw.py index 7a35cb8a9d0a3..def764eceaa08 100644 --- a/src/pybind/mgr/dashboard/controllers/rgw.py +++ b/src/pybind/mgr/dashboard/controllers/rgw.py @@ -753,6 +753,14 @@ class RgwRealm(RESTController): except NoRgwDaemonsException as e: raise DashboardException(e, http_status_code=404, component='rgw') + def delete(self, realm_name, daemon_name=None): + try: + instance = RgwClient.admin_instance(daemon_name) + result = instance.delete_realm(realm_name) + return result + except NoRgwDaemonsException as e: + raise DashboardException(e, http_status_code=404, component='rgw') + @APIRouter('/rgw/zonegroup', Scope.RGW) class RgwZonegroup(RESTController): @@ -798,6 +806,14 @@ class RgwZonegroup(RESTController): except NoRgwDaemonsException as e: raise DashboardException(e, http_status_code=404, component='rgw') + def delete(self, zonegroup_name, delete_pools, daemon_name=None): + try: + instance = RgwClient.admin_instance(daemon_name) + result = instance.delete_zonegroup(zonegroup_name, delete_pools) + return result + except NoRgwDaemonsException as e: + raise DashboardException(e, http_status_code=404, component='rgw') + @APIRouter('/rgw/zone', Scope.RGW) class RgwZone(RESTController): @@ -842,3 +858,11 @@ class RgwZone(RESTController): return result except NoRgwDaemonsException as e: raise DashboardException(e, http_status_code=404, component='rgw') + + def delete(self, zonegroup_name, zone_name, delete_pools, daemon_name=None): + try: + instance = RgwClient.admin_instance(daemon_name) + result = instance.delete_zone(zonegroup_name, zone_name, delete_pools) + return result + except NoRgwDaemonsException as e: + raise DashboardException(e, http_status_code=404, component='rgw') diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/models/rgw-multisite-zone-deletion-form/rgw-multisite-zone-deletion-form.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/models/rgw-multisite-zone-deletion-form/rgw-multisite-zone-deletion-form.component.html new file mode 100644 index 0000000000000..030cfe674ba85 --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/models/rgw-multisite-zone-deletion-form/rgw-multisite-zone-deletion-form.component.html @@ -0,0 +1,55 @@ + + Delete Zone + + +
+
+ + + +
+ + {{pool?.val.data_extra_pool}} + {{pool?.val.index_pool}} + {{pool?.val.storage_classes.STANDARD.data_pool}} + +
+
+
+
+ + +
+
+ + This will delete all the data in the pools! + +
+
+
+ + +
+
+ +
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/models/rgw-multisite-zone-deletion-form/rgw-multisite-zone-deletion-form.component.scss b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/models/rgw-multisite-zone-deletion-form/rgw-multisite-zone-deletion-form.component.scss new file mode 100644 index 0000000000000..f1d1d1b859462 --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/models/rgw-multisite-zone-deletion-form/rgw-multisite-zone-deletion-form.component.scss @@ -0,0 +1,9 @@ +strong { + display: block; +} + +#scroll { + height: 100%; + max-height: 10rem; + overflow: auto; +} diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/models/rgw-multisite-zone-deletion-form/rgw-multisite-zone-deletion-form.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/models/rgw-multisite-zone-deletion-form/rgw-multisite-zone-deletion-form.component.spec.ts new file mode 100644 index 0000000000000..947ef3cbfbc50 --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/models/rgw-multisite-zone-deletion-form/rgw-multisite-zone-deletion-form.component.spec.ts @@ -0,0 +1,30 @@ +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { RouterTestingModule } from '@angular/router/testing'; +import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; +import { ToastrModule } from 'ngx-toastr'; +import { SharedModule } from '~/app/shared/shared.module'; +import { configureTestBed } from '~/testing/unit-test-helper'; + +import { RgwMultisiteZoneDeletionFormComponent } from './rgw-multisite-zone-deletion-form.component'; + +describe('RgwMultisiteZoneDeletionFormComponent', () => { + let component: RgwMultisiteZoneDeletionFormComponent; + let fixture: ComponentFixture; + + configureTestBed({ + declarations: [RgwMultisiteZoneDeletionFormComponent], + imports: [SharedModule, HttpClientTestingModule, ToastrModule.forRoot(), RouterTestingModule], + providers: [NgbActiveModal] + }); + + beforeEach(() => { + fixture = TestBed.createComponent(RgwMultisiteZoneDeletionFormComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/models/rgw-multisite-zone-deletion-form/rgw-multisite-zone-deletion-form.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/models/rgw-multisite-zone-deletion-form/rgw-multisite-zone-deletion-form.component.ts new file mode 100644 index 0000000000000..bc4fb2813ff76 --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/models/rgw-multisite-zone-deletion-form/rgw-multisite-zone-deletion-form.component.ts @@ -0,0 +1,60 @@ +import { Component, OnInit } from '@angular/core'; +import { FormControl } from '@angular/forms'; +import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; +import { RgwZoneService } from '~/app/shared/api/rgw-zone.service'; +import { ActionLabelsI18n } from '~/app/shared/constants/app.constants'; +import { NotificationType } from '~/app/shared/enum/notification-type.enum'; +import { CdFormGroup } from '~/app/shared/forms/cd-form-group'; +import { NotificationService } from '~/app/shared/services/notification.service'; + +@Component({ + selector: 'cd-rgw-multisite-zone-deletion-form', + templateUrl: './rgw-multisite-zone-deletion-form.component.html', + styleUrls: ['./rgw-multisite-zone-deletion-form.component.scss'] +}) +export class RgwMultisiteZoneDeletionFormComponent implements OnInit { + zoneData$: any; + zone: any; + zoneForm: CdFormGroup; + displayText: boolean = false; + + constructor( + public activeModal: NgbActiveModal, + public actionLabels: ActionLabelsI18n, + public notificationService: NotificationService, + private rgwZoneService: RgwZoneService + ) { + this.createForm(); + } + + ngOnInit(): void { + this.zoneData$ = this.rgwZoneService.get(this.zone); + } + + createForm() { + this.zoneForm = new CdFormGroup({ + deletePools: new FormControl(false) + }); + } + + submit() { + this.rgwZoneService + .delete(this.zone.parent, this.zone.name, this.zoneForm.value.deletePools) + .subscribe( + () => { + this.notificationService.show( + NotificationType.success, + $localize`Zone: '${this.zone.name}' deleted successfully` + ); + this.activeModal.close(); + }, + () => { + this.zoneForm.setErrors({ cdSubmitButton: true }); + } + ); + } + + showDangerText() { + this.displayText = !this.displayText; + } +} diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/models/rgw-multisite-zonegroup-deletion-form/rgw-multisite-zonegroup-deletion-form.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/models/rgw-multisite-zonegroup-deletion-form/rgw-multisite-zonegroup-deletion-form.component.html new file mode 100644 index 0000000000000..62edde410a7a1 --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/models/rgw-multisite-zonegroup-deletion-form/rgw-multisite-zonegroup-deletion-form.component.html @@ -0,0 +1,58 @@ + + Delete Zonegroup + + +
+ + + +
+
+ +
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/models/rgw-multisite-zonegroup-deletion-form/rgw-multisite-zonegroup-deletion-form.component.scss b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/models/rgw-multisite-zonegroup-deletion-form/rgw-multisite-zonegroup-deletion-form.component.scss new file mode 100644 index 0000000000000..f1d1d1b859462 --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/models/rgw-multisite-zonegroup-deletion-form/rgw-multisite-zonegroup-deletion-form.component.scss @@ -0,0 +1,9 @@ +strong { + display: block; +} + +#scroll { + height: 100%; + max-height: 10rem; + overflow: auto; +} diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/models/rgw-multisite-zonegroup-deletion-form/rgw-multisite-zonegroup-deletion-form.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/models/rgw-multisite-zonegroup-deletion-form/rgw-multisite-zonegroup-deletion-form.component.spec.ts new file mode 100644 index 0000000000000..3ee39f2e9fbdb --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/models/rgw-multisite-zonegroup-deletion-form/rgw-multisite-zonegroup-deletion-form.component.spec.ts @@ -0,0 +1,30 @@ +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { RouterTestingModule } from '@angular/router/testing'; +import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; +import { ToastrModule } from 'ngx-toastr'; +import { SharedModule } from '~/app/shared/shared.module'; +import { configureTestBed } from '~/testing/unit-test-helper'; + +import { RgwMultisiteZonegroupDeletionFormComponent } from './rgw-multisite-zonegroup-deletion-form.component'; + +describe('RgwMultisiteZonegroupDeletionFormComponent', () => { + let component: RgwMultisiteZonegroupDeletionFormComponent; + let fixture: ComponentFixture; + + configureTestBed({ + declarations: [RgwMultisiteZonegroupDeletionFormComponent], + imports: [SharedModule, HttpClientTestingModule, ToastrModule.forRoot(), RouterTestingModule], + providers: [NgbActiveModal] + }); + + beforeEach(() => { + fixture = TestBed.createComponent(RgwMultisiteZonegroupDeletionFormComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/models/rgw-multisite-zonegroup-deletion-form/rgw-multisite-zonegroup-deletion-form.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/models/rgw-multisite-zonegroup-deletion-form/rgw-multisite-zonegroup-deletion-form.component.ts new file mode 100644 index 0000000000000..f7ee6f39d57ae --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/models/rgw-multisite-zonegroup-deletion-form/rgw-multisite-zonegroup-deletion-form.component.ts @@ -0,0 +1,55 @@ +import { Component, OnInit } from '@angular/core'; +import { FormControl } from '@angular/forms'; +import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; +import { RgwZonegroupService } from '~/app/shared/api/rgw-zonegroup.service'; +import { ActionLabelsI18n } from '~/app/shared/constants/app.constants'; +import { NotificationType } from '~/app/shared/enum/notification-type.enum'; +import { CdFormGroup } from '~/app/shared/forms/cd-form-group'; +import { NotificationService } from '~/app/shared/services/notification.service'; + +@Component({ + selector: 'cd-rgw-multisite-zonegroup-deletion-form', + templateUrl: './rgw-multisite-zonegroup-deletion-form.component.html', + styleUrls: ['./rgw-multisite-zonegroup-deletion-form.component.scss'] +}) +export class RgwMultisiteZonegroupDeletionFormComponent implements OnInit { + zonegroupData$: any; + zonegroup: any; + zonegroupForm: CdFormGroup; + displayText: boolean = false; + + constructor( + public activeModal: NgbActiveModal, + public actionLabels: ActionLabelsI18n, + public notificationService: NotificationService, + private rgwZonegroupService: RgwZonegroupService + ) { + this.createForm(); + } + + ngOnInit(): void { + this.zonegroupData$ = this.rgwZonegroupService.get(this.zonegroup); + } + + createForm() { + this.zonegroupForm = new CdFormGroup({ + deletePools: new FormControl(false) + }); + } + + submit() { + this.rgwZonegroupService + .delete(this.zonegroup.name, this.zonegroupForm.value.deletePools) + .subscribe(() => { + this.notificationService.show( + NotificationType.success, + $localize`Zone: '${this.zonegroup.name}' deleted successfully` + ); + this.activeModal.close(); + }); + } + + showDangerText() { + this.displayText = !this.displayText; + } +} diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-multisite-details/rgw-multisite-details.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-multisite-details/rgw-multisite-details.component.html index 50e17b41b1ada..3dcb290c64141 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-multisite-details/rgw-multisite-details.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-multisite-details/rgw-multisite-details.component.html @@ -48,6 +48,13 @@ ngbDropdownToggle> + diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-multisite-details/rgw-multisite-details.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-multisite-details/rgw-multisite-details.component.spec.ts index 1b0c0d65a41a6..173bdb4bd820e 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-multisite-details/rgw-multisite-details.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-multisite-details/rgw-multisite-details.component.spec.ts @@ -2,6 +2,7 @@ import { HttpClientTestingModule } from '@angular/common/http/testing'; import { DebugElement } from '@angular/core'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { TreeModule } from '@circlon/angular-tree-component'; +import { ToastrModule } from 'ngx-toastr'; import { SharedModule } from '~/app/shared/shared.module'; import { RgwMultisiteDetailsComponent } from './rgw-multisite-details.component'; @@ -14,7 +15,7 @@ describe('RgwMultisiteDetailsComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ declarations: [RgwMultisiteDetailsComponent], - imports: [HttpClientTestingModule, TreeModule, SharedModule] + imports: [HttpClientTestingModule, TreeModule, SharedModule, ToastrModule.forRoot()] }).compileComponents(); }); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-multisite-details/rgw-multisite-details.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-multisite-details/rgw-multisite-details.component.ts index a07e45d11ac77..0a8e598f6e73b 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-multisite-details/rgw-multisite-details.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-multisite-details/rgw-multisite-details.component.ts @@ -12,15 +12,20 @@ import { forkJoin, Subscription } from 'rxjs'; import { RgwRealmService } from '~/app/shared/api/rgw-realm.service'; import { RgwZoneService } from '~/app/shared/api/rgw-zone.service'; import { RgwZonegroupService } from '~/app/shared/api/rgw-zonegroup.service'; +import { CriticalConfirmationModalComponent } from '~/app/shared/components/critical-confirmation-modal/critical-confirmation-modal.component'; import { ActionLabelsI18n, TimerServiceInterval } from '~/app/shared/constants/app.constants'; import { Icons } from '~/app/shared/enum/icons.enum'; +import { NotificationType } from '~/app/shared/enum/notification-type.enum'; import { CdTableAction } from '~/app/shared/models/cd-table-action'; import { CdTableSelection } from '~/app/shared/models/cd-table-selection'; import { Permission } from '~/app/shared/models/permissions'; import { AuthStorageService } from '~/app/shared/services/auth-storage.service'; import { ModalService } from '~/app/shared/services/modal.service'; +import { NotificationService } from '~/app/shared/services/notification.service'; import { TimerService } from '~/app/shared/services/timer.service'; import { RgwRealm, RgwZone, RgwZonegroup } from '../models/rgw-multisite'; +import { RgwMultisiteZoneDeletionFormComponent } from '../models/rgw-multisite-zone-deletion-form/rgw-multisite-zone-deletion-form.component'; +import { RgwMultisiteZonegroupDeletionFormComponent } from '../models/rgw-multisite-zonegroup-deletion-form/rgw-multisite-zonegroup-deletion-form.component'; import { RgwMultisiteRealmFormComponent } from '../rgw-multisite-realm-form/rgw-multisite-realm-form.component'; import { RgwMultisiteZoneFormComponent } from '../rgw-multisite-zone-form/rgw-multisite-zone-form.component'; import { RgwMultisiteZonegroupFormComponent } from '../rgw-multisite-zonegroup-form/rgw-multisite-zonegroup-form.component'; @@ -56,6 +61,7 @@ export class RgwMultisiteDetailsComponent implements OnDestroy, OnInit { } } }; + modalRef: NgbModalRef; realms: RgwRealm[] = []; zonegroups: RgwZonegroup[] = []; @@ -79,7 +85,8 @@ export class RgwMultisiteDetailsComponent implements OnDestroy, OnInit { public timerServiceVariable: TimerServiceInterval, public rgwRealmService: RgwRealmService, public rgwZonegroupService: RgwZonegroupService, - public rgwZoneService: RgwZoneService + public rgwZoneService: RgwZoneService, + private notificationService: NotificationService ) { this.permission = this.authStorageService.getPermissions().rgw; const createRealmAction: CdTableAction = { @@ -300,4 +307,35 @@ export class RgwMultisiteDetailsComponent implements OnDestroy, OnInit { } } } + + delete(node: TreeNode) { + if (node.data.type === 'realm') { + this.modalRef = this.modalService.show(CriticalConfirmationModalComponent, { + itemDescription: $localize`${node.data.type} ${node.data.name}`, + itemNames: [`${node.data.name}`], + submitAction: () => { + this.rgwRealmService.delete(node.data.name).subscribe( + () => { + this.modalRef.close(); + this.notificationService.show( + NotificationType.success, + $localize`Realm: '${node.data.name}' deleted successfully` + ); + }, + () => { + this.modalRef.componentInstance.stopLoadingSpinner(); + } + ); + } + }); + } else if (node.data.type === 'zonegroup') { + this.modalRef = this.modalService.show(RgwMultisiteZonegroupDeletionFormComponent, { + zonegroup: node.data + }); + } else if (node.data.type === 'zone') { + this.modalRef = this.modalService.show(RgwMultisiteZoneDeletionFormComponent, { + zone: node.data + }); + } + } } diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw.module.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw.module.ts index d7074b690dec5..2f41e8545a1f7 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw.module.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw.module.ts @@ -34,6 +34,8 @@ import { RgwMultisiteRealmFormComponent } from './rgw-multisite-realm-form/rgw-m import { RgwMultisiteZonegroupFormComponent } from './rgw-multisite-zonegroup-form/rgw-multisite-zonegroup-form.component'; import { RgwMultisiteZoneFormComponent } from './rgw-multisite-zone-form/rgw-multisite-zone-form.component'; import { CrudFormComponent } from '~/app/shared/forms/crud-form/crud-form.component'; +import { RgwMultisiteZoneDeletionFormComponent } from './models/rgw-multisite-zone-deletion-form/rgw-multisite-zone-deletion-form.component'; +import { RgwMultisiteZonegroupDeletionFormComponent } from './models/rgw-multisite-zonegroup-deletion-form/rgw-multisite-zonegroup-deletion-form.component'; @NgModule({ imports: [ @@ -77,7 +79,9 @@ import { CrudFormComponent } from '~/app/shared/forms/crud-form/crud-form.compon RgwMultisiteDetailsComponent, RgwMultisiteRealmFormComponent, RgwMultisiteZonegroupFormComponent, - RgwMultisiteZoneFormComponent + RgwMultisiteZoneFormComponent, + RgwMultisiteZoneDeletionFormComponent, + RgwMultisiteZonegroupDeletionFormComponent ] }) export class RgwModule {} diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/api/rgw-realm.service.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/api/rgw-realm.service.ts index bacdedc19dc58..63bb7b8c657f2 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/shared/api/rgw-realm.service.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/api/rgw-realm.service.ts @@ -52,6 +52,15 @@ export class RgwRealmService { }); } + delete(realmName: string): Observable { + return this.rgwDaemonService.request((params: HttpParams) => { + params = params.appendAll({ + realm_name: realmName + }); + return this.http.delete(`${this.url}/${realmName}`, { params: params }); + }); + } + getRealmTree(realm: RgwRealm, defaultRealmId: string) { let nodes = {}; let realmIds = []; diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/api/rgw-zone.service.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/api/rgw-zone.service.ts index 707154f247535..a8530c9558e77 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/shared/api/rgw-zone.service.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/api/rgw-zone.service.ts @@ -52,6 +52,17 @@ export class RgwZoneService { }); } + delete(zonegroupName: string, zoneName: string, deletePools: boolean): Observable { + return this.rgwDaemonService.request((params: HttpParams) => { + params = params.appendAll({ + zonegroup_name: zonegroupName, + zone_name: zoneName, + delete_pools: deletePools + }); + return this.http.delete(`${this.url}/${zoneName}`, { params: params }); + }); + } + getZoneTree(zone: RgwZone, defaultZoneId: string, zonegroup?: RgwZonegroup, realm?: RgwRealm) { let nodes = {}; let zoneIds = []; diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/api/rgw-zonegroup.service.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/api/rgw-zonegroup.service.ts index b16d0f7e928ed..c8bec73383747 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/shared/api/rgw-zonegroup.service.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/api/rgw-zonegroup.service.ts @@ -44,6 +44,16 @@ export class RgwZonegroupService { }); } + delete(zonegroupName: string, deletePools: boolean): Observable { + return this.rgwDaemonService.request((params: HttpParams) => { + params = params.appendAll({ + zonegroup_name: zonegroupName, + delete_pools: deletePools + }); + return this.http.delete(`${this.url}/${zonegroupName}`, { params: params }); + }); + } + getZonegroupTree(zonegroup: RgwZonegroup, defaultZonegroupId: string, realm?: RgwRealm) { let nodes = {}; nodes['id'] = zonegroup.id; diff --git a/src/pybind/mgr/dashboard/openapi.yaml b/src/pybind/mgr/dashboard/openapi.yaml index 856260a61d97c..dfb30d3a309b1 100644 --- a/src/pybind/mgr/dashboard/openapi.yaml +++ b/src/pybind/mgr/dashboard/openapi.yaml @@ -8275,6 +8275,42 @@ paths: tags: - RgwRealm /api/rgw/realm/{realm_name}: + delete: + parameters: + - in: path + name: realm_name + required: true + schema: + type: string + - allowEmptyValue: true + in: query + name: daemon_name + schema: + type: string + responses: + '202': + content: + application/vnd.ceph.api.v1.0+json: + type: object + description: Operation is still executing. Please check the task queue. + '204': + content: + application/vnd.ceph.api.v1.0+json: + type: object + description: Resource deleted. + '400': + description: Operation exception. Please check the response body for details. + '401': + description: Unauthenticated access. Please login first. + '403': + description: Unauthorized access. Please check your permissions. + '500': + description: Unexpected error. Please check the response body for the stack + trace. + security: + - jwt: [] + tags: + - RgwRealm get: parameters: - in: path @@ -9173,6 +9209,52 @@ paths: tags: - RgwZone /api/rgw/zone/{zone_name}: + delete: + parameters: + - in: path + name: zone_name + required: true + schema: + type: string + - in: query + name: zonegroup_name + required: true + schema: + type: string + - in: query + name: delete_pools + required: true + schema: + type: string + - allowEmptyValue: true + in: query + name: daemon_name + schema: + type: string + responses: + '202': + content: + application/vnd.ceph.api.v1.0+json: + type: object + description: Operation is still executing. Please check the task queue. + '204': + content: + application/vnd.ceph.api.v1.0+json: + type: object + description: Resource deleted. + '400': + description: Operation exception. Please check the response body for details. + '401': + description: Unauthenticated access. Please login first. + '403': + description: Unauthorized access. Please check your permissions. + '500': + description: Unexpected error. Please check the response body for the stack + trace. + security: + - jwt: [] + tags: + - RgwZone get: parameters: - in: path @@ -9301,6 +9383,47 @@ paths: tags: - RgwZonegroup /api/rgw/zonegroup/{zonegroup_name}: + delete: + parameters: + - in: path + name: zonegroup_name + required: true + schema: + type: string + - in: query + name: delete_pools + required: true + schema: + type: string + - allowEmptyValue: true + in: query + name: daemon_name + schema: + type: string + responses: + '202': + content: + application/vnd.ceph.api.v1.0+json: + type: object + description: Operation is still executing. Please check the task queue. + '204': + content: + application/vnd.ceph.api.v1.0+json: + type: object + description: Resource deleted. + '400': + description: Operation exception. Please check the response body for details. + '401': + description: Unauthenticated access. Please login first. + '403': + description: Unauthorized access. Please check your permissions. + '500': + description: Unexpected error. Please check the response body for the stack + trace. + security: + - jwt: [] + tags: + - RgwZonegroup get: parameters: - in: path diff --git a/src/pybind/mgr/dashboard/services/rgw_client.py b/src/pybind/mgr/dashboard/services/rgw_client.py index 836531b65f997..09719af20acfd 100644 --- a/src/pybind/mgr/dashboard/services/rgw_client.py +++ b/src/pybind/mgr/dashboard/services/rgw_client.py @@ -651,6 +651,16 @@ class RgwClient(RestClient): all_realms_info['default_realm'] = '' # type: ignore return all_realms_info + def delete_realm(self, realm_name: str): + rgw_delete_realm_cmd = ['realm', 'rm', '--rgw-realm', realm_name] + try: + exit_code, _, _ = mgr.send_rgwadmin_command(rgw_delete_realm_cmd) + if exit_code > 0: + raise DashboardException(msg='Unable to delete realm', + http_status_code=500, component='rgw') + except SubprocessError as error: + raise DashboardException(error, http_status_code=500, component='rgw') + def update_period(self): rgw_update_period_cmd = ['period', 'update', '--commit'] try: @@ -755,6 +765,23 @@ class RgwClient(RestClient): all_zonegroups_info['default_zonegroup'] = '' # type: ignore return all_zonegroups_info + def delete_zonegroup(self, zonegroup_name: str, delete_pools: str): + if delete_pools == 'true': + zonegroup_info = self.get_zonegroup(zonegroup_name) + rgw_delete_zonegroup_cmd = ['zonegroup', 'delete', '--rgw-zonegroup', zonegroup_name] + try: + exit_code, _, _ = mgr.send_rgwadmin_command(rgw_delete_zonegroup_cmd) + if exit_code > 0: + raise DashboardException(msg='Unable to delete zonegroup', + http_status_code=500, component='rgw') + except SubprocessError as error: + raise DashboardException(error, http_status_code=500, component='rgw') + self.update_period() + if delete_pools == 'true': + for zone in zonegroup_info['zones']: + zone_info = self.get_zone(zone['name']) + self.delete_zone_pools(zone['name'], zone_info) + def create_zone(self, zone_name, zonegroup_name, default, master, endpoints, user): if user != 'null': access_key, secret_key = _get_user_keys(user) @@ -835,6 +862,55 @@ class RgwClient(RestClient): all_zones_info['default_zone'] = '' # type: ignore return all_zones_info + def delete_zone(self, zonegroup_name: str, zone_name: str, delete_pools: str): + rgw_remove_zone_from_zonegroup_cmd = ['zonegroup', 'remove', '--rgw-zonegroup', + zonegroup_name, '--rgw-zone', zone_name] + rgw_delete_zone_cmd = ['zone', 'delete', '--rgw-zone', zone_name] + zone_info = self.get_zone(zone_name) + if zonegroup_name: + try: + exit_code, _, _ = mgr.send_rgwadmin_command(rgw_remove_zone_from_zonegroup_cmd) + if exit_code > 0: + raise DashboardException(msg='Unable to remove zone from zonegroup', + http_status_code=500, component='rgw') + except SubprocessError as error: + raise DashboardException(error, http_status_code=500, component='rgw') + self.update_period() + try: + exit_code, _, _ = mgr.send_rgwadmin_command(rgw_delete_zone_cmd) + if exit_code > 0: + raise DashboardException(msg='Unable to delete zone', + http_status_code=500, component='rgw') + except SubprocessError as error: + raise DashboardException(error, http_status_code=500, component='rgw') + self.update_period() + if delete_pools == 'true': + self.delete_zone_pools(zone_name, zone_info) + + def delete_zone_pools(self, zone_name, zone_info): + # pylint: disable=unused-variable + for key, value in zone_info.items(): + if zone_name in value: + if mgr.rados.pool_exists(value): + mgr.rados.delete_pool(value) + self.search_and_delete_pools(zone_info['placement_pools'], zone_name) + + def search_and_delete_pools(self, obj, pool_name): + if isinstance(obj, dict): + # pylint: disable=unused-variable + for key, value in obj.items(): + if isinstance(value, str) and pool_name in value: + if mgr.rados.pool_exists(value): + mgr.rados.delete_pool(value) + self.search_and_delete_pools(value, pool_name) + elif isinstance(obj, list): + for item in obj: + self.search_and_delete_pools(item, pool_name) + else: + if isinstance(obj, str) and pool_name in obj: + if mgr.rados.pool_exists(obj): + mgr.rados.delete_pool(obj) + def get_multisite_status(self): is_multisite_configured = True rgw_realm_list = self.list_realms() -- 2.39.5