]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/dashboard: Handle always-on Ceph Manager modules correctly
authorVolker Theile <vtheile@suse.com>
Thu, 5 Sep 2019 14:06:57 +0000 (16:06 +0200)
committerAlfonso Martínez <almartin@redhat.com>
Thu, 21 Nov 2019 10:04:49 +0000 (11:04 +0100)
Currently always-on modules are not marked as enabled in the WebUI and can be disabled. This PR will fix that.
Note, this PR will NOT implement code that will prevent a developer from trying to disable an always-on module through the REST API. The Mgr Python extension will throw an adequate exception.

This PR will also do:
* Remove old code fragments from a previous Mgr Module management UI that is obsolete now.
* Cleanup code in BaseMgrModule code.

Fixes: https://tracker.ceph.com/issues/41648
Signed-off-by: Volker Theile <vtheile@suse.com>
(cherry picked from commit 8ac655963755820c9bab3953a4195fbc916f7072)

src/mgr/BaseMgrModule.cc
src/pybind/mgr/dashboard/controllers/mgr_modules.py
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/mgr-modules/mgr-module-list/mgr-module-list.component.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/mgr-modules/mgr-module-list/mgr-module-list.component.ts
src/pybind/mgr/dashboard/frontend/src/app/core/mgr-modules/telemetry/telemetry.component.html [deleted file]
src/pybind/mgr/mgr_module.py

index 7415db0ba4c47e66c9f437b336cd528baf5a9bdb..ae6de0315c60aa55d07d93bbf9ad6b2074853eee 100644 (file)
@@ -562,7 +562,13 @@ ceph_get_version(BaseMgrModule *self, PyObject *args)
 }
 
 static PyObject *
-ceph_get_context(BaseMgrModule *self, PyObject *args)
+ceph_get_release_name(BaseMgrModule *self, PyObject *args)
+{
+  return PyString_FromString(ceph_release_to_str());
+}
+
+static PyObject *
+ceph_get_context(BaseMgrModule *self)
 {
   return self->py_modules->get_context();
 }
@@ -1056,9 +1062,12 @@ PyMethodDef BaseMgrModule_methods[] = {
   {"_ceph_cluster_log", (PyCFunction)ceph_cluster_log, METH_VARARGS,
    "Emit a cluster log message"},
 
-  {"_ceph_get_version", (PyCFunction)ceph_get_version, METH_VARARGS,
+  {"_ceph_get_version", (PyCFunction)ceph_get_version, METH_NOARGS,
    "Get the ceph version of this process"},
 
+  {"_ceph_get_release_name", (PyCFunction)ceph_get_release_name, METH_NOARGS,
+   "Get the ceph release name of this process"},
+
   {"_ceph_get_context", (PyCFunction)ceph_get_context, METH_NOARGS,
     "Get a CephContext* in a python capsule"},
 
index 358ae8be56abe1045441c16f97e4626843ab11ad..6e3cb5a3ee18013853ef8d3e4916dd6c769aaab2 100644 (file)
@@ -21,18 +21,19 @@ class MgrModules(RESTController):
         """
         result = []
         mgr_map = mgr.get('mgr_map')
+        always_on_modules = mgr_map['always_on_modules'][mgr.release_name]
         for module_config in mgr_map['available_modules']:
-            if module_config['name'] not in self.ignore_modules:
+            module_name = module_config['name']
+            if module_name not in self.ignore_modules:
+                always_on = module_name in always_on_modules
+                enabled = module_name in mgr_map['modules'] or always_on
                 result.append({
-                    'name': module_config['name'],
-                    'enabled': False,
+                    'name': module_name,
+                    'enabled': enabled,
+                    'always_on': always_on,
                     'options': self._convert_module_options(
                         module_config['module_options'])
                 })
-        for name in mgr_map['modules']:
-            if name not in self.ignore_modules:
-                obj = find_object_in_list('name', name, result)
-                obj['enabled'] = True
         return result
 
     def get(self, module_name):
index 252bd67e2085a40563d4c2335356631fc2eb9a0e..88c801344076f98f641964e20fc1d0d6a79b4624 100644 (file)
@@ -118,7 +118,8 @@ describe('MgrModuleListComponent', () => {
       spyOn(mgrModuleService, 'list').and.returnValues(observableThrowError('z'), observableOf([]));
       component.selection.selected.push({
         name: 'foo',
-        enabled: false
+        enabled: false,
+        always_on: false
       });
       component.selection.update();
       component.updateModuleState();
@@ -137,7 +138,8 @@ describe('MgrModuleListComponent', () => {
       spyOn(mgrModuleService, 'list').and.returnValue(observableOf([]));
       component.selection.selected.push({
         name: 'bar',
-        enabled: true
+        enabled: true,
+        always_on: false
       });
       component.selection.update();
       component.updateModuleState();
@@ -149,5 +151,26 @@ describe('MgrModuleListComponent', () => {
       expect(component.blockUI.stop).toHaveBeenCalled();
       expect(component.table.refreshBtn).toHaveBeenCalled();
     }));
+
+    it('should not disable module (1)', () => {
+      component.selection.selected = [
+        {
+          name: 'dashboard'
+        }
+      ];
+      component.selection.update();
+      expect(component.isTableActionDisabled('enabled')).toBeTruthy();
+    });
+
+    it('should not disable module (2)', () => {
+      component.selection.selected = [
+        {
+          name: 'bar',
+          always_on: true
+        }
+      ];
+      component.selection.update();
+      expect(component.isTableActionDisabled('enabled')).toBeTruthy();
+    });
   });
 });
index 50a13fd39ff27fa39aa22378e26c39b3cdf2815c..294ba1215d31a5de0db5934c2d0bb9b50ab8d4f6 100644 (file)
@@ -81,6 +81,7 @@ export class MgrModuleListComponent {
         permission: 'update',
         click: () => this.updateModuleState(),
         disable: () => this.isTableActionDisabled('disabled'),
+        disableDesc: () => this.getTableActionDisabledDesc(),
         icon: 'fa-stop'
       }
     ];
@@ -111,17 +112,31 @@ export class MgrModuleListComponent {
     if (!this.selection.hasSelection) {
       return true;
     }
+    const selected = this.selection.first();
     // Make sure the user can't modify the run state of the 'Dashboard' module.
     // This check is only done in the UI because the REST API should still be
     // able to do so.
-    if (this.selection.first().name === 'dashboard') {
+    if (selected.name === 'dashboard') {
+      return true;
+    }
+    // Always-on modules can't be disabled.
+    if (selected.always_on) {
       return true;
     }
     switch (state) {
       case 'enabled':
-        return this.selection.first().enabled;
+        return selected.enabled;
       case 'disabled':
-        return !this.selection.first().enabled;
+        return !selected.enabled;
+    }
+  }
+
+  getTableActionDisabledDesc(): string | undefined {
+    if (this.selection.hasSelection) {
+      const selected = this.selection.first();
+      if (selected.always_on) {
+        return this.i18n('This Manager module is always on.');
+      }
     }
   }
 
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/core/mgr-modules/telemetry/telemetry.component.html b/src/pybind/mgr/dashboard/frontend/src/app/core/mgr-modules/telemetry/telemetry.component.html
deleted file mode 100644 (file)
index e69de29..0000000
index 9d4bcb41cc77ff9cdeae8c3c811f6062d880f921..c5fc979f93499f4f108e93c79998b622c45ec565 100644 (file)
@@ -584,6 +584,15 @@ class MgrModule(ceph_module.BaseMgrModule):
     def version(self):
         return self._version
 
+    @property
+    def release_name(self):
+        """
+        Get the release name of the Ceph version, e.g. 'nautilus' or 'octopus'.
+        :return: Returns the release name of the Ceph version in lower case.
+        :rtype: str
+        """
+        return self._ceph_get_release_name()
+
     def get_context(self):
         """
         :return: a Python capsule containing a C++ CephContext pointer