]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/dashboard: Administration > Configuration > Some of the config options are not...
authorNaman Munet <namanmunet@li-ff83bccc-26af-11b2-a85c-a4b04bfb1003.ibm.com>
Fri, 22 Nov 2024 09:57:44 +0000 (15:27 +0530)
committerNaman Munet <naman.munet@ibm.com>
Fri, 27 Dec 2024 06:49:39 +0000 (12:19 +0530)
Fixes: https://tracker.ceph.com/issues/68976
Fixes Includes:
1) by-passing 'can_update_at_runtime' flag for 'rgw' related configurations as the same can be updated at runtime via CLI.
Also implemented a warning popup for user to make force edit to rgw related configurations.

Signed-off-by: Naman Munet <naman.munet@ibm.com>
(cherry picked from commit 3181acc223dafd04e3fc56d418389ad50c5868e4)

qa/tasks/mgr/dashboard/test_rbd.py
src/pybind/mgr/dashboard/controllers/cluster_configuration.py
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/configuration/configuration-form/configuration-form-create-request.model.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/configuration/configuration-form/configuration-form.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/configuration/configuration-form/configuration-form.component.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/configuration/configuration.component.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/components/config-option/config-option.model.ts
src/pybind/mgr/dashboard/openapi.yaml

index c2ffbd48e8a8507fc81a793ff29736c4fc500599..e5ccaaffedd4b6ab6b6174334f790299a4da56d1 100644 (file)
@@ -859,7 +859,19 @@ class RbdTest(DashboardTestCase):
         self.assertEqual(clone_format_version, 2)
         self.assertStatus(200)
 
+        # if empty list is sent, then the config will remain as it is
         value = []
+        res = [{'section': "global", 'value': "2"}]
+        self._post('/api/cluster_conf', {
+            'name': config_name,
+            'value': value
+        })
+        self.wait_until_equal(
+            lambda: _get_config_by_name(config_name),
+            res,
+            timeout=60)
+
+        value = [{'section': "global", 'value': ""}]
         self._post('/api/cluster_conf', {
             'name': config_name,
             'value': value
index da5be2cc81d6a5a8a8ec3fa049efe97a49f6a101..292f381d79f0138a65e025b0d10ac61b3a5c5f88 100644 (file)
@@ -1,12 +1,14 @@
 # -*- coding: utf-8 -*-
 
+from typing import Optional
+
 import cherrypy
 
 from .. import mgr
 from ..exceptions import DashboardException
 from ..security import Scope
 from ..services.ceph_service import CephService
-from . import APIDoc, APIRouter, EndpointDoc, RESTController
+from . import APIDoc, APIRouter, EndpointDoc, Param, RESTController
 
 FILTER_SCHEMA = [{
     "name": (str, 'Name of the config option'),
@@ -80,22 +82,33 @@ class ClusterConfiguration(RESTController):
 
         return config_options
 
-    def create(self, name, value):
+    @EndpointDoc("Create/Update Cluster Configuration",
+                 parameters={
+                     'name': Param(str, 'Config option name'),
+                     'value': (
+                         [
+                            {
+                                'section': Param(
+                                    str, 'Section/Client where config needs to be updated'
+                                ),
+                                'value': Param(str, 'Value of the config option')
+                            }
+                         ], 'Section and Value of the config option'
+                     ),
+                     'force_update': Param(bool, 'Force update the config option', False, None)
+                 }
+                 )
+    def create(self, name, value, force_update: Optional[bool] = None):
         # Check if config option is updateable at runtime
-        self._updateable_at_runtime([name])
+        self._updateable_at_runtime([name], force_update)
 
-        # Update config option
-        avail_sections = ['global', 'mon', 'mgr', 'osd', 'mds', 'client']
+        for entry in value:
+            section = entry['section']
+            entry_value = entry['value']
 
-        for section in avail_sections:
-            for entry in value:
-                if entry['value'] is None:
-                    break
-
-                if entry['section'] == section:
-                    CephService.send_command('mon', 'config set', who=section, name=name,
-                                             value=str(entry['value']))
-                    break
+            if entry_value not in (None, ''):
+                CephService.send_command('mon', 'config set', who=section, name=name,
+                                         value=str(entry_value))
             else:
                 CephService.send_command('mon', 'config rm', who=section, name=name)
 
@@ -116,11 +129,24 @@ class ClusterConfiguration(RESTController):
 
         raise cherrypy.HTTPError(404)
 
-    def _updateable_at_runtime(self, config_option_names):
+    def _updateable_at_runtime(self, config_option_names, force_update=False):
         not_updateable = []
 
         for name in config_option_names:
             config_option = self._get_config_option(name)
+
+            # making rgw configuration to be editable by bypassing 'can_update_at_runtime'
+            # as the same can be done via CLI.
+            if force_update and 'rgw' in name and not config_option['can_update_at_runtime']:
+                break
+
+            if force_update and 'rgw' not in name and not config_option['can_update_at_runtime']:
+                raise DashboardException(
+                    msg=f'Only the configuration containing "rgw" can be edited at runtime with'
+                        f' force_update flag, hence not able to update "{name}"',
+                    code='config_option_not_updatable_at_runtime',
+                    component='cluster_configuration'
+                )
             if not config_option['can_update_at_runtime']:
                 not_updateable.append(name)
 
index 741c18d52a610e10ae0f6f9cb39871eb99719fe1..a6775dcee1750456d9237b4e4c1ca5406ca56b41 100644 (file)
       </div>
       <!-- Footer -->
       <div class="card-footer">
-        <cd-form-button-panel (submitActionEvent)="submit()"
+        <cd-form-button-panel (submitActionEvent)="forceUpdate ? openCriticalConfirmModal() : submit()"
                               [form]="configForm"
                               [submitText]="actionLabels.UPDATE"
                               wrappingClass="text-right"></cd-form-button-panel>
     </div>
   </form>
 </div>
+
index b6e9e700be464250c91d66dd7f6380ba0a46bdb8..f95479fc53e012258af2e2668437622000015087 100644 (file)
@@ -13,6 +13,10 @@ import { CdForm } from '~/app/shared/forms/cd-form';
 import { CdFormGroup } from '~/app/shared/forms/cd-form-group';
 import { NotificationService } from '~/app/shared/services/notification.service';
 import { ConfigFormCreateRequestModel } from './configuration-form-create-request.model';
+import { CriticalConfirmationModalComponent } from '~/app/shared/components/critical-confirmation-modal/critical-confirmation-modal.component';
+import { ModalService } from '~/app/shared/services/modal.service';
+
+const RGW = 'rgw';
 
 @Component({
   selector: 'cd-configuration-form',
@@ -29,13 +33,15 @@ export class ConfigurationFormComponent extends CdForm implements OnInit {
   maxValue: number;
   patternHelpText: string;
   availSections = ['global', 'mon', 'mgr', 'osd', 'mds', 'client'];
+  forceUpdate: boolean;
 
   constructor(
     public actionLabels: ActionLabelsI18n,
     private route: ActivatedRoute,
     private router: Router,
     private configService: ConfigurationService,
-    private notificationService: NotificationService
+    private notificationService: NotificationService,
+    private modalService: ModalService
   ) {
     super();
     this.createForm();
@@ -95,7 +101,6 @@ export class ConfigurationFormComponent extends CdForm implements OnInit {
   setResponse(response: ConfigFormModel) {
     this.response = response;
     const validators = this.getValidators(response);
-
     this.configForm.get('name').setValue(response.name);
     this.configForm.get('desc').setValue(response.desc);
     this.configForm.get('long_desc').setValue(response.long_desc);
@@ -118,7 +123,7 @@ export class ConfigurationFormComponent extends CdForm implements OnInit {
         this.configForm.get('values').get(value.section).setValue(sectionValue);
       });
     }
-
+    this.forceUpdate = !this.response.can_update_at_runtime && response.name.includes(RGW);
     this.availSections.forEach((section) => {
       this.configForm.get('values').get(section).setValidators(validators);
     });
@@ -134,7 +139,7 @@ export class ConfigurationFormComponent extends CdForm implements OnInit {
 
     this.availSections.forEach((section) => {
       const sectionValue = this.configForm.getValue(section);
-      if (sectionValue !== null && sectionValue !== '') {
+      if (sectionValue !== null) {
         values.push({ section: section, value: sectionValue });
       }
     });
@@ -143,12 +148,28 @@ export class ConfigurationFormComponent extends CdForm implements OnInit {
       const request = new ConfigFormCreateRequestModel();
       request.name = this.configForm.getValue('name');
       request.value = values;
+      if (this.forceUpdate) {
+        request.force_update = this.forceUpdate;
+      }
       return request;
     }
 
     return null;
   }
 
+  openCriticalConfirmModal() {
+    this.modalService.show(CriticalConfirmationModalComponent, {
+      buttonText: $localize`Force Edit`,
+      actionDescription: $localize`force edit`,
+      itemDescription: $localize`configuration`,
+      infoMessage: 'Updating this configuration might require restarting the client',
+      submitAction: () => {
+        this.modalService.dismissAll();
+        this.submit();
+      }
+    });
+  }
+
   submit() {
     const request = this.createRequest();
 
index a57603d4c8aa5b562cbac1cf003b0509e1a3b516..d72b5922854e7429103846d980163aed921b4fd1 100644 (file)
@@ -12,6 +12,8 @@ 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';
 
+const RGW = 'rgw';
+
 @Component({
   selector: 'cd-configuration',
   templateUrl: './configuration.component.html',
@@ -143,7 +145,9 @@ export class ConfigurationComponent extends ListWithDetails implements OnInit {
     if (selection.selected.length !== 1) {
       return false;
     }
-
-    return selection.selected[0].can_update_at_runtime;
+    if ((this.selection.selected[0].name as string).includes(RGW)) {
+      return true;
+    }
+    return this.selection.selected[0].can_update_at_runtime;
   }
 }
index d3ebc5f37c63dffad2ba02f973f4858360c30a56..0e1c0906f4ac1e4e632188d9174d6d06d9781283 100644 (file)
@@ -9,4 +9,5 @@ export class ConfigFormModel {
   min: any;
   max: any;
   services: Array<string>;
+  can_update_at_runtime: boolean;
 }
index c368c878d7ec984da16fe487021ccdfb3b948fee..560ed870e6fe31fc655174f1d3306d6aeff35753 100644 (file)
@@ -3885,10 +3885,27 @@ paths:
           application/json:
             schema:
               properties:
+                force_update:
+                  description: Force update the config option
+                  type: boolean
                 name:
+                  description: Config option name
                   type: string
                 value:
-                  type: string
+                  description: Section and Value of the config option
+                  items:
+                    properties:
+                      section:
+                        description: Section/Client where config needs to be updated
+                        type: string
+                      value:
+                        description: Value of the config option
+                        type: string
+                    required:
+                    - section
+                    - value
+                    type: object
+                  type: array
               required:
               - name
               - value
@@ -3915,6 +3932,7 @@ paths:
             trace.
       security:
       - jwt: []
+      summary: Create/Update Cluster Configuration
       tags:
       - ClusterConfiguration
     put: