]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/dashboard: Add QA tests for RBD configuration 35215/head
authorPatrick Nawracay <pnawracay@suse.com>
Tue, 19 Feb 2019 10:14:33 +0000 (11:14 +0100)
committerShyukri Shyukriev <shshyukriev@suse.com>
Mon, 25 May 2020 16:38:00 +0000 (19:38 +0300)
Fixes: http://tracker.ceph.com/issues/37572
Signed-off-by: Patrick Nawracay <pnawracay@suse.com>
(cherry picked from commit d410bda838173ea80f6f977a8e9fa095f5ddaef4)

Conflicts:
qa/tasks/mgr/dashboard/test_pool.py - pg_num for dashboard_poolX differs
btw master and nautilus: 10 vs 32

qa/tasks/mgr/dashboard/test_pool.py

index 40a1e20e1265525fa5860ab1e609a86691984dfe..ad86979565dcd2df69f409186e52193ce4059404 100644 (file)
@@ -2,9 +2,9 @@
 from __future__ import absolute_import
 
 import logging
-
 import six
 import time
+from contextlib import contextmanager
 
 from .helper import DashboardTestCase, JAny, JList, JObj
 
@@ -37,30 +37,68 @@ class PoolTest(DashboardTestCase):
         'wr': pool_list_stat_schema,
     }, allow_unknown=True)
 
-    def _pool_create(self, data):
-        try:
-            self._task_post('/api/pool/', data)
-            self.assertStatus(201)
-
-            self._check_pool_properties(data)
-
-            self._task_delete("/api/pool/" + data['pool'])
-            self.assertStatus(204)
-        except Exception:
-            log.exception("test_pool_create: data=%s", data)
-            raise
-
-    def _check_pool_properties(self, data, pool_name=None):
-        if not pool_name:
-            pool_name = data['pool']
-        pool = self._get_pool(pool_name)
-        try:
-            for k, v in data.items():
-                self._check_pool_property(k, v, pool)
+    pool_rbd_conf_schema = JList(JObj(sub_elems={
+        'name': str,
+        'value': str,
+        'source': int
+    }))
+
+    @contextmanager
+    def __create_pool(self, name, data=None):
+        pool_data = data or {
+            'pool': name,
+            'pg_num': '4',
+            'pool_type': 'replicated',
+            'compression_algorithm': 'snappy',
+            'compression_mode': 'passive',
+            'compression_max_blob_size': '131072',
+            'compression_required_ratio': '0.875',
+            'application_metadata': ['rbd'],
+            'configuration': {
+                'rbd_qos_bps_limit': 1024000,
+                'rbd_qos_iops_limit': 5000,
+            }
+        }
+        self._task_post('/api/pool/', pool_data)
+        self.assertStatus(201)
+        time.sleep(5)
+        self._validate_pool_properties(pool_data, self._get_pool(name))
+        yield pool_data
+        self._task_delete('/api/pool/' + name)
+        self.assertStatus(204)
 
-        except Exception:
-            log.exception("test_pool_create: pool=%s", pool)
-            raise
+    def _validate_pool_properties(self, data, pool):
+        for prop, value in data.items():
+            if prop == 'pool_type':
+                self.assertEqual(pool['type'], value)
+            elif prop == 'size':
+                self.assertEqual(pool[prop], int(value),
+                                 '{}: {} != {}'.format(prop, pool[prop], value))
+            elif prop == 'pg_num':
+                self._check_pg_num(value, pool)
+            elif prop == 'application_metadata':
+                self.assertIsInstance(pool[prop], list)
+                self.assertEqual(value, pool[prop])
+            elif prop == 'pool':
+                self.assertEqual(pool['pool_name'], value)
+            elif prop.startswith('compression'):
+                if value is not None:
+                    if prop.endswith('size'):
+                        value = int(value)
+                    elif prop.endswith('ratio'):
+                        value = float(value)
+                    self.assertEqual(pool['options'][prop], value)
+                else:
+                    self.assertEqual(pool['options'], {})
+            elif prop == 'configuration':
+                # configuration cannot really be checked here for two reasons:
+                #   1.  The default value cannot be given to this method, which becomes relevant
+                #       when resetting a value, because it's not always zero.
+                #   2.  The expected `source` cannot be given to this method, and it cannot
+                #       relibably be determined (see 1)
+                pass
+            else:
+                self.assertEqual(pool[prop], value, '{}: {} != {}'.format(prop, pool[prop], value))
 
         health = self._get('/api/health/minimal')['health']
         self.assertEqual(health['status'], 'HEALTH_OK', msg='health={}'.format(health))
@@ -71,49 +109,27 @@ class PoolTest(DashboardTestCase):
         self.assertSchemaBody(self.pool_schema)
         return pool
 
-    def _check_pool_property(self, prop, value, pool):
-        if prop == 'pool_type':
-            self.assertEqual(pool['type'], value)
-        elif prop == 'size':
-            self.assertEqual(pool[prop], int(value), '{}: {} != {}'.format(prop, pool[prop], value))
-        elif prop == 'pg_num':
-            self._check_pg_num(value, pool)
-        elif prop == 'application_metadata':
-            self.assertIsInstance(pool[prop], list)
-            self.assertEqual(pool[prop], value)
-        elif prop == 'pool':
-            self.assertEqual(pool['pool_name'], value)
-        elif prop.startswith('compression'):
-            if value is not None:
-                if prop.endswith('size'):
-                    value = int(value)
-                elif prop.endswith('ratio'):
-                    value = float(value)
-            self.assertEqual(pool['options'].get(prop), value)
-        else:
-            self.assertEqual(pool[prop], value, '{}: {} != {}'.format(prop, pool[prop], value))
-
     def _check_pg_num(self, value, pool):
-        # If both properties have not the same value, the cluster goes into a warning state,
-        # which will only happen during a pg update on a existing pool.
-        # The test that does that is currently commented out because
-        # our QA systems can't deal with the change.
-        # Feel free to test it locally.
-        prop = 'pg_num'
+        """
+        If both properties have not the same value, the cluster goes into a warning state, which
+        will only happen during a pg update on an existing pool. The test that does that is
+        currently commented out because our QA systems can't deal with the change. Feel free to test
+        it locally.
+        """
         pgp_prop = 'pg_placement_num'
-        health = lambda: self._get('/api/health/minimal')['health']['status'] == 'HEALTH_OK'
-        t = 0;
-        while (int(value) != pool[pgp_prop] or not health()) and t < 180:
+        t = 0
+        while (int(value) != pool[pgp_prop] or self._get('/api/health/minimal')['health']['status']
+               != 'HEALTH_OK') and t < 180:
             time.sleep(2)
             t += 2
             pool = self._get_pool(pool['pool_name'])
-        for p in [prop, pgp_prop]:  # Should have the same values
+        for p in ['pg_num', pgp_prop]:  # Should have the same values
             self.assertEqual(pool[p], int(value), '{}: {} != {}'.format(p, pool[p], value))
 
     @classmethod
     def tearDownClass(cls):
         super(PoolTest, cls).tearDownClass()
-        for name in ['dashboard_pool1', 'dashboard_pool2', 'dashboard_pool3', 'dashboard_pool_update1']:
+        for name in ['dashboard_pool1', 'dashboard_pool2', 'dashboard_pool3']:
             cls._ceph_cmd(['osd', 'pool', 'delete', name, name, '--yes-i-really-really-mean-it'])
         cls._ceph_cmd(['osd', 'erasure-code-profile', 'rm', 'ecprofile'])
 
@@ -188,23 +204,40 @@ class PoolTest(DashboardTestCase):
         self.assertNotIn('pg_status', pool)
         self.assertSchema(pool['stats'], self.pool_list_stats_schema)
         self.assertNotIn('flags_names', pool)
+        self.assertSchema(pool['configuration'], self.pool_rbd_conf_schema)
 
     def test_pool_create(self):
         self._ceph_cmd(['osd', 'crush', 'rule', 'create-erasure', 'ecrule'])
         self._ceph_cmd(
             ['osd', 'erasure-code-profile', 'set', 'ecprofile', 'crush-failure-domain=osd'])
-        pools = [{
+
+        pool = {
             'pool': 'dashboard_pool1',
             'pg_num': '32',
             'pool_type': 'replicated',
             'application_metadata': ['rbd', 'sth'],
-        }, {
+        }
+        self._task_post('/api/pool/', pool)
+        self.assertStatus(201)
+        self._validate_pool_properties(pool, self._get_pool(pool['pool']))
+        self._task_delete("/api/pool/" + pool['pool'])
+        self.assertStatus(204)
+
+        pool = {
             'pool': 'dashboard_pool2',
             'pg_num': '32',
             'pool_type': 'erasure',
+            'application_metadata': ['rbd'],
             'erasure_code_profile': 'ecprofile',
             'crush_rule': 'ecrule',
-        }, {
+        }
+        self._task_post('/api/pool/', pool)
+        self.assertStatus(201)
+        self._validate_pool_properties(pool, self._get_pool(pool['pool']))
+        self._task_delete("/api/pool/" + pool['pool'])
+        self.assertStatus(204)
+
+        pool = {
             'pool': 'dashboard_pool3',
             'pg_num': '32',
             'pool_type': 'replicated',
@@ -212,64 +245,99 @@ class PoolTest(DashboardTestCase):
             'compression_mode': 'aggressive',
             'compression_max_blob_size': '10000000',
             'compression_required_ratio': '0.8',
+            'configuration': {
+                'rbd_qos_bps_limit': 2048,
+                'rbd_qos_iops_limit': None,
+            },
+        }
+        expected_configuration = [{
+            'name': 'rbd_qos_bps_limit',
+            'source': 1,
+            'value': '2048',
+        }, {
+            'name': 'rbd_qos_iops_limit',
+            'source': 0,
+            'value': '0',
         }]
-        for data in pools:
-            self._pool_create(data)
+        self._task_post('/api/pool/', pool)
+        self.assertStatus(201)
+        new_pool = self._get_pool(pool['pool'])
+        self._validate_pool_properties(pool, new_pool)
+        for conf in expected_configuration:
+            self.assertIn(conf, new_pool['configuration'])
 
-    def test_update(self):
-        pool = {
-            'pool': 'dashboard_pool_update1',
-            'pg_num': '32',
-            'pool_type': 'replicated',
-            'compression_mode': 'passive',
-            'compression_algorithm': 'snappy',
-            'compression_max_blob_size': '131072',
-            'compression_required_ratio': '0.875',
-        }
-        updates = [
-            {
-                'application_metadata': ['rbd', 'sth'],
-            },
-            # The following test case is currently commented out because
-            # our QA systems can't deal with the change and will fail because
-            # they can't recover from the resulting warning state.
-            # Feel free to test it locally.
-            # {
-            #     'pg_num': '2', # Decrease PGs
-            # },
-            # {
-            #     'pg_num': '8', # Increase PGs
-            # },
-            {
-                'application_metadata': ['rgw'],
-            },
-            {
+        self._task_delete("/api/pool/" + pool['pool'])
+        self.assertStatus(204)
+
+    def test_pool_update_metadata(self):
+        pool_name = 'pool_update_metadata'
+        with self.__create_pool(pool_name):
+            props = {'application_metadata': ['rbd', 'sth']}
+            self._task_put('/api/pool/{}'.format(pool_name), props)
+            time.sleep(5)
+            self._validate_pool_properties(props, self._get_pool(pool_name))
+
+            properties = {'application_metadata': ['rgw']}
+            self._task_put('/api/pool/' + pool_name, properties)
+            time.sleep(5)
+            self._validate_pool_properties(properties, self._get_pool(pool_name))
+
+            properties = {'application_metadata': ['rbd', 'sth']}
+            self._task_put('/api/pool/' + pool_name, properties)
+            time.sleep(5)
+            self._validate_pool_properties(properties, self._get_pool(pool_name))
+
+            properties = {'application_metadata': ['rgw']}
+            self._task_put('/api/pool/' + pool_name, properties)
+            time.sleep(5)
+            self._validate_pool_properties(properties, self._get_pool(pool_name))
+
+    def test_pool_update_configuration(self):
+        pool_name = 'pool_update_configuration'
+        with self.__create_pool(pool_name):
+            configuration = {
+                'rbd_qos_bps_limit': 1024,
+                'rbd_qos_iops_limit': None,
+            }
+            expected_configuration = [{
+                'name': 'rbd_qos_bps_limit',
+                'source': 1,
+                'value': '1024',
+            }, {
+                'name': 'rbd_qos_iops_limit',
+                'source': 0,
+                'value': '0',
+            }]
+            self._task_put('/api/pool/' + pool_name, {'configuration': configuration})
+            time.sleep(5)
+            pool_config = self._get_pool(pool_name)['configuration']
+            for conf in expected_configuration:
+                self.assertIn(conf, pool_config)
+
+    def test_pool_update_compression(self):
+        pool_name = 'pool_update_compression'
+        with self.__create_pool(pool_name):
+            properties = {
                 'compression_algorithm': 'zstd',
                 'compression_mode': 'aggressive',
                 'compression_max_blob_size': '10000000',
                 'compression_required_ratio': '0.8',
-            },
-            {
-                'compression_mode': 'unset'
             }
-        ]
-        self._task_post('/api/pool/', pool)
-        self.assertStatus(201)
-        self._check_pool_properties(pool)
-
-        for update in updates:
-            self._task_put('/api/pool/' + pool['pool'], update)
-            if update.get('compression_mode') == 'unset':
-                update = {
-                    'compression_mode': None,
-                    'compression_algorithm': None,
-                    'compression_mode': None,
-                    'compression_max_blob_size': None,
-                    'compression_required_ratio': None,
-                }
-            self._check_pool_properties(update, pool_name=pool['pool'])
-        self._task_delete("/api/pool/" + pool['pool'])
-        self.assertStatus(204)
+            self._task_put('/api/pool/' + pool_name, properties)
+            time.sleep(5)
+            self._validate_pool_properties(properties, self._get_pool(pool_name))
+
+    def test_pool_update_unset_compression(self):
+        pool_name = 'pool_update_unset_compression'
+        with self.__create_pool(pool_name):
+            self._task_put('/api/pool/' + pool_name, {'compression_mode': 'unset'})
+            time.sleep(5)
+            self._validate_pool_properties({
+                'compression_algorithm': None,
+                'compression_mode': None,
+                'compression_max_blob_size': None,
+                'compression_required_ratio': None,
+            }, self._get_pool(pool_name))
 
     def test_pool_create_fail(self):
         data = {'pool_type': u'replicated', 'rule_name': u'dnf', 'pg_num': u'8', 'pool': u'sadfs'}