]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/dashboard: add config options to documentation page 21460/head
authorTatjana Dehler <tdehler@suse.com>
Wed, 23 May 2018 14:35:51 +0000 (16:35 +0200)
committerTatjana Dehler <tdehler@suse.com>
Wed, 13 Jun 2018 13:39:24 +0000 (15:39 +0200)
This commit adds the config options stored by the MON database to the
configuration documentation page.
One can filter for these config options by setting the 'Source' filter
to 'mon' on the configuration documentation page.

Signed-off-by: Tatjana Dehler <tdehler@suse.com>
qa/tasks/mgr/dashboard/test_cluster_configuration.py
src/pybind/mgr/dashboard/controllers/cluster_configuration.py
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/configuration/configuration.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/configuration/configuration.component.ts

index 6cfe605e9474d6d9a623d4ca64985e4b1a4adf1c..d0e8c450c5694838c39feb60fae10a23e12800f8 100644 (file)
@@ -1,5 +1,7 @@
 from __future__ import absolute_import
 
+import time
+
 from .helper import DashboardTestCase, authenticate
 
 
@@ -23,6 +25,22 @@ class ClusterConfigurationTest(DashboardTestCase):
         data = self._get('/api/cluster_conf/fantasy_name')
         self.assertStatus(404)
 
+    @authenticate
+    def test_get_specific_db_config_option(self):
+        def _get_mon_allow_pool_delete_config():
+            data = self._get('/api/cluster_conf/mon_allow_pool_delete')
+            return data['value'][0]
+
+        self._ceph_cmd(['config', 'set', 'mon', 'mon_allow_pool_delete', 'true'])
+        result = self._wait_for_expected_get_result(_get_mon_allow_pool_delete_config,
+                                                    {'section': 'mon', 'value': 'true'})
+        self.assertEqual(result, {'section': 'mon', 'value': 'true'})
+
+        self._ceph_cmd(['config', 'set', 'mon', 'mon_allow_pool_delete', 'false'])
+        result = self._wait_for_expected_get_result(_get_mon_allow_pool_delete_config,
+                                                    {'section': 'mon', 'value': 'false'})
+        self.assertEqual(result, {'section': 'mon', 'value': 'false'})
+
     def _validate_single(self, data):
         self.assertIn('name', data)
         self.assertIn('daemon_default', data)
@@ -37,3 +55,23 @@ class ClusterConfigurationTest(DashboardTestCase):
         self.assertIn('type', data)
         self.assertIn('desc', data)
 
+        if 'value' in data:
+            self.assertIn('source', data)
+            self.assertIsInstance(data['value'], list)
+
+            for entry in data['value']:
+                self.assertIsInstance(entry, dict)
+                self.assertIn('section', entry)
+                self.assertIn('value', entry)
+
+    def _wait_for_expected_get_result(self, get_func, expected_result, max_attempts=30,
+                                      sleep_time=1):
+        attempts = 0
+        while attempts < max_attempts:
+            get_result = get_func()
+            if get_result == expected_result:
+                self.assertStatus(200)
+                return get_result
+
+            time.sleep(sleep_time)
+            attempts += 1
index 35b5ca3778f44d0316568b2356265d97c092d9bc..0e724cf1a4acc7a398e8a401d6bb75ab8f5a1b5d 100644 (file)
@@ -4,19 +4,39 @@ from __future__ import absolute_import
 import cherrypy
 
 from .. import mgr
+from ..services.ceph_service import CephService
 from . import ApiController, RESTController, AuthRequired
 
 
 @ApiController('/cluster_conf')
 @AuthRequired()
 class ClusterConfiguration(RESTController):
+
+    def _append_config_option_values(self, options):
+        """
+        Appends values from the config database (if available) to the given options
+        :param options: list of config options
+        :return: list of config options extended by their current values
+        """
+        config_dump = CephService.send_command('mon', 'config dump')
+        for config_dump_entry in config_dump:
+            for i, elem in enumerate(options):
+                if config_dump_entry['name'] == elem['name']:
+                    if 'value' not in elem:
+                        options[i]['value'] = []
+                        options[i]['source'] = 'mon'
+
+                    options[i]['value'].append({'section': config_dump_entry['section'],
+                                                'value': config_dump_entry['value']})
+        return options
+
     def list(self):
         options = mgr.get("config_options")['options']
-        return options
+        return self._append_config_option_values(options)
 
     def get(self, name):
         for option in mgr.get('config_options')['options']:
             if option['name'] == name:
-                return option
+                return self._append_config_option_values([option])[0]
 
         raise cherrypy.HTTPError(404)
index 9299ffac05d968b721912e08249f8e626fac579a..9e7c64082b446a3399907a6c2bf55c4ca899d9fc 100644 (file)
       </cd-table-key-value>
     </tab>
   </tabset>
+  <ng-template #confValTpl let-value="value">
+    <span *ngIf="value">
+      <span *ngFor="let conf of value; last as isLast">
+        {{ conf.section }}: {{ conf.value }}{{ !isLast ? "," : "" }}<br/>
+      </span>
+    </span>
+  </ng-template>
 </cd-table>
index 8acc8046a44308d095668a9f016eb10f708e7efe..ef06274935b522725f584fd6714e5d81e3fdbaf4 100644 (file)
@@ -1,4 +1,4 @@
-import { Component } from '@angular/core';
+import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core';
 
 import { ConfigurationService } from '../../../shared/api/configuration.service';
 import { CdTableColumn } from '../../../shared/models/cd-table-column';
@@ -9,11 +9,10 @@ import { CdTableSelection } from '../../../shared/models/cd-table-selection';
   templateUrl: './configuration.component.html',
   styleUrls: ['./configuration.component.scss']
 })
-export class ConfigurationComponent {
+export class ConfigurationComponent implements OnInit {
   data = [];
   columns: CdTableColumn[];
   selection = new CdTableSelection();
-
   filters = [
     {
       label: 'Level',
@@ -44,19 +43,46 @@ export class ConfigurationComponent {
 
         return row.services.includes(value);
       }
+    },
+    {
+      label: 'Source',
+      prop: 'source',
+      value: 'any',
+      options: ['any', 'mon'],
+      applyFilter: (row, value) => {
+        if (value === 'any') {
+          return true;
+        }
+
+        if (!row.hasOwnProperty('source')) {
+          return false;
+        }
+
+        return row.source.includes(value);
+      }
     }
   ];
 
-  constructor(
-    private configurationService: ConfigurationService,
-  ) {
+  @ViewChild('confValTpl') public confValTpl: TemplateRef<any>;
+
+  constructor(private configurationService: ConfigurationService) {}
+
+  ngOnInit() {
     this.columns = [
       { flexGrow: 2, canAutoResize: true, prop: 'name' },
+      {
+        flexGrow: 2,
+        prop: 'value',
+        name: 'Current value',
+        cellClass: 'wrap',
+        cellTemplate: this.confValTpl
+      },
+      { flexGrow: 1, prop: 'source' },
       { flexGrow: 2, prop: 'desc', name: 'Description', cellClass: 'wrap' },
       { flexGrow: 2, prop: 'long_desc', name: 'Long description', cellClass: 'wrap' },
       { flexGrow: 1, prop: 'type' },
       { flexGrow: 1, prop: 'level' },
-      { flexGrow: 1, prop: 'default', cellClass: 'wrap'},
+      { flexGrow: 1, prop: 'default', cellClass: 'wrap' },
       { flexGrow: 2, prop: 'daemon_default', name: 'Daemon default' },
       { flexGrow: 1, prop: 'tags', name: 'Tags' },
       { flexGrow: 1, prop: 'services', name: 'Services' },