]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/dashboard: delete smb cluster 60935/head
authorDnyaneshwari <dnyaneshwari@li-9c9fbecc-2d5c-11b2-a85c-e2a7cc8a424f.ibm.com>
Wed, 4 Dec 2024 05:51:11 +0000 (11:21 +0530)
committerDnyaneshwari <dnyaneshwari@li-9c9fbecc-2d5c-11b2-a85c-e2a7cc8a424f.ibm.com>
Mon, 27 Jan 2025 08:59:40 +0000 (14:29 +0530)
Fixes: https://tracker.ceph.com/issues/69191
       https://tracker.ceph.com/issues/69605

Signed-off-by: Dnyaneshwari Talwekar <dtalweka@redhat.com>
src/pybind/mgr/dashboard/controllers/smb.py
src/pybind/mgr/dashboard/frontend/src/app/ceph/smb/smb-cluster-list/smb-cluster-list.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/smb/smb-cluster-list/smb-cluster-list.component.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/api/smb.service.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/api/smb.service.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/services/task-message.service.ts
src/pybind/mgr/dashboard/openapi.yaml
src/pybind/mgr/dashboard/tests/test_smb.py

index 97eff8c3dfec43ebda277fde0f410dbfe3b07b37..c171864145609cb4edd7249c51fa0c781d91b8b1 100644 (file)
@@ -137,6 +137,24 @@ class SMBCluster(RESTController):
         except RuntimeError as e:
             raise DashboardException(e, component='smb')
 
+    @DeletePermission
+    @EndpointDoc("Remove an smb cluster",
+                 parameters={
+                     'cluster_id': (str, 'Unique identifier for the cluster')},
+                 responses={204: None})
+    def delete(self, cluster_id: str):
+        """
+        Remove an smb cluster
+
+        :param cluster_id: Cluster identifier
+        :return: None.
+        """
+        resource = {}
+        resource['resource_type'] = self._resource
+        resource['cluster_id'] = cluster_id
+        resource['intent'] = Intent.REMOVED
+        return mgr.remote('smb', 'apply_resources', json.dumps(resource)).one().to_simplified()
+
 
 @APIRouter('/smb/share', Scope.SMB)
 @APIDoc("SMB Share Management API", "SMB")
index 1a73e58cdd28f3156bae21b48d1b0cb0d1589b67..cfd7b65dbf47d13db6993312e7bcbf7aa388676f 100644 (file)
     [hasDetails]="false"
     (setExpandedRow)="setExpandedRow($event)"
     (fetchData)="loadSMBCluster($event)"
+    (updateSelection)="updateSelection($event)"
   >
+    <div class="table-actions">
+      <cd-table-actions
+        class="btn-group"
+        [permission]="permission"
+        [selection]="selection"
+        [tableActions]="tableActions"
+      >
+      </cd-table-actions>
+    </div>
   </cd-table>
 </ng-container>
index bf61643a0cc775d91454281b15e11c39bda52ba5..aa603730bbd1c1cff5b9df6b70550f9da4ca4010 100644 (file)
@@ -15,6 +15,13 @@ import { Permission } from '~/app/shared/models/permissions';
 import { AuthStorageService } from '~/app/shared/services/auth-storage.service';
 import { SmbService } from '~/app/shared/api/smb.service';
 import { SMBCluster } from '../smb.model';
+import { Icons } from '~/app/shared/enum/icons.enum';
+import { CdTableSelection } from '~/app/shared/models/cd-table-selection';
+import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
+import { ModalCdsService } from '~/app/shared/services/modal-cds.service';
+import { CriticalConfirmationModalComponent } from '~/app/shared/components/critical-confirmation-modal/critical-confirmation-modal.component';
+import { TaskWrapperService } from '~/app/shared/services/task-wrapper.service';
+import { FinishedTask } from '~/app/shared/models/finished-task';
 
 @Component({
   selector: 'cd-smb-cluster-list',
@@ -28,17 +35,28 @@ export class SmbClusterListComponent extends ListWithDetails implements OnInit {
   permission: Permission;
   tableActions: CdTableAction[];
   context: CdTableFetchDataContext;
-
   smbClusters$: Observable<SMBCluster[]>;
   subject$ = new BehaviorSubject<SMBCluster[]>([]);
+  selection = new CdTableSelection();
+  modalRef: NgbModalRef;
 
   constructor(
     private authStorageService: AuthStorageService,
     public actionLabels: ActionLabelsI18n,
-    private smbService: SmbService
+    private smbService: SmbService,
+    private modalService: ModalCdsService,
+    private taskWrapper: TaskWrapperService
   ) {
     super();
     this.permission = this.authStorageService.getPermissions().smb;
+    this.tableActions = [
+      {
+        permission: 'delete',
+        icon: Icons.destroy,
+        click: () => this.removeSMBClusterModal(),
+        name: this.actionLabels.REMOVE
+      }
+    ];
   }
 
   ngOnInit() {
@@ -70,4 +88,25 @@ export class SmbClusterListComponent extends ListWithDetails implements OnInit {
   loadSMBCluster() {
     this.subject$.next([]);
   }
+
+  updateSelection(selection: CdTableSelection) {
+    this.selection = selection;
+  }
+
+  removeSMBClusterModal() {
+    const cluster_id = this.selection.first().cluster_id;
+
+    this.modalService.show(CriticalConfirmationModalComponent, {
+      itemDescription: $localize`Cluster`,
+      itemNames: [cluster_id],
+      actionDescription: $localize`remove`,
+      submitActionObservable: () =>
+        this.taskWrapper.wrapTaskAroundCall({
+          task: new FinishedTask('smb/cluster/remove', {
+            cluster_id: cluster_id
+          }),
+          call: this.smbService.removeCluster(cluster_id)
+        })
+    });
+  }
 }
index 2dcdbdb6402a9db63c641323b7c27177754a252a..f20977330d14193ad78d49d3867eff2818354ed5 100644 (file)
@@ -28,4 +28,10 @@ describe('SmbService', () => {
     const req = httpTesting.expectOne('api/smb/cluster');
     expect(req.request.method).toBe('GET');
   });
+
+  it('should call remove', () => {
+    service.removeCluster('cluster_1').subscribe();
+    const req = httpTesting.expectOne('api/smb/cluster/cluster_1');
+    expect(req.request.method).toBe('DELETE');
+  });
 });
index 4f4ebcb423c4b956f6fa914c4d6361cecf350155..ba640d11451f73a9702aee8f4af0681a9b35b9cc 100644 (file)
@@ -15,4 +15,10 @@ export class SmbService {
   listClusters(): Observable<SMBCluster[]> {
     return this.http.get<SMBCluster[]>(`${this.baseURL}/cluster`);
   }
+
+  removeCluster(clusterId: string) {
+    return this.http.delete(`${this.baseURL}/cluster/${clusterId}`, {
+      observe: 'response'
+    });
+  }
 }
index cf7662eac65e95ca6c93b970f095e2ca68fcaa32..e0711ead95d88d373e0a2314f99b6ba34658c445 100644 (file)
@@ -396,6 +396,10 @@ export class TaskMessageService {
     'nfs/delete': this.newTaskMessage(this.commonOperations.delete, (metadata) =>
       this.nfs(metadata)
     ),
+    // smb
+    'smb/cluster/remove': this.newTaskMessage(this.commonOperations.remove, (metadata) =>
+      this.smb(metadata)
+    ),
     // Grafana tasks
     'grafana/dashboards/update': this.newTaskMessage(
       this.commonOperations.update,
@@ -539,6 +543,10 @@ export class TaskMessageService {
     }'`;
   }
 
+  smb(metadata: { cluster_id: string }) {
+    return $localize`SMB Cluster '${metadata.cluster_id}'`;
+  }
+
   service(metadata: any) {
     return $localize`service '${metadata.service_name}'`;
   }
index de8362980642914d4b639a537c1c31fb336ca358..49b34ea52012200f5862c9f915be37387c42e107 100644 (file)
@@ -14313,6 +14313,43 @@ paths:
       tags:
       - SMB
   /api/smb/cluster/{cluster_id}:
+    delete:
+      description: "\n        Remove an smb cluster\n\n        :param cluster_id:\
+        \ Cluster identifier\n        :return: None.\n        "
+      parameters:
+      - description: Unique identifier for the cluster
+        in: path
+        name: cluster_id
+        required: true
+        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:
+              schema:
+                properties: {}
+                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: []
+      summary: Remove an smb cluster
+      tags:
+      - SMB
     get:
       description: "\n        Get an smb cluster by cluster id\n        "
       parameters:
index 754df482add9820dfcbcea7c320e09ce8229d7a0..9a577709d066da33c0ccd749c960f7c22e41a238 100644 (file)
@@ -121,6 +121,27 @@ class SMBClusterTest(ControllerTestCase):
         self.assertStatus(201)
         self.assertInJsonBody(json.dumps(self._clusters['resources'][1]))
 
+    def test_remove(self):
+        _res = {
+            "resource": {
+                "resource_type": "ceph.smb.cluster",
+                "cluster_id": "smbRemoveCluster",
+                "intent": "removed"
+            },
+            "state": "removed",
+            "success": "true"
+        }
+        _res_simplified = {
+            "resource_type": "ceph.smb.cluster",
+            "cluster_id": "smbRemoveCluster",
+            "intent": "removed"
+        }
+        mgr.remote = Mock(return_value=Mock(return_value=_res))
+        mgr.remote.return_value.one.return_value.to_simplified = Mock(return_value=_res_simplified)
+        self._delete(f'{self._endpoint}/smbRemoveCluster')
+        self.assertStatus(204)
+        mgr.remote.assert_called_once_with('smb', 'apply_resources', json.dumps(_res_simplified))
+
 
 class SMBShareTest(ControllerTestCase):
     _endpoint = '/api/smb/share'