def test_crud_w_locking(self): # Create self._post('/api/rgw/bucket', params={ 'bucket': 'teuth-test-bucket', 'uid': 'teuth-test-user', 'zonegroup': 'default', 'placement_target': 'default-placement', 'lock_enabled': 'true', 'lock_mode': 'GOVERNANCE', 'lock_retention_period_days': '0', 'lock_retention_period_years': '1' }) self.assertStatus(201) # Read data = self._get('/api/rgw/bucket/teuth-test-bucket') self.assertStatus(200) self.assertSchema( data, JObj(sub_elems={ 'lock_enabled': JLeaf(bool), 'lock_mode': JLeaf(str), 'lock_retention_period_days': JLeaf(int), 'lock_retention_period_years': JLeaf(int) }, allow_unknown=True)) self.assertTrue(data['lock_enabled']) self.assertEqual(data['lock_mode'], 'GOVERNANCE') self.assertEqual(data['lock_retention_period_days'], 0) self.assertEqual(data['lock_retention_period_years'], 1) # Update self._put('/api/rgw/bucket/teuth-test-bucket', params={ 'bucket_id': data['id'], 'uid': 'teuth-test-user', 'lock_mode': 'COMPLIANCE', 'lock_retention_period_days': '15', 'lock_retention_period_years': '0' }) self.assertStatus(200) data = self._get('/api/rgw/bucket/teuth-test-bucket') self.assertTrue(data['lock_enabled']) self.assertEqual(data['lock_mode'], 'COMPLIANCE') self.assertEqual(data['lock_retention_period_days'], 15) self.assertEqual(data['lock_retention_period_years'], 0) self.assertStatus(200) # Delete self._delete('/api/rgw/bucket/teuth-test-bucket') self.assertStatus(204)
def test_check_token(self): self.login("admin", "admin") self._post("/api/auth/check", {"token": self.jsonBody()["token"]}) self.assertStatus(200) data = self.jsonBody() self.assertSchema( data, JObj(sub_elems={ "username": JLeaf(str), "permissions": JObj(sub_elems={}, allow_unknown=True), "sso": JLeaf(bool), "pwdUpdateRequired": JLeaf(bool) }, allow_unknown=False)) self.logout()
def test_login_valid(self): self._post("/api/auth", {'username': '******', 'password': '******'}) self.assertStatus(201) data = self.jsonBody() self.assertSchema( data, JObj(sub_elems={ 'token': JLeaf(str), 'username': JLeaf(str), 'permissions': JObj(sub_elems={}, allow_unknown=True), 'sso': JLeaf(bool), 'pwdExpirationDate': JLeaf(int, none=True), 'pwdUpdateRequired': JLeaf(bool) }, allow_unknown=False)) self._validate_jwt_token(data['token'], "admin", data['permissions'])
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)])))
def test_check_wo_token(self): self.login("admin", "admin") self._post("/api/auth/check", {"token": ""}) self.assertStatus(200) data = self.jsonBody() self.assertSchema( data, JObj(sub_elems={"login_url": JLeaf(str)}, allow_unknown=False)) self.logout()
def _verify_tenant_bucket(bucket, tenant, uid): full_bucket_name = '{}/{}'.format(tenant, bucket) _data = self._get('/api/rgw/bucket/{}'.format( urllib.quote_plus(full_bucket_name))) self.assertStatus(200) self.assertSchema( _data, JObj(sub_elems={ 'owner': JLeaf(str), 'bucket': JLeaf(str), 'tenant': JLeaf(str), 'bid': JLeaf(str) }, allow_unknown=True)) self.assertEqual(_data['owner'], '{}${}'.format(tenant, uid)) self.assertEqual(_data['bucket'], bucket) self.assertEqual(_data['tenant'], tenant) self.assertEqual(_data['bid'], full_bucket_name) return _data
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'])
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')
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)
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)
def test_all(self): # Create a new bucket. self._post('/api/rgw/bucket', params={ 'bucket': 'teuth-test-bucket', 'uid': 'admin', 'zonegroup': 'default', 'placement_target': 'default-placement' }) self.assertStatus(201) data = self.jsonBody() self.assertSchema( data, JObj(sub_elems={ 'bucket_info': JObj(sub_elems={ 'bucket': JObj(allow_unknown=True, sub_elems={ 'name': JLeaf(str), 'bucket_id': JLeaf(str), 'tenant': JLeaf(str) }), 'quota': JObj(sub_elems={}, allow_unknown=True), 'creation_time': JLeaf(str) }, allow_unknown=True) }, allow_unknown=True)) data = data['bucket_info']['bucket'] self.assertEqual(data['name'], 'teuth-test-bucket') self.assertEqual(data['tenant'], '') # List all buckets. data = self._get('/api/rgw/bucket') self.assertStatus(200) self.assertEqual(len(data), 1) self.assertIn('teuth-test-bucket', data) # Get the bucket. data = self._get('/api/rgw/bucket/teuth-test-bucket') self.assertStatus(200) self.assertSchema( data, JObj(sub_elems={ 'id': JLeaf(str), 'bid': JLeaf(str), 'tenant': JLeaf(str), 'bucket': JLeaf(str), 'bucket_quota': JObj(sub_elems={}, allow_unknown=True), 'owner': JLeaf(str) }, allow_unknown=True)) self.assertEqual(data['bucket'], 'teuth-test-bucket') self.assertEqual(data['owner'], 'admin') self.assertEqual(data['placement_rule'], 'default-placement') self.assertEqual(data['versioning'], 'Suspended') # Update bucket: change owner, enable versioning. self._put('/api/rgw/bucket/teuth-test-bucket', params={ 'bucket_id': data['id'], 'uid': 'teuth-test-user', 'versioning_state': 'Enabled' }) self.assertStatus(200) data = self._get('/api/rgw/bucket/teuth-test-bucket') self.assertStatus(200) self.assertSchema( data, JObj(sub_elems={ 'owner': JLeaf(str), 'bid': JLeaf(str), 'tenant': JLeaf(str) }, allow_unknown=True)) self.assertEqual(data['owner'], 'teuth-test-user') self.assertEqual(data['versioning'], 'Enabled') # Update bucket: enable MFA Delete. self._put('/api/rgw/bucket/teuth-test-bucket', params={ 'bucket_id': data['id'], 'uid': 'teuth-test-user', 'versioning_state': 'Enabled', 'mfa_delete': 'Enabled', 'mfa_token_serial': self._mfa_token_serial, 'mfa_token_pin': self._get_mfa_token_pin() }) self.assertStatus(200) data = self._get('/api/rgw/bucket/teuth-test-bucket') self.assertStatus(200) self.assertEqual(data['versioning'], 'Enabled') self.assertEqual(data['mfa_delete'], 'Enabled') # Update bucket: disable versioning & MFA Delete. time.sleep(self._mfa_token_time_step + 2) # Required to get new TOTP pin. self._put('/api/rgw/bucket/teuth-test-bucket', params={ 'bucket_id': data['id'], 'uid': 'teuth-test-user', 'versioning_state': 'Suspended', 'mfa_delete': 'Disabled', 'mfa_token_serial': self._mfa_token_serial, 'mfa_token_pin': self._get_mfa_token_pin() }) self.assertStatus(200) data = self._get('/api/rgw/bucket/teuth-test-bucket') self.assertStatus(200) self.assertEqual(data['versioning'], 'Suspended') self.assertEqual(data['mfa_delete'], 'Disabled') # Delete the bucket. self._delete('/api/rgw/bucket/teuth-test-bucket') self.assertStatus(204) data = self._get('/api/rgw/bucket') self.assertStatus(200) self.assertEqual(len(data), 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)
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)
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)