]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/dashboard: provide option to enable pool based mirroring mode while 57755/head
authorAashish Sharma <aasharma@li-e74156cc-2f67-11b2-a85c-e98659a63c5c.ibm.com>
Wed, 29 May 2024 04:34:13 +0000 (10:04 +0530)
committerAashish Sharma <aasharma@li-e74156cc-2f67-11b2-a85c-e98659a63c5c.ibm.com>
Fri, 7 Jun 2024 05:10:44 +0000 (10:40 +0530)
creating a pool

Fixes: https://tracker.ceph.com/issues/66267
Signed-off-by: Aashish Sharma <aasharma@redhat.com>
src/pybind/mgr/dashboard/controllers/pool.py
src/pybind/mgr/dashboard/controllers/rbd_mirroring.py
src/pybind/mgr/dashboard/frontend/cypress/e2e/pools/pools.e2e-spec.ts
src/pybind/mgr/dashboard/frontend/cypress/e2e/pools/pools.po.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-form/pool-form.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-form/pool-form.component.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-form/pool-form.component.ts
src/pybind/mgr/dashboard/openapi.yaml

index 9310d2f97aab188914dc147ef7e9cd5d27babff1..5c25c8b2a5d36a1c97a40e0274c52fc4a4a53d28 100644 (file)
@@ -14,6 +14,7 @@ from ..services.rbd import RbdConfiguration
 from ..tools import TaskManager, str_to_bool
 from . import APIDoc, APIRouter, Endpoint, EndpointDoc, ReadPermission, \
     RESTController, Task, UIRouter
+from .rbd_mirroring import RbdMirroringPoolMode
 
 POOL_SCHEMA = ([{
     "pool": (int, "pool id"),
@@ -169,25 +170,38 @@ class Pool(RESTController):
                                         yes_i_really_really_mean_it=True)
 
     @pool_task('edit', ['{pool_name}'])
-    def set(self, pool_name, flags=None, application_metadata=None, configuration=None, **kwargs):
+    def set(self, pool_name, flags=None, application_metadata=None, configuration=None,
+            rbd_mirroring=None, **kwargs):
         self._set_pool_values(pool_name, application_metadata, flags, True, kwargs)
         if kwargs.get('pool'):
             pool_name = kwargs['pool']
         RbdConfiguration(pool_name).set_configuration(configuration)
+        if rbd_mirroring is not None:
+            self._set_mirroring_mode(rbd_mirroring, pool_name)
         self._wait_for_pgs(pool_name)
 
     @pool_task('create', {'pool_name': '{pool}'})
     @handle_send_command_error('pool')
     def create(self, pool, pg_num, pool_type, erasure_code_profile=None, flags=None,
-               application_metadata=None, rule_name=None, configuration=None, **kwargs):
+               application_metadata=None, rule_name=None, configuration=None,
+               rbd_mirroring=None, **kwargs):
         ecp = erasure_code_profile if erasure_code_profile else None
         CephService.send_command('mon', 'osd pool create', pool=pool, pg_num=int(pg_num),
                                  pgp_num=int(pg_num), pool_type=pool_type, erasure_code_profile=ecp,
                                  rule=rule_name)
         self._set_pool_values(pool, application_metadata, flags, False, kwargs)
         RbdConfiguration(pool).set_configuration(configuration)
+        if rbd_mirroring is not None:
+            self._set_mirroring_mode(rbd_mirroring, pool)
         self._wait_for_pgs(pool)
 
+    def _set_mirroring_mode(self, mirroring_enabled, pool):
+        rbd_mirroring = RbdMirroringPoolMode()
+        if str_to_bool(mirroring_enabled):
+            rbd_mirroring.set_pool_mirror_mode(pool, 'pool')
+        else:
+            rbd_mirroring.set_pool_mirror_mode(pool, 'disabled')
+
     def _set_pool_values(self, pool, application_metadata, flags, update_existing, kwargs):
         current_pool = self._get(pool)
         if update_existing and kwargs.get('compression_mode') == 'unset':
index af30e8415eb795e4d13d3d950af3fcfc4355b9c5..1e80de74b3b94da79cf191ec32c0d7c210d88980 100644 (file)
@@ -11,7 +11,6 @@ import cherrypy
 import rbd
 
 from .. import mgr
-from ..controllers.pool import RBDPool
 from ..controllers.service import Service
 from ..security import Scope
 from ..services.ceph_service import CephService
@@ -507,6 +506,9 @@ class RbdMirroringPoolMode(RESTController):
 
     @RbdMirroringTask('pool/edit', {'pool_name': '{pool_name}'}, 5.0)
     def set(self, pool_name, mirror_mode=None):
+        return self.set_pool_mirror_mode(pool_name, mirror_mode)
+
+    def set_pool_mirror_mode(self, pool_name, mirror_mode):
         def _edit(ioctx, mirror_mode=None):
             if mirror_mode:
                 mode_enum = {x[1]: x[0] for x in
@@ -674,6 +676,8 @@ class RbdMirroringStatus(BaseController):
     @EndpointDoc('Configure RBD Mirroring')
     @CreatePermission
     def configure(self):
+        from ..controllers.pool import RBDPool  # to avoid circular import
+
         rbd_pool = RBDPool()
         service = Service()
 
index dd4ab6f3b75a310c731e2245f9abac3d5b58fef0..536f5fbadc739da46111c8807da6b61a6daf5cfd 100644 (file)
@@ -28,14 +28,14 @@ describe('Pools page', () => {
   });
 
   describe('Create, update and destroy', () => {
-    it('should create a pool', () => {
+    it('should create a pool with mirroring enabled', () => {
       pools.existTableCell(poolName, false);
       pools.navigateTo('create');
       pools.create(poolName, 8, 'rbd');
       pools.existTableCell(poolName);
     });
 
-    it('should edit a pools placement group', () => {
+    it('should edit a pools placement group and check if mirroring is enabled', () => {
       pools.existTableCell(poolName);
       pools.edit_pool_pg(poolName, 32);
     });
index af46355ff1c51032033c614bea9473d11924cb55..b1c0263dde123ecb6da514bf37052cbe03788364 100644 (file)
@@ -25,6 +25,7 @@ export class PoolPageHelper extends PageHelper {
     this.selectOption('pgAutoscaleMode', 'off'); // To show pgNum field
     cy.get('input[name=pgNum]').clear().type(`${placement_groups}`);
     this.setApplications(apps);
+    cy.get('#rbdMirroring').check({ force: true });
     cy.get('cd-submit-button').click();
   }
 
@@ -32,6 +33,8 @@ export class PoolPageHelper extends PageHelper {
     this.isPowerOf2(new_pg);
     this.navigateEdit(name);
 
+    cy.get('#rbdMirroring').should('be.checked');
+
     cy.get('input[name=pgNum]').clear().type(`${new_pg}`);
     cy.get('cd-submit-button').click();
     const str = `${new_pg} active+clean`;
index 13103da324aab5cc3194e57701c93b96773942d7..4dcc8171ccbdb1c2c1c09d28a175803905c915b4 100644 (file)
             </i>
           </div>
         </div>
+        <!-- Mirroring -->
+        <div class="form-group row"
+             *ngIf="data.applications.selected.includes('rbd')">
+          <div class="cd-col-form-offset">
+            <div class="custom-control custom-checkbox">
+              <input class="custom-control-input"
+                     id="rbdMirroring"
+                     name="rbdMirroring"
+                     type="checkbox"
+                     formControlName="rbdMirroring">
+              <label class="custom-control-label"
+                     for="rbdMirroring"
+                     i18n>Mirroring</label>
+              <cd-help-text>
+                <span i18n>Check this option to enable Pool based mirroring on a Block(RBD) pool.</span>
+              </cd-help-text>
+            </div>
+          </div>
+        </div>
         <!-- CRUSH -->
         <div *ngIf="isErasure || isReplicated">
 
index 7e2bccb32dd2d10f492a0088383f862e7635c31a..5556e4b2df44a2924223b283eed4c6cc4f06acbe 100644 (file)
@@ -1384,7 +1384,8 @@ describe('PoolFormComponent', () => {
               compression_max_blob_size: 0,
               compression_min_blob_size: 0,
               compression_required_ratio: 0,
-              pool: 'somePoolName'
+              pool: 'somePoolName',
+              rbd_mirroring: false
             },
             'pool/edit',
             'update'
@@ -1397,7 +1398,8 @@ describe('PoolFormComponent', () => {
             {
               application_metadata: ['ownApp', 'rbd'],
               compression_mode: 'unset',
-              pool: 'somePoolName'
+              pool: 'somePoolName',
+              rbd_mirroring: false
             },
             'pool/edit',
             'update'
index c91ca765367259823e54c319d21b824b0a121d18..c46e1b33bd766fdeacfa37e32e90de19165f097c 100644 (file)
@@ -37,6 +37,8 @@ import { CrushRuleFormModalComponent } from '../crush-rule-form-modal/crush-rule
 import { ErasureCodeProfileFormModalComponent } from '../erasure-code-profile-form/erasure-code-profile-form-modal.component';
 import { Pool } from '../pool';
 import { PoolFormData } from './pool-form-data';
+import { PoolEditModeResponseModel } from '../../block/mirroring/pool-edit-mode-modal/pool-edit-mode-response.model';
+import { RbdMirroringService } from '~/app/shared/api/rbd-mirroring.service';
 
 interface FormFieldDescription {
   externalFieldName: string;
@@ -97,7 +99,8 @@ export class PoolFormComponent extends CdForm implements OnInit {
     private taskWrapper: TaskWrapperService,
     private ecpService: ErasureCodeProfileService,
     private crushRuleService: CrushRuleService,
-    public actionLabels: ActionLabelsI18n
+    public actionLabels: ActionLabelsI18n,
+    private rbdMirroringService: RbdMirroringService
   ) {
     super();
     this.editing = this.router.url.startsWith(`/pool/${URLVerbs.EDIT}`);
@@ -176,7 +179,8 @@ export class PoolFormComponent extends CdForm implements OnInit {
         ecOverwrites: new UntypedFormControl(false),
         compression: compressionForm,
         max_bytes: new UntypedFormControl(''),
-        max_objects: new UntypedFormControl(0)
+        max_objects: new UntypedFormControl(0),
+        rbdMirroring: new UntypedFormControl(false)
       },
       [CdValidators.custom('form', (): null => null)]
     );
@@ -284,6 +288,11 @@ export class PoolFormComponent extends CdForm implements OnInit {
     this.data.pgs = this.form.getValue('pgNum');
     this.setAvailableApps(this.data.applications.default.concat(pool.application_metadata));
     this.data.applications.selected = pool.application_metadata;
+    this.rbdMirroringService
+      .getPool(pool.pool_name)
+      .subscribe((resp: PoolEditModeResponseModel) => {
+        this.form.get('rbdMirroring').setValue(resp.mirror_mode === 'pool');
+      });
   }
 
   private setAvailableApps(apps: string[] = this.data.applications.default) {
@@ -775,7 +784,14 @@ export class PoolFormComponent extends CdForm implements OnInit {
         formControlName: 'max_objects',
         editable: true,
         resetValue: this.editing ? 0 : undefined
-      }
+      },
+      this.data.applications.selected.includes('rbd')
+        ? { externalFieldName: 'rbd_mirroring', formControlName: 'rbdMirroring' }
+        : {
+            externalFieldName: 'rbd_mirroring',
+            formControlName: 'rbdMirroring',
+            resetValue: undefined
+          }
     ]);
 
     if (this.info.is_all_bluestore) {
@@ -840,6 +856,9 @@ export class PoolFormComponent extends CdForm implements OnInit {
     const apps = this.data.applications.selected;
     if (apps.length > 0 || this.editing) {
       pool['application_metadata'] = apps;
+      if (apps.includes('rbd')) {
+        pool['rbd_mirroring'] = this.form.getValue('rbdMirroring');
+      }
     }
 
     // Only collect configuration data for replicated pools, as QoS cannot be configured on EC
@@ -892,10 +911,11 @@ export class PoolFormComponent extends CdForm implements OnInit {
   }
 
   private triggerApiTask(pool: Record<string, any>) {
+    const poolName = pool.hasOwnProperty('srcpool') ? pool.srcpool : pool.pool;
     this.taskWrapper
       .wrapTaskAroundCall({
         task: new FinishedTask('pool/' + (this.editing ? URLVerbs.EDIT : URLVerbs.CREATE), {
-          pool_name: pool.hasOwnProperty('srcpool') ? pool.srcpool : pool.pool
+          pool_name: poolName
         }),
         call: this.poolService[this.editing ? URLVerbs.UPDATE : URLVerbs.CREATE](pool)
       })
index 1c004bd8b3c7006a72a41bab03b4ef1c22de4002..0432aed55bf40187907076027e9949a0d0a12b59 100644 (file)
@@ -10246,6 +10246,8 @@ paths:
                   type: string
                 flags:
                   type: string
+                rbd_mirroring:
+                  type: string
               type: object
       responses:
         '200':