From d7de1fa067098c38538e2cec2537c5023cdff6b4 Mon Sep 17 00:00:00 2001 From: Patrick Nawracay Date: Tue, 15 May 2018 09:47:19 +0200 Subject: [PATCH] mgr/dashboard/backend: Enable get/set of cluster-wide OSD settings Add ability to list, set and unset cluster-wide OSD flags. Flags can be listed and changed through the `/api/osd/flags` API resource. By using a GET request, the list is retrieved. By using a PUT request, the flags are updated (all at once). Flags not contained in the data of the PUT are removed, additional once are added. Note that the PUT requests require a JSON body with the data contained as value of the 'flags' key like so: {"flags": ["flag1", "flag2", ...]} Fixes: http://tracker.ceph.com/issues/24056 Signed-off-by: Patrick Nawracay --- qa/suites/rados/mgr/tasks/dashboard.yaml | 1 + qa/tasks/mgr/dashboard/test_osd.py | 41 +++++++++++++++++++++ src/pybind/mgr/dashboard/controllers/osd.py | 41 ++++++++++++++++++++- 3 files changed, 81 insertions(+), 2 deletions(-) diff --git a/qa/suites/rados/mgr/tasks/dashboard.yaml b/qa/suites/rados/mgr/tasks/dashboard.yaml index 3550ff41badb0..8a134aad22a8b 100644 --- a/qa/suites/rados/mgr/tasks/dashboard.yaml +++ b/qa/suites/rados/mgr/tasks/dashboard.yaml @@ -18,6 +18,7 @@ tasks: - \(MDS_DAMAGE\) - \(MDS_ALL_DOWN\) - \(MDS_UP_LESS_THAN_MAX\) + - pauserd,pausewr flag\(s\) set - rgw: [client.0] - cephfs_test_runner: fail_on_skip: false diff --git a/qa/tasks/mgr/dashboard/test_osd.py b/qa/tasks/mgr/dashboard/test_osd.py index c61a8020c7392..f322544b36f62 100644 --- a/qa/tasks/mgr/dashboard/test_osd.py +++ b/qa/tasks/mgr/dashboard/test_osd.py @@ -2,6 +2,8 @@ from __future__ import absolute_import +import json + from .helper import DashboardTestCase, authenticate, JObj, JAny, JList, JLeaf, JTuple @@ -42,3 +44,42 @@ class OsdTest(DashboardTestCase): self._post('/api/osd/0/scrub?deep=True') self.assertStatus(200) + + +class OsdFlagsTest(DashboardTestCase): + def __init__(self, *args, **kwargs): + super(OsdFlagsTest, self).__init__(*args, **kwargs) + self._initial_flags = sorted( # These flags cannot be unset + ['sortbitwise', 'recovery_deletes', 'purged_snapdirs']) + + @classmethod + def _get_cluster_osd_flags(cls): + return sorted( + json.loads(cls._ceph_cmd(['osd', 'dump', + '--format=json']))['flags_set']) + + @classmethod + def _put_flags(cls, flags): + cls._put('/api/osd/flags', data={'flags': flags}) + return sorted(cls._resp.json()) + + @authenticate + def test_list_osd_flags(self): + flags = self._get('/api/osd/flags') + self.assertStatus(200) + self.assertEqual(len(flags), 3) + self.assertEqual(sorted(flags), self._initial_flags) + + @authenticate + def test_add_osd_flag(self): + flags = self._put_flags([ + 'sortbitwise', 'recovery_deletes', 'purged_snapdirs', 'noout', + 'pause' + ]) + self.assertEqual(flags, sorted([ + 'sortbitwise', 'recovery_deletes', 'purged_snapdirs', 'noout', + 'pause' + ])) + + # Restore flags + self._put_flags(self._initial_flags) diff --git a/src/pybind/mgr/dashboard/controllers/osd.py b/src/pybind/mgr/dashboard/controllers/osd.py index 3259649b8e484..77b275a0ccc42 100644 --- a/src/pybind/mgr/dashboard/controllers/osd.py +++ b/src/pybind/mgr/dashboard/controllers/osd.py @@ -2,8 +2,7 @@ from __future__ import absolute_import from . import ApiController, AuthRequired, RESTController -from .. import mgr - +from .. import mgr, logger from ..services.ceph_service import CephService from ..services.exception import handle_send_command_error from ..tools import str_to_bool @@ -63,3 +62,41 @@ class Osd(RESTController): def scrub(self, svc_id, deep=False): api_scrub = "osd deep-scrub" if str_to_bool(deep) else "osd scrub" CephService.send_command("mon", api_scrub, who=svc_id) + + +@ApiController('/osd/flags') +class OsdFlagsController(RESTController): + @staticmethod + def _osd_flags(): + enabled_flags = mgr.get('osd_map')['flags_set'] + if 'pauserd' in enabled_flags and 'pausewr' in enabled_flags: + # 'pause' is set by calling `ceph osd set pause` and unset by + # calling `set osd unset pause`, but `ceph osd dump | jq '.flags'` + # will contain 'pauserd,pausewr' if pause is set. + # Let's pretend to the API that 'pause' is in fact a proper flag. + enabled_flags = list( + set(enabled_flags) - {'pauserd', 'pausewr'} | {'pause'}) + return sorted(enabled_flags) + + def list(self): + return self._osd_flags() + + def bulk_set(self, flags): + """ + The `recovery_deletes` and `sortbitwise` flags cannot be unset. + `purged_snapshots` cannot even be set. It is therefore required to at + least include those three flags for a successful operation. + """ + assert isinstance(flags, list) + + enabled_flags = set(self._osd_flags()) + data = set(flags) + added = data - enabled_flags + removed = enabled_flags - data + for flag in added: + CephService.send_command('mon', 'osd set', '', key=flag) + for flag in removed: + CephService.send_command('mon', 'osd unset', '', key=flag) + logger.info('Changed OSD flags: added=%s removed=%s', added, removed) + + return sorted(enabled_flags - removed | added) -- 2.39.5