Remove deprecated config options for volume API selection
Volume APIs tests have been moved to v3 as default and can be run under v2 API via catalog_type. - https://review.opendev.org/#/q/topic:volume-default-testing-v3+(status:open+OR+status:merged) Below config options were deprecated since Rocky and now time to remove them: * ``CONF.volume-feature-enabled.api_v2`` * ``CONF.volume-feature-enabled.api_v3`` Change-Id: I31f487694e5f5e586f99323c81e45f43306606fb
This commit is contained in:
parent
fec2c93cdc
commit
449287d45e
@ -0,0 +1,9 @@
|
|||||||
|
---
|
||||||
|
upgrade:
|
||||||
|
- |
|
||||||
|
Deprecated config options to select the Volume API version have been
|
||||||
|
removed. Use ``CONF.volume.catalog_type`` to run volume tests under v3
|
||||||
|
or v2 APIs.
|
||||||
|
|
||||||
|
* ``CONF.volume-feature-enabled.api_v2``
|
||||||
|
* ``CONF.volume-feature-enabled.api_v3``
|
@ -20,7 +20,6 @@ from tempest import config
|
|||||||
from tempest.lib.common import api_version_utils
|
from tempest.lib.common import api_version_utils
|
||||||
from tempest.lib.common.utils import data_utils
|
from tempest.lib.common.utils import data_utils
|
||||||
from tempest.lib.common.utils import test_utils
|
from tempest.lib.common.utils import test_utils
|
||||||
from tempest.lib import exceptions
|
|
||||||
import tempest.test
|
import tempest.test
|
||||||
|
|
||||||
CONF = config.CONF
|
CONF = config.CONF
|
||||||
@ -34,11 +33,6 @@ class BaseVolumeTest(api_version_utils.BaseMicroversionTest,
|
|||||||
# https://bugs.launchpad.net/tempest/+bug/1844568
|
# https://bugs.launchpad.net/tempest/+bug/1844568
|
||||||
create_default_network = False
|
create_default_network = False
|
||||||
_api_version = 2
|
_api_version = 2
|
||||||
# if api_v2 is not enabled while api_v3 is enabled, the volume v2 classes
|
|
||||||
# should be transferred to volume v3 classes.
|
|
||||||
if (not CONF.volume_feature_enabled.api_v2 and
|
|
||||||
CONF.volume_feature_enabled.api_v3):
|
|
||||||
_api_version = 3
|
|
||||||
credentials = ['primary']
|
credentials = ['primary']
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -48,17 +42,6 @@ class BaseVolumeTest(api_version_utils.BaseMicroversionTest,
|
|||||||
if not CONF.service_available.cinder:
|
if not CONF.service_available.cinder:
|
||||||
skip_msg = ("%s skipped as Cinder is not available" % cls.__name__)
|
skip_msg = ("%s skipped as Cinder is not available" % cls.__name__)
|
||||||
raise cls.skipException(skip_msg)
|
raise cls.skipException(skip_msg)
|
||||||
if cls._api_version == 2:
|
|
||||||
if not CONF.volume_feature_enabled.api_v2:
|
|
||||||
msg = "Volume API v2 is disabled"
|
|
||||||
raise cls.skipException(msg)
|
|
||||||
elif cls._api_version == 3:
|
|
||||||
if not CONF.volume_feature_enabled.api_v3:
|
|
||||||
msg = "Volume API v3 is disabled"
|
|
||||||
raise cls.skipException(msg)
|
|
||||||
else:
|
|
||||||
msg = ("Invalid Cinder API version (%s)" % cls._api_version)
|
|
||||||
raise exceptions.InvalidConfiguration(msg)
|
|
||||||
|
|
||||||
api_version_utils.check_skip_with_microversion(
|
api_version_utils.check_skip_with_microversion(
|
||||||
cls.min_microversion, cls.max_microversion,
|
cls.min_microversion, cls.max_microversion,
|
||||||
|
@ -231,89 +231,85 @@ class Manager(clients.ServiceClients):
|
|||||||
|
|
||||||
def _set_volume_clients(self):
|
def _set_volume_clients(self):
|
||||||
|
|
||||||
# if only api_v3 is enabled, all these clients should be available
|
self.backups_client_latest = self.volume_v3.BackupsClient()
|
||||||
if (CONF.volume_feature_enabled.api_v2 or
|
self.encryption_types_client_latest = \
|
||||||
CONF.volume_feature_enabled.api_v3):
|
self.volume_v3.EncryptionTypesClient()
|
||||||
self.backups_client_latest = self.volume_v3.BackupsClient()
|
self.snapshot_manage_client_latest = \
|
||||||
self.encryption_types_client_latest = \
|
self.volume_v3.SnapshotManageClient()
|
||||||
self.volume_v3.EncryptionTypesClient()
|
self.snapshots_client_latest = self.volume_v3.SnapshotsClient()
|
||||||
self.snapshot_manage_client_latest = \
|
self.volume_capabilities_client_latest = \
|
||||||
self.volume_v3.SnapshotManageClient()
|
self.volume_v3.CapabilitiesClient()
|
||||||
self.snapshots_client_latest = self.volume_v3.SnapshotsClient()
|
self.volume_manage_client_latest = (
|
||||||
self.volume_capabilities_client_latest = \
|
self.volume_v3.VolumeManageClient())
|
||||||
self.volume_v3.CapabilitiesClient()
|
self.volume_qos_client_latest = self.volume_v3.QosSpecsClient()
|
||||||
self.volume_manage_client_latest = (
|
self.volume_services_client_latest = (
|
||||||
self.volume_v3.VolumeManageClient())
|
self.volume_v3.ServicesClient())
|
||||||
self.volume_qos_client_latest = self.volume_v3.QosSpecsClient()
|
self.volume_types_client_latest = self.volume_v3.TypesClient()
|
||||||
self.volume_services_client_latest = (
|
self.volume_hosts_client_latest = self.volume_v3.HostsClient()
|
||||||
self.volume_v3.ServicesClient())
|
self.volume_quotas_client_latest = self.volume_v3.QuotasClient()
|
||||||
self.volume_types_client_latest = self.volume_v3.TypesClient()
|
self.volume_quota_classes_client_latest = \
|
||||||
self.volume_hosts_client_latest = self.volume_v3.HostsClient()
|
self.volume_v3.QuotaClassesClient()
|
||||||
self.volume_quotas_client_latest = self.volume_v3.QuotasClient()
|
self.volume_scheduler_stats_client_latest = \
|
||||||
self.volume_quota_classes_client_latest = \
|
self.volume_v3.SchedulerStatsClient()
|
||||||
self.volume_v3.QuotaClassesClient()
|
self.volume_transfers_client_latest = \
|
||||||
self.volume_scheduler_stats_client_latest = \
|
self.volume_v3.TransfersClient()
|
||||||
self.volume_v3.SchedulerStatsClient()
|
self.volume_availability_zone_client_latest = \
|
||||||
self.volume_transfers_client_latest = \
|
self.volume_v3.AvailabilityZoneClient()
|
||||||
self.volume_v3.TransfersClient()
|
self.volume_limits_client_latest = self.volume_v3.LimitsClient()
|
||||||
self.volume_availability_zone_client_latest = \
|
self.volumes_client_latest = self.volume_v3.VolumesClient()
|
||||||
self.volume_v3.AvailabilityZoneClient()
|
self.volumes_extension_client_latest = \
|
||||||
self.volume_limits_client_latest = self.volume_v3.LimitsClient()
|
self.volume_v3.ExtensionsClient()
|
||||||
self.volumes_client_latest = self.volume_v3.VolumesClient()
|
self.group_types_client_latest = self.volume_v3.GroupTypesClient()
|
||||||
self.volumes_extension_client_latest = \
|
self.groups_client_latest = self.volume_v3.GroupsClient()
|
||||||
self.volume_v3.ExtensionsClient()
|
self.group_snapshots_client_latest = \
|
||||||
self.group_types_client_latest = self.volume_v3.GroupTypesClient()
|
self.volume_v3.GroupSnapshotsClient()
|
||||||
self.groups_client_latest = self.volume_v3.GroupsClient()
|
self.volume_messages_client_latest = (
|
||||||
self.group_snapshots_client_latest = \
|
self.volume_v3.MessagesClient())
|
||||||
self.volume_v3.GroupSnapshotsClient()
|
self.volume_versions_client_latest = (
|
||||||
self.volume_messages_client_latest = (
|
self.volume_v3.VersionsClient())
|
||||||
self.volume_v3.MessagesClient())
|
self.attachments_client_latest = (
|
||||||
self.volume_versions_client_latest = (
|
self.volume_v3.AttachmentsClient())
|
||||||
self.volume_v3.VersionsClient())
|
|
||||||
self.attachments_client_latest = (
|
|
||||||
self.volume_v3.AttachmentsClient())
|
|
||||||
|
|
||||||
# TODO(gmann): Below alias for service clients have been
|
# TODO(gmann): Below alias for service clients have been
|
||||||
# deprecated and will be removed in future. Start using the alias
|
# deprecated and will be removed in future. Start using the alias
|
||||||
# defined above with suffix _latest.
|
# defined above with suffix _latest.
|
||||||
# ****************Deprecated alias start from here***************
|
# ****************Deprecated alias start from here***************
|
||||||
self.backups_v2_client = self.volume_v3.BackupsClient()
|
self.backups_v2_client = self.volume_v3.BackupsClient()
|
||||||
self.encryption_types_v2_client = \
|
self.encryption_types_v2_client = \
|
||||||
self.volume_v3.EncryptionTypesClient()
|
self.volume_v3.EncryptionTypesClient()
|
||||||
self.snapshot_manage_v2_client = \
|
self.snapshot_manage_v2_client = \
|
||||||
self.volume_v3.SnapshotManageClient()
|
self.volume_v3.SnapshotManageClient()
|
||||||
self.snapshots_v2_client = self.volume_v3.SnapshotsClient()
|
self.snapshots_v2_client = self.volume_v3.SnapshotsClient()
|
||||||
self.volume_capabilities_v2_client = \
|
self.volume_capabilities_v2_client = \
|
||||||
self.volume_v3.CapabilitiesClient()
|
self.volume_v3.CapabilitiesClient()
|
||||||
self.volume_manage_v2_client = self.volume_v3.VolumeManageClient()
|
self.volume_manage_v2_client = self.volume_v3.VolumeManageClient()
|
||||||
self.volume_qos_v2_client = self.volume_v3.QosSpecsClient()
|
self.volume_qos_v2_client = self.volume_v3.QosSpecsClient()
|
||||||
self.volume_services_v2_client = self.volume_v3.ServicesClient()
|
self.volume_services_v2_client = self.volume_v3.ServicesClient()
|
||||||
self.volume_types_v2_client = self.volume_v3.TypesClient()
|
self.volume_types_v2_client = self.volume_v3.TypesClient()
|
||||||
self.volume_hosts_v2_client = self.volume_v3.HostsClient()
|
self.volume_hosts_v2_client = self.volume_v3.HostsClient()
|
||||||
self.volume_quotas_v2_client = self.volume_v3.QuotasClient()
|
self.volume_quotas_v2_client = self.volume_v3.QuotasClient()
|
||||||
self.volume_quota_classes_v2_client = \
|
self.volume_quota_classes_v2_client = \
|
||||||
self.volume_v3.QuotaClassesClient()
|
self.volume_v3.QuotaClassesClient()
|
||||||
self.volume_scheduler_stats_v2_client = \
|
self.volume_scheduler_stats_v2_client = \
|
||||||
self.volume_v3.SchedulerStatsClient()
|
self.volume_v3.SchedulerStatsClient()
|
||||||
self.volume_transfers_v2_client = self.volume_v3.TransfersClient()
|
self.volume_transfers_v2_client = self.volume_v3.TransfersClient()
|
||||||
self.volume_v2_availability_zone_client = \
|
self.volume_v2_availability_zone_client = \
|
||||||
self.volume_v3.AvailabilityZoneClient()
|
self.volume_v3.AvailabilityZoneClient()
|
||||||
self.volume_v2_limits_client = self.volume_v3.LimitsClient()
|
self.volume_v2_limits_client = self.volume_v3.LimitsClient()
|
||||||
self.volumes_v2_client = self.volume_v3.VolumesClient()
|
self.volumes_v2_client = self.volume_v3.VolumesClient()
|
||||||
self.volumes_v2_extension_client = \
|
self.volumes_v2_extension_client = \
|
||||||
self.volume_v3.ExtensionsClient()
|
self.volume_v3.ExtensionsClient()
|
||||||
|
|
||||||
if CONF.volume_feature_enabled.api_v3:
|
self.backups_v3_client = self.volume_v3.BackupsClient()
|
||||||
self.backups_v3_client = self.volume_v3.BackupsClient()
|
self.group_types_v3_client = self.volume_v3.GroupTypesClient()
|
||||||
self.group_types_v3_client = self.volume_v3.GroupTypesClient()
|
self.groups_v3_client = self.volume_v3.GroupsClient()
|
||||||
self.groups_v3_client = self.volume_v3.GroupsClient()
|
self.group_snapshots_v3_client = \
|
||||||
self.group_snapshots_v3_client = \
|
self.volume_v3.GroupSnapshotsClient()
|
||||||
self.volume_v3.GroupSnapshotsClient()
|
self.snapshots_v3_client = self.volume_v3.SnapshotsClient()
|
||||||
self.snapshots_v3_client = self.volume_v3.SnapshotsClient()
|
self.volume_v3_messages_client = self.volume_v3.MessagesClient()
|
||||||
self.volume_v3_messages_client = self.volume_v3.MessagesClient()
|
self.volume_v3_versions_client = self.volume_v3.VersionsClient()
|
||||||
self.volume_v3_versions_client = self.volume_v3.VersionsClient()
|
self.volumes_v3_client = self.volume_v3.VolumesClient()
|
||||||
self.volumes_v3_client = self.volume_v3.VolumesClient()
|
# ****************Deprecated alias end here***********************
|
||||||
# ****************Deprecated alias end here***********************
|
|
||||||
|
|
||||||
def _set_object_storage_clients(self):
|
def _set_object_storage_clients(self):
|
||||||
self.account_client = self.object_storage.AccountClient()
|
self.account_client = self.object_storage.AccountClient()
|
||||||
|
@ -202,22 +202,8 @@ def verify_keystone_api_versions(os, update):
|
|||||||
not CONF.identity_feature_enabled.api_v3, update)
|
not CONF.identity_feature_enabled.api_v3, update)
|
||||||
|
|
||||||
|
|
||||||
def verify_cinder_api_versions(os, update):
|
|
||||||
# Check cinder api versions
|
|
||||||
versions = _get_api_versions(os, 'cinder')
|
|
||||||
if (CONF.volume_feature_enabled.api_v2 !=
|
|
||||||
contains_version('v2.', versions)):
|
|
||||||
print_and_or_update('api_v2', 'volume-feature-enabled',
|
|
||||||
not CONF.volume_feature_enabled.api_v2, update)
|
|
||||||
if (CONF.volume_feature_enabled.api_v3 !=
|
|
||||||
contains_version('v3.', versions)):
|
|
||||||
print_and_or_update('api_v3', 'volume-feature-enabled',
|
|
||||||
not CONF.volume_feature_enabled.api_v3, update)
|
|
||||||
|
|
||||||
|
|
||||||
def verify_api_versions(os, service, update):
|
def verify_api_versions(os, service, update):
|
||||||
verify = {
|
verify = {
|
||||||
'cinder': verify_cinder_api_versions,
|
|
||||||
'glance': verify_glance_api_versions,
|
'glance': verify_glance_api_versions,
|
||||||
'keystone': verify_keystone_api_versions,
|
'keystone': verify_keystone_api_versions,
|
||||||
}
|
}
|
||||||
|
@ -1007,31 +1007,6 @@ VolumeFeaturesGroup = [
|
|||||||
help='A list of enabled volume extensions with a special '
|
help='A list of enabled volume extensions with a special '
|
||||||
'entry all which indicates every extension is enabled. '
|
'entry all which indicates every extension is enabled. '
|
||||||
'Empty list indicates all extensions are disabled'),
|
'Empty list indicates all extensions are disabled'),
|
||||||
cfg.BoolOpt('api_v2',
|
|
||||||
default=True,
|
|
||||||
help="Is the v2 volume API enabled",
|
|
||||||
deprecated_for_removal=True,
|
|
||||||
deprecated_reason="The v2 volume API has been deprecated "
|
|
||||||
"since Pike release. Now Tempest run all "
|
|
||||||
"the volume tests against v2 or v3 API "
|
|
||||||
"based on CONF.volume.catalog_type which "
|
|
||||||
"makes this config option unusable. If "
|
|
||||||
"catalog_type is volumev2, then all the "
|
|
||||||
"volume tests will run against v2 API. "
|
|
||||||
"Use ``CONF.volume.catalog_type`` to run "
|
|
||||||
"the Tempest against volume v2 or v3 API"),
|
|
||||||
cfg.BoolOpt('api_v3',
|
|
||||||
default=True,
|
|
||||||
help="Is the v3 volume API enabled",
|
|
||||||
deprecated_for_removal=True,
|
|
||||||
deprecated_reason="Tempest run all the volume tests against "
|
|
||||||
"v2 or v3 API based on "
|
|
||||||
"CONF.volume.catalog_type which makes this "
|
|
||||||
"config option unusable. If catalog_type is "
|
|
||||||
"volumev3 which is default, then all the "
|
|
||||||
"volume tests will run against v3 API. "
|
|
||||||
"Use ``CONF.volume.catalog_type`` to run "
|
|
||||||
"the Tempest against volume v2 or v3 API"),
|
|
||||||
cfg.BoolOpt('extend_attached_volume',
|
cfg.BoolOpt('extend_attached_volume',
|
||||||
default=False,
|
default=False,
|
||||||
help='Does the cloud support extending the size of a volume '
|
help='Does the cloud support extending the size of a volume '
|
||||||
|
@ -97,15 +97,15 @@ class TestDiscovery(base.TestCase):
|
|||||||
self.useFixture(fixtures.MockPatchObject(
|
self.useFixture(fixtures.MockPatchObject(
|
||||||
verify_tempest_config, '_get_unversioned_endpoint',
|
verify_tempest_config, '_get_unversioned_endpoint',
|
||||||
return_value='http://fake_endpoint:5000'))
|
return_value='http://fake_endpoint:5000'))
|
||||||
fake_resp = {'versions': [{'id': 'v1.0'}, {'id': 'v2.0'}]}
|
fake_resp = {'versions': [{'id': 'v2.0'}, {'id': 'v3.0'}]}
|
||||||
fake_resp = json.dumps(fake_resp)
|
fake_resp = json.dumps(fake_resp)
|
||||||
self.useFixture(fixtures.MockPatch(
|
self.useFixture(fixtures.MockPatch(
|
||||||
'tempest.lib.common.http.ClosingHttp.request',
|
'tempest.lib.common.http.ClosingHttp.request',
|
||||||
return_value=(None, fake_resp)))
|
return_value=(None, fake_resp)))
|
||||||
fake_os = mock.MagicMock()
|
fake_os = mock.MagicMock()
|
||||||
versions = verify_tempest_config._get_api_versions(fake_os, 'cinder')
|
versions = verify_tempest_config._get_api_versions(fake_os, 'cinder')
|
||||||
self.assertIn('v1.0', versions)
|
|
||||||
self.assertIn('v2.0', versions)
|
self.assertIn('v2.0', versions)
|
||||||
|
self.assertIn('v3.0', versions)
|
||||||
|
|
||||||
def test_get_nova_versions(self):
|
def test_get_nova_versions(self):
|
||||||
self.useFixture(fixtures.MockPatchObject(
|
self.useFixture(fixtures.MockPatchObject(
|
||||||
@ -145,7 +145,7 @@ class TestDiscovery(base.TestCase):
|
|||||||
self.assertTrue(mock_log_error.called)
|
self.assertTrue(mock_log_error.called)
|
||||||
|
|
||||||
def test_verify_api_versions(self):
|
def test_verify_api_versions(self):
|
||||||
api_services = ['cinder', 'glance', 'keystone']
|
api_services = ['glance', 'keystone']
|
||||||
fake_os = mock.MagicMock()
|
fake_os = mock.MagicMock()
|
||||||
for svc in api_services:
|
for svc in api_services:
|
||||||
m = 'verify_%s_api_versions' % svc
|
m = 'verify_%s_api_versions' % svc
|
||||||
@ -154,7 +154,7 @@ class TestDiscovery(base.TestCase):
|
|||||||
verify_mock.assert_called_once_with(fake_os, True)
|
verify_mock.assert_called_once_with(fake_os, True)
|
||||||
|
|
||||||
def test_verify_api_versions_not_implemented(self):
|
def test_verify_api_versions_not_implemented(self):
|
||||||
api_services = ['cinder', 'glance', 'keystone']
|
api_services = ['glance', 'keystone']
|
||||||
fake_os = mock.MagicMock()
|
fake_os = mock.MagicMock()
|
||||||
for svc in api_services:
|
for svc in api_services:
|
||||||
m = 'verify_%s_api_versions' % svc
|
m = 'verify_%s_api_versions' % svc
|
||||||
@ -178,52 +178,6 @@ class TestDiscovery(base.TestCase):
|
|||||||
'identity-feature-enabled',
|
'identity-feature-enabled',
|
||||||
False, True)
|
False, True)
|
||||||
|
|
||||||
@mock.patch('tempest.lib.common.http.ClosingHttp.request')
|
|
||||||
def test_verify_cinder_api_versions_no_v3(self, mock_request):
|
|
||||||
self.useFixture(fixtures.MockPatchObject(
|
|
||||||
verify_tempest_config, '_get_unversioned_endpoint',
|
|
||||||
return_value='http://fake_endpoint:5000'))
|
|
||||||
fake_resp = {'versions': [{'id': 'v2.0'}]}
|
|
||||||
fake_resp = json.dumps(fake_resp)
|
|
||||||
mock_request.return_value = (None, fake_resp)
|
|
||||||
fake_os = mock.MagicMock()
|
|
||||||
with mock.patch.object(verify_tempest_config,
|
|
||||||
'print_and_or_update') as print_mock:
|
|
||||||
verify_tempest_config.verify_cinder_api_versions(fake_os, True)
|
|
||||||
print_mock.assert_any_call('api_v3', 'volume-feature-enabled',
|
|
||||||
False, True)
|
|
||||||
self.assertEqual(1, print_mock.call_count)
|
|
||||||
|
|
||||||
@mock.patch('tempest.lib.common.http.ClosingHttp.request')
|
|
||||||
def test_verify_cinder_api_versions_no_v2(self, mock_request):
|
|
||||||
self.useFixture(fixtures.MockPatchObject(
|
|
||||||
verify_tempest_config, '_get_unversioned_endpoint',
|
|
||||||
return_value='http://fake_endpoint:5000'))
|
|
||||||
fake_resp = {'versions': [{'id': 'v3.0'}]}
|
|
||||||
fake_resp = json.dumps(fake_resp)
|
|
||||||
mock_request.return_value = (None, fake_resp)
|
|
||||||
fake_os = mock.MagicMock()
|
|
||||||
with mock.patch.object(verify_tempest_config,
|
|
||||||
'print_and_or_update') as print_mock:
|
|
||||||
verify_tempest_config.verify_cinder_api_versions(fake_os, True)
|
|
||||||
print_mock.assert_any_call('api_v2', 'volume-feature-enabled',
|
|
||||||
False, True)
|
|
||||||
self.assertEqual(1, print_mock.call_count)
|
|
||||||
|
|
||||||
@mock.patch('tempest.lib.common.http.ClosingHttp.request')
|
|
||||||
def test_verify_cinder_api_versions_no_v1(self, mock_request):
|
|
||||||
self.useFixture(fixtures.MockPatchObject(
|
|
||||||
verify_tempest_config, '_get_unversioned_endpoint',
|
|
||||||
return_value='http://fake_endpoint:5000'))
|
|
||||||
fake_resp = {'versions': [{'id': 'v2.0'}, {'id': 'v3.0'}]}
|
|
||||||
fake_resp = json.dumps(fake_resp)
|
|
||||||
mock_request.return_value = (None, fake_resp)
|
|
||||||
fake_os = mock.MagicMock()
|
|
||||||
with mock.patch.object(verify_tempest_config,
|
|
||||||
'print_and_or_update') as print_mock:
|
|
||||||
verify_tempest_config.verify_cinder_api_versions(fake_os, True)
|
|
||||||
print_mock.assert_not_called()
|
|
||||||
|
|
||||||
def test_verify_glance_version_no_v2_with_v1_1(self):
|
def test_verify_glance_version_no_v2_with_v1_1(self):
|
||||||
# This test verifies that wrong config api_v2 = True is detected
|
# This test verifies that wrong config api_v2 = True is detected
|
||||||
class FakeClient(object):
|
class FakeClient(object):
|
||||||
|
Loading…
Reference in New Issue
Block a user