Ejemplo n.º 1
0
 def test_crush_rule_info(self):
     self._get('/ui-api/crush_rule/info')
     self.assertStatus(200)
     self.assertSchemaBody(
         JObj({
             'names': JList(six.string_types),
             'nodes': JList(JObj({}, allow_unknown=True))
         }))
 def test_ecp_info(self):
     self._get('/ui-api/erasure_code_profile/info')
     self.assertSchemaBody(
         JObj({
             'names': JList(six.string_types),
             'failure_domains': JList(six.string_types),
             'plugins': JList(six.string_types),
             'devices': JList(six.string_types),
             'directory': six.string_types,
         }))
Ejemplo n.º 3
0
    def _validate_image(self, img, **kwargs):
        """
        Example of an RBD image json:

        {
            "size": 1073741824,
            "obj_size": 4194304,
            "num_objs": 256,
            "order": 22,
            "block_name_prefix": "rbd_data.10ae2ae8944a",
            "name": "img1",
            "pool_name": "rbd",
            "features": 61,
            "features_name": ["deep-flatten", "exclusive-lock", "fast-diff", "layering",
                              "object-map"]
        }
        """
        schema = JObj(sub_elems={
            'size': JLeaf(int),
            'obj_size': JLeaf(int),
            'num_objs': JLeaf(int),
            'order': JLeaf(int),
            'block_name_prefix': JLeaf(str),
            'name': JLeaf(str),
            'id': JLeaf(str),
            'pool_name': JLeaf(str),
            'namespace': JLeaf(str, none=True),
            'features': JLeaf(int),
            'features_name': JList(JLeaf(str)),
            'stripe_count': JLeaf(int, none=True),
            'stripe_unit': JLeaf(int, none=True),
            'parent': JObj(sub_elems={'pool_name': JLeaf(str),
                                      'pool_namespace': JLeaf(str, none=True),
                                      'image_name': JLeaf(str),
                                      'snap_name': JLeaf(str)}, none=True),
            'data_pool': JLeaf(str, none=True),
            'snapshots': JList(JLeaf(dict)),
            'timestamp': JLeaf(str, none=True),
            'disk_usage': JLeaf(int, none=True),
            'total_disk_usage': JLeaf(int, none=True),
            'configuration': JList(JObj(sub_elems={
                'name': JLeaf(str),
                'source': JLeaf(int),
                'value': JLeaf(str),
            })),
        })
        self.assertSchema(img, schema)

        for k, v in kwargs.items():
            if isinstance(v, list):
                self.assertSetEqual(set(img[k]), set(v))
            else:
                self.assertEqual(img[k], v)
Ejemplo n.º 4
0
 def test_osd_devices(self):
     data = self._get('/api/osd/0/devices')
     self.assertStatus(200)
     self.assertSchema(
         data,
         JList(
             JObj({
                 'daemons':
                 JList(str),
                 'devid':
                 str,
                 'location':
                 JList(JObj({
                     'host': str,
                     'dev': str,
                     'path': str
                 }))
             })))
Ejemplo n.º 5
0
 def _assert_user_data(self, data):
     self.assertSchema(
         data,
         JObj(sub_elems={
             'caps': JList(JObj(sub_elems={}, allow_unknown=True)),
             'display_name': JLeaf(str),
             'email': JLeaf(str),
             'keys': JList(JObj(sub_elems={}, allow_unknown=True)),
             'max_buckets': JLeaf(int),
             'subusers': JList(JLeaf(str)),
             'suspended': JLeaf(int),
             'swift_keys': JList(JObj(sub_elems={}, allow_unknown=True)),
             'tenant': JLeaf(str),
             'user_id': JLeaf(str),
             'uid': JLeaf(str)
         },
              allow_unknown=True))
     self.assertGreaterEqual(len(data['keys']), 1)
Ejemplo n.º 6
0
    def test_pool_list(self):
        data = self._get("/api/pool")
        self.assertStatus(200)

        cluster_pools = self.ceph_cluster.mon_manager.list_pools()
        self.assertEqual(len(cluster_pools), len(data))
        self.assertSchemaBody(JList(self.pool_schema))
        for pool in data:
            self.assertNotIn('pg_status', pool)
            self.assertNotIn('stats', pool)
            self.assertIn(pool['pool_name'], cluster_pools)
Ejemplo n.º 7
0
 def test_list_settings(self):
     settings = self._get('/api/settings')
     self.assertGreater(len(settings), 10)
     self.assertSchema(
         settings,
         JList(
             JObj({
                 'default': JAny(none=False),
                 'name': str,
                 'type': str,
                 'value': JAny(none=False)
             })))
     self.assertStatus(200)
Ejemplo n.º 8
0
 def test_list_enabled_module(self):
     self._ceph_cmd(['mgr', 'module', 'enable', 'iostat'])
     self.wait_until_rest_api_accessible()
     data = self._get('/api/mgr/module')
     self.assertStatus(200)
     self.assertSchema(
         data,
         JList(
             JObj(
                 sub_elems={
                     'name':
                     JLeaf(str),
                     'enabled':
                     JLeaf(bool),
                     'always_on':
                     JLeaf(bool),
                     'options':
                     JObj({},
                          allow_unknown=True,
                          unknown_schema=JObj(
                              {
                                  'name': str,
                                  'type': str,
                                  'level': str,
                                  'flags': int,
                                  'default_value': JAny(none=False),
                                  'min': JAny(none=False),
                                  'max': JAny(none=False),
                                  'enum_allowed': JList(str),
                                  'see_also': JList(str),
                                  'desc': str,
                                  'long_desc': str,
                                  'tags': JList(str)
                              }))
                 })))
     module_info = self.find_object_in_list('name', 'iostat', data)
     self.assertIsNotNone(module_info)
     self.assertTrue(module_info['enabled'])
Ejemplo n.º 9
0
    def test_health_permissions(self):
        data = self._get('/api/health/full')
        self.assertStatus(200)

        schema = JObj({
            'client_perf':
            JObj({}, allow_unknown=True),
            'df':
            JObj({}, allow_unknown=True),
            'health':
            JObj({
                'checks': JList(JObj({}, allow_unknown=True)),
                'mutes': JList(JObj({}, allow_unknown=True)),
                'status': str
            }),
            'pools':
            JList(JLeaf(dict)),
        })
        self.assertSchema(data, schema)

        cluster_pools = self.ceph_cluster.mon_manager.list_pools()
        self.assertEqual(len(cluster_pools), len(data['pools']))
        for pool in data['pools']:
            self.assertIn(pool['pool_name'], cluster_pools)
Ejemplo n.º 10
0
 def test_logs(self):
     data = self._get("/api/logs/all")
     self.assertStatus(200)
     log_entry_schema = JList(JObj({
         'addrs': JObj({
             'addrvec': JList(JObj({
                 'addr': str,
                 'nonce': int,
                 'type': str
             }))
         }),
         'channel': str,
         'message': str,
         'name': str,
         'priority': str,
         'rank': str,
         'seq': int,
         'stamp': str
     }))
     schema = JObj({
         'audit_log': log_entry_schema,
         'clog': log_entry_schema
     })
     self.assertSchema(data, schema)
Ejemplo n.º 11
0
    def test_pool_list_stats(self):
        data = self._get("/api/pool?stats=true")
        self.assertStatus(200)

        cluster_pools = self.ceph_cluster.mon_manager.list_pools()
        self.assertEqual(len(cluster_pools), len(data))
        self.assertSchemaBody(JList(self.pool_schema))
        for pool in data:
            self.assertIn('pool_name', pool)
            self.assertIn('type', pool)
            self.assertIn('application_metadata', pool)
            self.assertIn('flags', pool)
            self.assertIn('pg_status', pool)
            self.assertSchema(pool['stats'], self.pool_list_stats_schema)
            self.assertIn('flags_names', pool)
            self.assertIn(pool['pool_name'], cluster_pools)
Ejemplo n.º 12
0
    def test_snapshots(self):
        fs_id = self.get_fs_id()
        self.mk_dirs('/movies/dune/extended_version')

        self._post("/api/cephfs/{}/mk_snapshot".format(fs_id),
                   params={'path': '/movies/dune', 'name': 'test'})
        self.assertStatus(200)

        data = self.ls_dir('/movies', 1)
        self.assertSchema(data[0], JObj(sub_elems={
            'name': JLeaf(str),
            'path': JLeaf(str),
            'parent': JLeaf(str),
            'snapshots': JList(JObj(sub_elems={
                'name': JLeaf(str),
                'path': JLeaf(str),
                'created': JLeaf(str)
            })),
            'quotas': JObj(sub_elems={
                'max_bytes': JLeaf(int),
                'max_files': JLeaf(int)
            })
        }))
        snapshots = data[0]['snapshots']
        self.assertEqual(len(snapshots), 1)
        snapshot = snapshots[0]
        self.assertEqual(snapshot['name'], "test")
        self.assertEqual(snapshot['path'], "/movies/dune/.snap/test")

        # Should have filtered out "_test_$timestamp"
        data = self.ls_dir('/movies/dune', 1)
        snapshots = data[0]['snapshots']
        self.assertEqual(len(snapshots), 0)

        self._post("/api/cephfs/{}/rm_snapshot".format(fs_id),
                   params={'path': '/movies/dune', 'name': 'test'})
        self.assertStatus(200)

        data = self.ls_dir('/movies', 1)
        self.assertEqual(len(data[0]['snapshots']), 0)

        # Cleanup. Note, the CephFS Python extension (and therefor the Dashboard
        # REST API) does not support recursive deletion of a directory.
        self.rm_dir('/movies/dune/extended_version')
        self.rm_dir('/movies/dune')
        self.rm_dir('/movies')
Ejemplo n.º 13
0
    def test_list(self):
        data = self._get('/api/osd')
        self.assertStatus(200)

        self.assertGreaterEqual(len(data), 1)
        data = data[0]
        self.assert_in_and_not_none(
            data, ['host', 'tree', 'state', 'stats', 'stats_history'])
        self.assert_in_and_not_none(data['host'], ['name'])
        self.assert_in_and_not_none(data['tree'], ['id'])
        self.assert_in_and_not_none(
            data['stats'],
            ['numpg', 'stat_bytes_used', 'stat_bytes', 'op_r', 'op_w'])
        self.assert_in_and_not_none(data['stats_history'],
                                    ['op_out_bytes', 'op_in_bytes'])
        self.assertSchema(data['stats_history']['op_out_bytes'],
                          JList(JTuple([JLeaf(float),
                                        JLeaf(float)])))
Ejemplo n.º 14
0
 def test_pool_info(self):
     self._get("/ui-api/pool/info")
     self.assertSchemaBody(
         JObj({
             'pool_names': JList(six.string_types),
             'compression_algorithms': JList(six.string_types),
             'compression_modes': JList(six.string_types),
             'is_all_bluestore': bool,
             'bluestore_compression_algorithm': six.string_types,
             'osd_count': int,
             'crush_rules_replicated': JList(JObj({}, allow_unknown=True)),
             'crush_rules_erasure': JList(JObj({}, allow_unknown=True)),
             'pg_autoscale_default_mode': six.string_types,
             'pg_autoscale_modes': JList(six.string_types),
             'erasure_code_profiles': JList(JObj({}, allow_unknown=True)),
             'used_rules': JObj({}, allow_unknown=True),
         }))
Ejemplo n.º 15
0
 def test_list(self):
     self._get('/api/crush_rule')
     self.assertStatus(200)
     self.assertSchemaBody(JList(self.rule_schema))
Ejemplo n.º 16
0
class CrushRuleTest(DashboardTestCase):

    AUTH_ROLES = ['pool-manager']

    rule_schema = JObj(sub_elems={
        'max_size': int,
        'min_size': int,
        'rule_id': int,
        'rule_name': six.string_types,
        'ruleset': int,
        'steps': JList(JObj({}, allow_unknown=True))
    },
                       allow_unknown=True)

    def create_and_delete_rule(self, data):
        name = data['name']
        # Creates rule
        self._post('/api/crush_rule', data)
        self.assertStatus(201)
        # Makes sure rule exists
        rule = self._get('/api/crush_rule/{}'.format(name))
        self.assertStatus(200)
        self.assertSchemaBody(self.rule_schema)
        self.assertEqual(rule['rule_name'], name)
        # Deletes rule
        self._delete('/api/crush_rule/{}'.format(name))
        self.assertStatus(204)

    @DashboardTestCase.RunAs('test', 'test', ['rgw-manager'])
    def test_read_access_permissions(self):
        self._get('/api/crush_rule')
        self.assertStatus(403)

    @DashboardTestCase.RunAs('test', 'test', ['read-only'])
    def test_write_access_permissions(self):
        self._get('/api/crush_rule')
        self.assertStatus(200)
        data = {
            'name': 'some_rule',
            'root': 'default',
            'failure_domain': 'osd'
        }
        self._post('/api/crush_rule', data)
        self.assertStatus(403)
        self._delete('/api/crush_rule/default')
        self.assertStatus(403)

    @classmethod
    def tearDownClass(cls):
        super(CrushRuleTest, cls).tearDownClass()
        cls._ceph_cmd(['osd', 'crush', 'rule', 'rm', 'some_rule'])
        cls._ceph_cmd(['osd', 'crush', 'rule', 'rm', 'another_rule'])

    def test_list(self):
        self._get('/api/crush_rule')
        self.assertStatus(200)
        self.assertSchemaBody(JList(self.rule_schema))

    def test_create(self):
        self.create_and_delete_rule({
            'name': 'some_rule',
            'root': 'default',
            'failure_domain': 'osd'
        })

    @DashboardTestCase.RunAs('test', 'test',
                             ['pool-manager', 'cluster-manager'])
    def test_create_with_ssd(self):
        data = self._get('/api/osd/0')
        self.assertStatus(200)
        device_class = data['osd_metadata']['default_device_class']
        self.create_and_delete_rule({
            'name': 'another_rule',
            'root': 'default',
            'failure_domain': 'osd',
            'device_class': device_class
        })

    def test_crush_rule_info(self):
        self._get('/ui-api/crush_rule/info')
        self.assertStatus(200)
        self.assertSchemaBody(
            JObj({
                'names': JList(six.string_types),
                'nodes': JList(JObj({}, allow_unknown=True))
            }))
Ejemplo n.º 17
0
 def test_minimal_health(self):
     data = self._get('/api/health/minimal')
     self.assertStatus(200)
     schema = JObj({
         'client_perf':
         JObj({
             'read_bytes_sec': int,
             'read_op_per_sec': int,
             'recovering_bytes_per_sec': int,
             'write_bytes_sec': int,
             'write_op_per_sec': int
         }),
         'df':
         JObj({
             'stats':
             JObj({
                 'total_avail_bytes': int,
                 'total_bytes': int,
                 'total_used_raw_bytes': int,
             })
         }),
         'fs_map':
         JObj({
             'filesystems': JList(JObj({'mdsmap': self.__mdsmap_schema}), ),
             'standbys': JList(JObj({}, allow_unknown=True)),
         }),
         'health':
         JObj({
             'checks': JList(JObj({}, allow_unknown=True)),
             'mutes': JList(JObj({}, allow_unknown=True)),
             'status': str,
         }),
         'hosts':
         int,
         'iscsi_daemons':
         JObj({
             'up': int,
             'down': int
         }),
         'mgr_map':
         JObj({
             'active_name': str,
             'standbys': JList(JLeaf(dict))
         }),
         'mon_status':
         JObj({
             'monmap': JObj({
                 'mons': JList(JLeaf(dict)),
             }),
             'quorum': JList(int)
         }),
         'osd_map':
         JObj({
             'osds': JList(JObj({
                 'in': int,
                 'up': int,
             })),
         }),
         'pg_info':
         self.__pg_info_schema,
         'pools':
         JList(JLeaf(dict)),
         'rgw':
         int,
         'scrub_status':
         str
     })
     self.assertSchema(data, schema)
Ejemplo n.º 18
0
class HealthTest(DashboardTestCase):
    CEPHFS = True

    __pg_info_schema = JObj({
        'object_stats':
        JObj({
            'num_objects': int,
            'num_object_copies': int,
            'num_objects_degraded': int,
            'num_objects_misplaced': int,
            'num_objects_unfound': int
        }),
        'pgs_per_osd':
        float,
        'statuses':
        JObj({}, allow_unknown=True, unknown_schema=int)
    })

    __mdsmap_schema = JObj({
        'session_autoclose':
        int,
        'balancer':
        str,
        'up':
        JObj({}, allow_unknown=True),
        'last_failure_osd_epoch':
        int,
        'in':
        JList(int),
        'last_failure':
        int,
        'max_file_size':
        int,
        'explicitly_allowed_features':
        int,
        'damaged':
        JList(int),
        'tableserver':
        int,
        'failed':
        JList(int),
        'metadata_pool':
        int,
        'epoch':
        int,
        'stopped':
        JList(int),
        'max_mds':
        int,
        'compat':
        JObj({
            'compat': JObj({}, allow_unknown=True),
            'ro_compat': JObj({}, allow_unknown=True),
            'incompat': JObj({}, allow_unknown=True)
        }),
        'min_compat_client':
        str,
        'data_pools':
        JList(int),
        'info':
        JObj({}, allow_unknown=True),
        'fs_name':
        str,
        'created':
        str,
        'standby_count_wanted':
        int,
        'enabled':
        bool,
        'modified':
        str,
        'session_timeout':
        int,
        'flags':
        int,
        'ever_allowed_features':
        int,
        'root':
        int
    })

    def test_minimal_health(self):
        data = self._get('/api/health/minimal')
        self.assertStatus(200)
        schema = JObj({
            'client_perf':
            JObj({
                'read_bytes_sec': int,
                'read_op_per_sec': int,
                'recovering_bytes_per_sec': int,
                'write_bytes_sec': int,
                'write_op_per_sec': int
            }),
            'df':
            JObj({
                'stats':
                JObj({
                    'total_avail_bytes': int,
                    'total_bytes': int,
                    'total_used_raw_bytes': int,
                })
            }),
            'fs_map':
            JObj({
                'filesystems': JList(JObj({'mdsmap': self.__mdsmap_schema}), ),
                'standbys': JList(JObj({}, allow_unknown=True)),
            }),
            'health':
            JObj({
                'checks': JList(JObj({}, allow_unknown=True)),
                'mutes': JList(JObj({}, allow_unknown=True)),
                'status': str,
            }),
            'hosts':
            int,
            'iscsi_daemons':
            JObj({
                'up': int,
                'down': int
            }),
            'mgr_map':
            JObj({
                'active_name': str,
                'standbys': JList(JLeaf(dict))
            }),
            'mon_status':
            JObj({
                'monmap': JObj({
                    'mons': JList(JLeaf(dict)),
                }),
                'quorum': JList(int)
            }),
            'osd_map':
            JObj({
                'osds': JList(JObj({
                    'in': int,
                    'up': int,
                })),
            }),
            'pg_info':
            self.__pg_info_schema,
            'pools':
            JList(JLeaf(dict)),
            'rgw':
            int,
            'scrub_status':
            str
        })
        self.assertSchema(data, schema)

    def test_full_health(self):
        data = self._get('/api/health/full')
        self.assertStatus(200)
        module_info_schema = JObj({
            'can_run':
            bool,
            'error_string':
            str,
            'name':
            str,
            'module_options':
            JObj({},
                 allow_unknown=True,
                 unknown_schema=JObj({
                     'name': str,
                     'type': str,
                     'level': str,
                     'flags': int,
                     'default_value': str,
                     'min': str,
                     'max': str,
                     'enum_allowed': JList(str),
                     'see_also': JList(str),
                     'desc': str,
                     'long_desc': str,
                     'tags': JList(str),
                 })),
        })
        schema = JObj({
            'client_perf':
            JObj({
                'read_bytes_sec': int,
                'read_op_per_sec': int,
                'recovering_bytes_per_sec': int,
                'write_bytes_sec': int,
                'write_op_per_sec': int
            }),
            'df':
            JObj({
                'pools':
                JList(
                    JObj({
                        'stats':
                        JObj({
                            'stored': int,
                            'stored_data': int,
                            'stored_omap': int,
                            'objects': int,
                            'kb_used': int,
                            'bytes_used': int,
                            'data_bytes_used': int,
                            'omap_bytes_used': int,
                            'percent_used': float,
                            'max_avail': int,
                            'quota_objects': int,
                            'quota_bytes': int,
                            'dirty': int,
                            'rd': int,
                            'rd_bytes': int,
                            'wr': int,
                            'wr_bytes': int,
                            'compress_bytes_used': int,
                            'compress_under_bytes': int,
                            'stored_raw': int
                        }),
                        'name':
                        str,
                        'id':
                        int
                    })),
                'stats':
                JObj({
                    'total_avail_bytes': int,
                    'total_bytes': int,
                    'total_used_bytes': int,
                    'total_used_raw_bytes': int,
                    'total_used_raw_ratio': float,
                    'num_osds': int,
                    'num_per_pool_osds': int,
                    'num_per_pool_omap_osds': int
                })
            }),
            'fs_map':
            JObj({
                'compat':
                JObj({
                    'compat':
                    JObj({}, allow_unknown=True, unknown_schema=str),
                    'incompat':
                    JObj({}, allow_unknown=True, unknown_schema=str),
                    'ro_compat':
                    JObj({}, allow_unknown=True, unknown_schema=str)
                }),
                'default_fscid':
                int,
                'epoch':
                int,
                'feature_flags':
                JObj({}, allow_unknown=True, unknown_schema=bool),
                'filesystems':
                JList(JObj({
                    'id': int,
                    'mdsmap': self.__mdsmap_schema
                }), ),
                'standbys':
                JList(JObj({}, allow_unknown=True)),
            }),
            'health':
            JObj({
                'checks': JList(JObj({}, allow_unknown=True)),
                'mutes': JList(JObj({}, allow_unknown=True)),
                'status': str,
            }),
            'hosts':
            int,
            'iscsi_daemons':
            JObj({
                'up': int,
                'down': int
            }),
            'mgr_map':
            JObj(
                {
                    'active_addr':
                    str,
                    'active_addrs':
                    JObj({
                        'addrvec':
                        JList(JObj({
                            'addr': str,
                            'nonce': int,
                            'type': str
                        }))
                    }),
                    'active_change':
                    str,  # timestamp
                    'active_mgr_features':
                    int,
                    'active_gid':
                    int,
                    'active_name':
                    str,
                    'always_on_modules':
                    JObj({}, allow_unknown=True),
                    'available':
                    bool,
                    'available_modules':
                    JList(module_info_schema),
                    'epoch':
                    int,
                    'modules':
                    JList(str),
                    'services':
                    JObj(
                        {'dashboard': str
                         },  # This module should always be present
                        allow_unknown=True,
                        unknown_schema=str),
                    'standbys':
                    JList(
                        JObj(
                            {
                                'available_modules': JList(module_info_schema),
                                'gid': int,
                                'name': str,
                                'mgr_features': int
                            },
                            allow_unknown=True))
                },
                allow_unknown=True),
            'mon_status':
            JObj({
                'election_epoch':
                int,
                'extra_probe_peers':
                JList(JAny(none=True)),
                'feature_map':
                JObj({},
                     allow_unknown=True,
                     unknown_schema=JList(
                         JObj({
                             'features': str,
                             'num': int,
                             'release': str
                         }))),
                'features':
                JObj({
                    'quorum_con': str,
                    'quorum_mon': JList(str),
                    'required_con': str,
                    'required_mon': JList(str)
                }),
                'monmap':
                JObj(
                    {
                        # TODO: expand on monmap schema
                        'mons': JList(JLeaf(dict)),
                    },
                    allow_unknown=True),
                'name':
                str,
                'outside_quorum':
                JList(int),
                'quorum':
                JList(int),
                'quorum_age':
                int,
                'rank':
                int,
                'state':
                str,
                # TODO: What type should be expected here?
                'sync_provider':
                JList(JAny(none=True))
            }),
            'osd_map':
            JObj(
                {
                    # TODO: define schema for crush map and osd_metadata, among
                    # others
                    'osds':
                    JList(JObj({
                        'in': int,
                        'up': int,
                    }, allow_unknown=True)),
                },
                allow_unknown=True),
            'pg_info':
            self.__pg_info_schema,
            'pools':
            JList(JLeaf(dict)),
            'rgw':
            int,
            'scrub_status':
            str
        })
        self.assertSchema(data, schema)

        cluster_pools = self.ceph_cluster.mon_manager.list_pools()
        self.assertEqual(len(cluster_pools), len(data['pools']))
        for pool in data['pools']:
            self.assertIn(pool['pool_name'], cluster_pools)

    @DashboardTestCase.RunAs('test', 'test', ['pool-manager'])
    def test_health_permissions(self):
        data = self._get('/api/health/full')
        self.assertStatus(200)

        schema = JObj({
            'client_perf':
            JObj({}, allow_unknown=True),
            'df':
            JObj({}, allow_unknown=True),
            'health':
            JObj({
                'checks': JList(JObj({}, allow_unknown=True)),
                'mutes': JList(JObj({}, allow_unknown=True)),
                'status': str
            }),
            'pools':
            JList(JLeaf(dict)),
        })
        self.assertSchema(data, schema)

        cluster_pools = self.ceph_cluster.mon_manager.list_pools()
        self.assertEqual(len(cluster_pools), len(data['pools']))
        for pool in data['pools']:
            self.assertIn(pool['pool_name'], cluster_pools)
Ejemplo n.º 19
0
class PoolTest(DashboardTestCase):
    AUTH_ROLES = ['pool-manager']

    pool_schema = JObj(sub_elems={
        'pool_name': str,
        'type': str,
        'application_metadata': JList(str),
        'flags': int,
        'flags_names': str,
    },
                       allow_unknown=True)

    pool_list_stat_schema = JObj(sub_elems={
        'latest': int,
        'rate': float,
        'rates': JList(JAny(none=False)),
    })

    pool_list_stats_schema = JObj(sub_elems={
        'bytes_used': pool_list_stat_schema,
        'max_avail': pool_list_stat_schema,
        'rd_bytes': pool_list_stat_schema,
        'wr_bytes': pool_list_stat_schema,
        'rd': pool_list_stat_schema,
        'wr': pool_list_stat_schema,
    },
                                  allow_unknown=True)

    pool_rbd_conf_schema = JList(
        JObj(sub_elems={
            'name': str,
            'value': str,
            'source': int
        }))

    @contextmanager
    def __yield_pool(self, name=None, data=None, deletion_name=None):
        """
        Use either just a name or whole description of a pool to create one.
        This also validates the correct creation and deletion after the pool was used.

        :param name: Name of the pool
        :param data: Describes the pool in full length
        :param deletion_name: Only needed if the pool was renamed
        :return:
        """
        data = self._create_pool(name, data)
        yield data
        self._delete_pool(deletion_name or data['pool'])

    def _create_pool(self, name, data):
        data = data or {
            'pool': name,
            'pg_num': '32',
            '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/', data)
        self.assertStatus(201)
        self._validate_pool_properties(data, self._get_pool(data['pool']))
        return data

    def _delete_pool(self, name):
        self._task_delete('/api/pool/' + name)
        self.assertStatus(204)

    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))

    def _get_pool(self, pool_name):
        pool = self._get("/api/pool/" + pool_name)
        self.assertStatus(200)
        self.assertSchemaBody(self.pool_schema)
        return pool

    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 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'
        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 ['pg_num', pgp_prop]:  # Should have the same values
            self.assertEqual(pool[p], int(value),
                             '{}: {} != {}'.format(p, pool[p], value))

    @DashboardTestCase.RunAs('test', 'test', [{
        'pool': ['create', 'update', 'delete']
    }])
    def test_read_access_permissions(self):
        self._get('/api/pool')
        self.assertStatus(403)
        self._get('/api/pool/bla')
        self.assertStatus(403)

    @DashboardTestCase.RunAs('test', 'test', [{
        'pool': ['read', 'update', 'delete']
    }])
    def test_create_access_permissions(self):
        self._task_post('/api/pool/', {})
        self.assertStatus(403)

    @DashboardTestCase.RunAs('test', 'test', [{
        'pool': ['read', 'create', 'update']
    }])
    def test_delete_access_permissions(self):
        self._delete('/api/pool/ddd')
        self.assertStatus(403)

    def test_pool_list(self):
        data = self._get("/api/pool")
        self.assertStatus(200)

        cluster_pools = self.ceph_cluster.mon_manager.list_pools()
        self.assertEqual(len(cluster_pools), len(data))
        self.assertSchemaBody(JList(self.pool_schema))
        for pool in data:
            self.assertNotIn('pg_status', pool)
            self.assertNotIn('stats', pool)
            self.assertIn(pool['pool_name'], cluster_pools)

    def test_pool_list_attrs(self):
        data = self._get("/api/pool?attrs=type,flags")
        self.assertStatus(200)

        cluster_pools = self.ceph_cluster.mon_manager.list_pools()
        self.assertEqual(len(cluster_pools), len(data))
        for pool in data:
            self.assertIn('pool_name', pool)
            self.assertIn('type', pool)
            self.assertIn('flags', pool)
            self.assertNotIn('flags_names', pool)
            self.assertNotIn('pg_status', pool)
            self.assertNotIn('stats', pool)
            self.assertIn(pool['pool_name'], cluster_pools)

    def test_pool_list_stats(self):
        data = self._get("/api/pool?stats=true")
        self.assertStatus(200)

        cluster_pools = self.ceph_cluster.mon_manager.list_pools()
        self.assertEqual(len(cluster_pools), len(data))
        self.assertSchemaBody(JList(self.pool_schema))
        for pool in data:
            self.assertIn('pool_name', pool)
            self.assertIn('type', pool)
            self.assertIn('application_metadata', pool)
            self.assertIn('flags', pool)
            self.assertIn('pg_status', pool)
            self.assertSchema(pool['stats'], self.pool_list_stats_schema)
            self.assertIn('flags_names', pool)
            self.assertIn(pool['pool_name'], cluster_pools)

    def test_pool_get(self):
        cluster_pools = self.ceph_cluster.mon_manager.list_pools()
        pool = self._get(
            "/api/pool/{}?stats=true&attrs=type,flags,stats".format(
                cluster_pools[0]))
        self.assertEqual(pool['pool_name'], cluster_pools[0])
        self.assertIn('type', pool)
        self.assertIn('flags', pool)
        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_with_two_applications(self):
        self.__yield_pool(
            None, {
                'pool': 'dashboard_pool1',
                'pg_num': '32',
                'pool_type': 'replicated',
                'application_metadata': ['rbd', 'sth'],
            })

    def test_pool_create_with_ecp_and_rule(self):
        self._ceph_cmd(['osd', 'crush', 'rule', 'create-erasure', 'ecrule'])
        self._ceph_cmd([
            'osd', 'erasure-code-profile', 'set', 'ecprofile',
            'crush-failure-domain=osd'
        ])
        self.__yield_pool(
            None, {
                'pool': 'dashboard_pool2',
                'pg_num': '32',
                'pool_type': 'erasure',
                'application_metadata': ['rbd'],
                'erasure_code_profile': 'ecprofile',
                'crush_rule': 'ecrule',
            })
        self._ceph_cmd(['osd', 'erasure-code-profile', 'rm', 'ecprofile'])

    def test_pool_create_with_compression(self):
        pool = {
            'pool': 'dashboard_pool3',
            'pg_num': '32',
            'pool_type': 'replicated',
            'compression_algorithm': 'zstd',
            'compression_mode': 'aggressive',
            'compression_max_blob_size': '10000000',
            'compression_required_ratio': '0.8',
            'configuration': {
                'rbd_qos_bps_limit': 2048,
                'rbd_qos_iops_limit': None,
            },
        }
        with self.__yield_pool(None, pool):
            expected_configuration = [{
                'name': 'rbd_qos_bps_limit',
                'source': 1,
                'value': '2048',
            }, {
                'name': 'rbd_qos_iops_limit',
                'source': 0,
                'value': '0',
            }]
            new_pool = self._get_pool(pool['pool'])
            for conf in expected_configuration:
                self.assertIn(conf, new_pool['configuration'])

    def test_pool_create_with_quotas(self):
        pools = [{
            'pool_data': {
                'pool': 'dashboard_pool_quota1',
                'pg_num': '32',
                'pool_type': 'replicated',
            },
            'pool_quotas_to_check': {
                'quota_max_objects': 0,
                'quota_max_bytes': 0,
            }
        }, {
            'pool_data': {
                'pool': 'dashboard_pool_quota2',
                'pg_num': '32',
                'pool_type': 'replicated',
                'quota_max_objects': 1024,
                'quota_max_bytes': 1000,
            },
            'pool_quotas_to_check': {
                'quota_max_objects': 1024,
                'quota_max_bytes': 1000,
            }
        }]

        for pool in pools:
            pool_name = pool['pool_data']['pool']
            with self.__yield_pool(pool_name, pool['pool_data']):
                self._validate_pool_properties(pool['pool_quotas_to_check'],
                                               self._get_pool(pool_name))

    def test_pool_update_name(self):
        name = 'pool_update'
        updated_name = 'pool_updated_name'
        with self.__yield_pool(name, None, updated_name):
            props = {'pool': updated_name}
            self._task_put('/api/pool/{}'.format(name), props)
            time.sleep(5)
            self.assertStatus(200)
            self._validate_pool_properties(props, self._get_pool(updated_name))

    def test_pool_update_metadata(self):
        pool_name = 'pool_update_metadata'
        with self.__yield_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.__yield_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.__yield_pool(pool_name):
            properties = {
                'compression_algorithm': 'zstd',
                'compression_mode': 'aggressive',
                'compression_max_blob_size': '10000000',
                'compression_required_ratio': '0.8',
            }
            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.__yield_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_update_quotas(self):
        pool_name = 'pool_update_quotas'
        with self.__yield_pool(pool_name):
            properties = {
                'quota_max_objects': 1024,
                'quota_max_bytes': 1000,
            }
            self._task_put('/api/pool/' + pool_name, properties)
            time.sleep(5)
            self._validate_pool_properties(properties,
                                           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'
        }
        self._task_post('/api/pool/', data)
        self.assertStatus(400)
        self.assertJsonBody({
            'component':
            'pool',
            'code':
            "2",
            'detail':
            "[errno -2] specified rule dnf doesn't exist"
        })

    def test_pool_info(self):
        self._get("/ui-api/pool/info")
        self.assertSchemaBody(
            JObj({
                'pool_names': JList(six.string_types),
                'compression_algorithms': JList(six.string_types),
                'compression_modes': JList(six.string_types),
                'is_all_bluestore': bool,
                'bluestore_compression_algorithm': six.string_types,
                'osd_count': int,
                'crush_rules_replicated': JList(JObj({}, allow_unknown=True)),
                'crush_rules_erasure': JList(JObj({}, allow_unknown=True)),
                'pg_autoscale_default_mode': six.string_types,
                'pg_autoscale_modes': JList(six.string_types),
                'erasure_code_profiles': JList(JObj({}, allow_unknown=True)),
                'used_rules': JObj({}, allow_unknown=True),
            }))