quota: Migrate 'quota *' to SDK

This is done for both the compute and block storage services. The
network service was already using SDK.

Change-Id: Id7a6943b8b4d7d1330e7d7f13705f027d1b67189
Signed-off-by: Stephen Finucane <stephenfin@redhat.com>
Depends-on: https://review.opendev.org/c/openstack/openstacksdk/+/923864
This commit is contained in:
Stephen Finucane 2024-07-11 11:43:56 +01:00
parent b4f30a1583
commit 6127b44d0a
7 changed files with 559 additions and 887 deletions

View File

@ -19,6 +19,7 @@ import itertools
import logging
import sys
from openstack import exceptions as sdk_exceptions
from osc_lib.command import command
from osc_lib import exceptions
from osc_lib import utils
@ -104,11 +105,8 @@ def _xform_get_quota(data, value, keys):
def get_project(app, project):
if project is not None:
identity_client = app.client_manager.identity
project = utils.find_resource(
identity_client.projects,
project,
)
identity_client = app.client_manager.sdk_connection.identity
project = identity_client.find_project(project, ignore_missing=False)
project_id = project.id
project_name = project.name
elif app.client_manager.auth_ref:
@ -134,16 +132,18 @@ def get_compute_quotas(
default=False,
):
try:
client = app.client_manager.compute
client = app.client_manager.sdk_connection.compute
if default:
quota = client.quotas.defaults(project_id)
quota = client.get_quota_set_defaults(project_id)
else:
quota = client.quotas.get(project_id, detail=detail)
except Exception as e:
if type(e).__name__ == 'EndpointNotFound':
return {}
raise
return quota._info
quota = client.get_quota_set(project_id, usage=detail)
except sdk_exceptions.EndpointNotFound:
return {}
data = quota.to_dict()
if not detail:
del data['usage']
del data['reservation']
return data
def get_volume_quotas(
@ -154,17 +154,18 @@ def get_volume_quotas(
default=False,
):
try:
client = app.client_manager.volume
client = app.client_manager.sdk_connection.volume
if default:
quota = client.quotas.defaults(project_id)
quota = client.get_quota_set_defaults(project_id)
else:
quota = client.quotas.get(project_id, usage=detail)
except Exception as e:
if type(e).__name__ == 'EndpointNotFound':
return {}
else:
raise
return quota._info
quota = client.get_quota_set(project_id, usage=detail)
except sdk_exceptions.EndpointNotFound:
return {}
data = quota.to_dict()
if not detail:
del data['usage']
del data['reservation']
return data
def get_network_quotas(
@ -242,37 +243,35 @@ class ListQuota(command.Lister):
return parser
def _list_quota_compute(self, parsed_args, project_ids):
compute_client = self.app.client_manager.compute
compute_client = self.app.client_manager.sdk_connection.compute
result = []
for p in project_ids:
for project_id in project_ids:
try:
data = compute_client.quotas.get(p)
except Exception as ex:
if (
type(ex).__name__ == 'NotFound'
or ex.http_status >= 400
and ex.http_status <= 499
):
# Project not found, move on to next one
LOG.warning(f"Project {p} not found: {ex}")
continue
else:
raise
project_data = compute_client.get_quota_set(project_id)
except (
sdk_exceptions.NotFoundException,
sdk_exceptions.ForbiddenException,
) as exc:
# Project not found, move on to next one
LOG.warning(f"Project {project_id} not found: {exc}")
continue
result_data = _xform_get_quota(
data,
p,
project_result = _xform_get_quota(
project_data,
project_id,
COMPUTE_QUOTAS.keys(),
)
default_data = compute_client.quotas.defaults(p)
result_default = _xform_get_quota(
default_data = compute_client.get_quota_set_defaults(project_id)
default_result = _xform_get_quota(
default_data,
p,
project_id,
COMPUTE_QUOTAS.keys(),
)
if result_default != result_data:
result += result_data
if default_result != project_result:
result += project_result
columns = (
'id',
@ -306,33 +305,35 @@ class ListQuota(command.Lister):
)
def _list_quota_volume(self, parsed_args, project_ids):
volume_client = self.app.client_manager.volume
volume_client = self.app.client_manager.sdk_connection.volume
result = []
for p in project_ids:
for project_id in project_ids:
try:
data = volume_client.quotas.get(p)
except Exception as ex:
if type(ex).__name__ == 'NotFound':
# Project not found, move on to next one
LOG.warning(f"Project {p} not found: {ex}")
continue
else:
raise
project_data = volume_client.get_quota_set(project_id)
except (
sdk_exceptions.NotFoundException,
sdk_exceptions.ForbiddenException,
) as exc:
# Project not found, move on to next one
LOG.warning(f"Project {project_id} not found: {exc}")
continue
result_data = _xform_get_quota(
data,
p,
project_result = _xform_get_quota(
project_data,
project_id,
VOLUME_QUOTAS.keys(),
)
default_data = volume_client.quotas.defaults(p)
result_default = _xform_get_quota(
default_data = volume_client.get_quota_set_defaults(project_id)
default_result = _xform_get_quota(
default_data,
p,
project_id,
VOLUME_QUOTAS.keys(),
)
if result_default != result_data:
result += result_data
if default_result != project_result:
result += project_result
columns = (
'id',
@ -359,33 +360,35 @@ class ListQuota(command.Lister):
)
def _list_quota_network(self, parsed_args, project_ids):
client = self.app.client_manager.network
network_client = self.app.client_manager.network
result = []
for p in project_ids:
for project_id in project_ids:
try:
data = client.get_quota(p)
except Exception as ex:
if type(ex).__name__ == 'NotFound':
# Project not found, move on to next one
LOG.warning(f"Project {p} not found: {ex}")
continue
else:
raise
project_data = network_client.get_quota(project_id)
except (
sdk_exceptions.NotFoundException,
sdk_exceptions.ForbiddenException,
) as exc:
# Project not found, move on to next one
LOG.warning(f"Project {project_id} not found: {exc}")
continue
result_data = _xform_get_quota(
data,
p,
project_result = _xform_get_quota(
project_data,
project_id,
NETWORK_KEYS,
)
default_data = client.get_quota_default(p)
result_default = _xform_get_quota(
default_data = network_client.get_quota_default(project_id)
default_result = _xform_get_quota(
default_data,
p,
project_id,
NETWORK_KEYS,
)
if result_default != result_data:
result += result_data
if default_result != project_result:
result += project_result
columns = (
'id',
@ -419,7 +422,8 @@ class ListQuota(command.Lister):
def take_action(self, parsed_args):
project_ids = [
p.id for p in self.app.client_manager.identity.projects.list()
p.id
for p in self.app.client_manager.sdk_connection.identity.projects()
]
if parsed_args.compute:
return self._list_quota_compute(parsed_args, project_ids)
@ -561,8 +565,8 @@ class SetQuota(common.NetDetectionMixin, command.Command):
msg = _('--force cannot be used with --class or --default')
raise exceptions.CommandError(msg)
compute_client = self.app.client_manager.compute
volume_client = self.app.client_manager.volume
compute_client = self.app.client_manager.sdk_connection.compute
volume_client = self.app.client_manager.sdk_connection.volume
compute_kwargs = {}
for k, v in COMPUTE_QUOTAS.items():
@ -570,7 +574,7 @@ class SetQuota(common.NetDetectionMixin, command.Command):
if value is not None:
compute_kwargs[k] = value
if parsed_args.force is True:
if compute_kwargs and parsed_args.force is True:
compute_kwargs['force'] = parsed_args.force
volume_kwargs = {}
@ -604,12 +608,12 @@ class SetQuota(common.NetDetectionMixin, command.Command):
if parsed_args.quota_class or parsed_args.default:
if compute_kwargs:
compute_client.quota_classes.update(
compute_client.update_quota_class_set(
parsed_args.project or 'default',
**compute_kwargs,
)
if volume_kwargs:
volume_client.quota_classes.update(
volume_client.update_quota_class_set(
parsed_args.project or 'default',
**volume_kwargs,
)
@ -625,9 +629,9 @@ class SetQuota(common.NetDetectionMixin, command.Command):
project = project_info['id']
if compute_kwargs:
compute_client.quotas.update(project, **compute_kwargs)
compute_client.update_quota_set(project, **compute_kwargs)
if volume_kwargs:
volume_client.quotas.update(project, **volume_kwargs)
volume_client.update_quota_set(project, **volume_kwargs)
if (
network_kwargs
and self.app.client_manager.is_network_endpoint_enabled()
@ -765,14 +769,24 @@ and ``server-group-members`` output for a given quota class."""
if 'id' in info:
del info['id']
# Remove the 'location' field for resources from openstacksdk
if 'location' in info:
del info['location']
# Remove the sdk-derived fields
for field in ('location', 'name', 'force'):
if field in info:
del info[field]
if not parsed_args.usage:
result = [{'resource': k, 'limit': v} for k, v in info.items()]
else:
result = [{'resource': k, **v} for k, v in info.items()]
result = [
{
'resource': k,
'limit': v or 0,
'in_use': info['usage'].get(k, 0),
'reserved': info['reservation'].get(k, 0),
}
for k, v in info.items()
if k not in ('usage', 'reservation')
]
columns = (
'resource',
@ -850,21 +864,20 @@ class DeleteQuota(command.Command):
return parser
def take_action(self, parsed_args):
identity_client = self.app.client_manager.identity
project = utils.find_resource(
identity_client.projects,
parsed_args.project,
identity_client = self.app.client_manager.sdk_connection.identity
project = identity_client.find_project(
parsed_args.project, ignore_missing=False
)
# compute quotas
if parsed_args.service in {'all', 'compute'}:
compute_client = self.app.client_manager.compute
compute_client.quotas.delete(project.id)
compute_client = self.app.client_manager.sdk_connection.compute
compute_client.revert_quota_set(project.id)
# volume quotas
if parsed_args.service in {'all', 'volume'}:
volume_client = self.app.client_manager.volume
volume_client.quotas.delete(project.id)
volume_client = self.app.client_manager.sdk_connection.volume
volume_client.revert_quota_set(project.id)
# network quotas (but only if we're not using nova-network, otherwise
# we already deleted the quotas in the compute step)

File diff suppressed because it is too large Load Diff

View File

@ -46,42 +46,6 @@ from openstackclient.tests.unit.network.v2 import fakes as network_fakes
from openstackclient.tests.unit import utils
from openstackclient.tests.unit.volume.v3 import fakes as volume_fakes
floating_ip_num = 100
fix_ip_num = 100
injected_file_num = 100
injected_file_size_num = 10240
injected_path_size_num = 255
key_pair_num = 100
core_num = 20
ram_num = 51200
instance_num = 10
property_num = 128
secgroup_rule_num = 20
secgroup_num = 10
servgroup_num = 10
servgroup_members_num = 10
project_name = 'project_test'
QUOTA = {
'project': project_name,
'floating-ips': floating_ip_num,
'fix-ips': fix_ip_num,
'injected-files': injected_file_num,
'injected-file-size': injected_file_size_num,
'injected-path-size': injected_path_size_num,
'key-pairs': key_pair_num,
'cores': core_num,
'ram': ram_num,
'instances': instance_num,
'properties': property_num,
'secgroup_rules': secgroup_rule_num,
'secgroups': secgroup_num,
'server-groups': servgroup_num,
'server-group-members': servgroup_members_num,
}
QUOTA_columns = tuple(sorted(QUOTA))
QUOTA_data = tuple(QUOTA[x] for x in sorted(QUOTA))
class FakeComputev2Client:
def __init__(self, **kwargs):
@ -105,12 +69,6 @@ class FakeComputev2Client:
self.flavor_access = mock.Mock()
self.flavor_access.resource_class = fakes.FakeResource(None, {})
self.quotas = mock.Mock()
self.quotas.resource_class = fakes.FakeResource(None, {})
self.quota_classes = mock.Mock()
self.quota_classes.resource_class = fakes.FakeResource(None, {})
self.usage = mock.Mock()
self.usage.resource_class = fakes.FakeResource(None, {})
@ -1077,98 +1035,6 @@ def create_usages(attrs=None, count=2):
return usages
def create_one_comp_quota(attrs=None):
"""Create one quota"""
attrs = attrs or {}
quota_attrs = {
'id': 'project-id-' + uuid.uuid4().hex,
'cores': 20,
'fixed_ips': 30,
'injected_files': 100,
'injected_file_content_bytes': 10240,
'injected_file_path_bytes': 255,
'instances': 50,
'key_pairs': 20,
'metadata_items': 10,
'ram': 51200,
'server_groups': 10,
'server_group_members': 10,
}
quota_attrs.update(attrs)
quota = fakes.FakeResource(info=copy.deepcopy(quota_attrs), loaded=True)
quota.project_id = quota_attrs['id']
return quota
def create_one_default_comp_quota(attrs=None):
"""Create one quota"""
attrs = attrs or {}
quota_attrs = {
'id': 'project-id-' + uuid.uuid4().hex,
'cores': 10,
'fixed_ips': 10,
'injected_files': 100,
'injected_file_content_bytes': 10240,
'injected_file_path_bytes': 255,
'instances': 20,
'key_pairs': 20,
'metadata_items': 10,
'ram': 51200,
'server_groups': 10,
'server_group_members': 10,
}
quota_attrs.update(attrs)
quota = fakes.FakeResource(info=copy.deepcopy(quota_attrs), loaded=True)
quota.project_id = quota_attrs['id']
return quota
def create_one_comp_detailed_quota(attrs=None):
"""Create one quota"""
attrs = attrs or {}
quota_attrs = {
'id': 'project-id-' + uuid.uuid4().hex,
'cores': {'reserved': 0, 'in_use': 0, 'limit': 20},
'fixed_ips': {'reserved': 0, 'in_use': 0, 'limit': 30},
'injected_files': {'reserved': 0, 'in_use': 0, 'limit': 100},
'injected_file_content_bytes': {
'reserved': 0,
'in_use': 0,
'limit': 10240,
},
'injected_file_path_bytes': {
'reserved': 0,
'in_use': 0,
'limit': 255,
},
'instances': {'reserved': 0, 'in_use': 0, 'limit': 50},
'key_pairs': {'reserved': 0, 'in_use': 0, 'limit': 20},
'metadata_items': {'reserved': 0, 'in_use': 0, 'limit': 10},
'ram': {'reserved': 0, 'in_use': 0, 'limit': 51200},
'server_groups': {'reserved': 0, 'in_use': 0, 'limit': 10},
'server_group_members': {'reserved': 0, 'in_use': 0, 'limit': 10},
}
quota_attrs.update(attrs)
quota = fakes.FakeResource(info=copy.deepcopy(quota_attrs), loaded=True)
quota.project_id = quota_attrs['id']
return quota
def create_limits(attrs=None):
"""Create a fake limits object."""
attrs = attrs or {}

View File

@ -42,22 +42,6 @@ from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes
from openstackclient.tests.unit import utils
QUOTA = {
"subnet": 10,
"network": 10,
"floatingip": 50,
"subnetpool": -1,
"security_group_rule": 100,
"security_group": 10,
"router": 10,
"rbac_policy": -1,
"port": 50,
"vip": 10,
"member": 10,
"healthmonitor": 10,
"l7policy": 5,
}
RULE_TYPE_BANDWIDTH_LIMIT = 'bandwidth-limit'
RULE_TYPE_DSCP_MARKING = 'dscp-marking'
RULE_TYPE_MINIMUM_BANDWIDTH = 'minimum-bandwidth'
@ -990,82 +974,6 @@ class FakeNetworkServiceProvider:
return service_providers
class FakeQuota:
"""Fake quota"""
@staticmethod
def create_one_net_quota(attrs=None):
"""Create one quota"""
attrs = attrs or {}
quota_attrs = {
'floating_ips': 20,
'networks': 25,
'ports': 11,
'rbac_policies': 15,
'routers': 40,
'security_groups': 10,
'security_group_rules': 100,
'subnets': 20,
'subnet_pools': 30,
}
quota_attrs.update(attrs)
quota = fakes.FakeResource(
info=copy.deepcopy(quota_attrs), loaded=True
)
return quota
@staticmethod
def create_one_default_net_quota(attrs=None):
"""Create one quota"""
attrs = attrs or {}
quota_attrs = {
'floatingip': 30,
'network': 20,
'port': 10,
'rbac_policy': 25,
'router': 30,
'security_group': 30,
'security_group_rule': 200,
'subnet': 10,
'subnetpool': 20,
}
quota_attrs.update(attrs)
quota = fakes.FakeResource(
info=copy.deepcopy(quota_attrs), loaded=True
)
return quota
@staticmethod
def create_one_net_detailed_quota(attrs=None):
"""Create one quota"""
attrs = attrs or {}
quota_attrs = {
'floating_ips': {'used': 0, 'reserved': 0, 'limit': 20},
'networks': {'used': 0, 'reserved': 0, 'limit': 25},
'ports': {'used': 0, 'reserved': 0, 'limit': 11},
'rbac_policies': {'used': 0, 'reserved': 0, 'limit': 15},
'routers': {'used': 0, 'reserved': 0, 'limit': 40},
'security_groups': {'used': 0, 'reserved': 0, 'limit': 10},
'security_group_rules': {'used': 0, 'reserved': 0, 'limit': 100},
'subnets': {'used': 0, 'reserved': 0, 'limit': 20},
'subnet_pools': {'used': 0, 'reserved': 0, 'limit': 30},
}
quota_attrs.update(attrs)
quota = fakes.FakeResource(
info=copy.deepcopy(quota_attrs), loaded=True
)
return quota
class FakeFloatingIPPortForwarding:
"""Fake one or more Port forwarding"""

View File

@ -33,19 +33,6 @@ from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes
from openstackclient.tests.unit import utils
QUOTA = {
"gigabytes": 1000,
"volumes": 11,
"snapshots": 10,
"backups": 10,
"backup_gigabytes": 1000,
"per_volume_gigabytes": -1,
"gigabytes_volume_type_backend": -1,
"volumes_volume_type_backend": -1,
"snapshots_volume_type_backend": -1,
}
class FakeVolumeClient:
def __init__(self, **kwargs):
self.auth_token = kwargs['token']
@ -68,10 +55,6 @@ class FakeVolumeClient:
self.pools.resource_class = fakes.FakeResource(None, {})
self.qos_specs = mock.Mock()
self.qos_specs.resource_class = fakes.FakeResource(None, {})
self.quota_classes = mock.Mock()
self.quota_classes.resource_class = fakes.FakeResource(None, {})
self.quotas = mock.Mock()
self.quotas.resource_class = fakes.FakeResource(None, {})
self.restores = mock.Mock()
self.restores.resource_class = fakes.FakeResource(None, {})
self.services = mock.Mock()
@ -991,74 +974,3 @@ def create_one_encryption_volume_type(attrs=None):
info=copy.deepcopy(encryption_info), loaded=True
)
return encryption_type
def create_one_vol_quota(attrs=None):
"""Create one quota"""
attrs = attrs or {}
quota_attrs = {
'id': 'project-id-' + uuid.uuid4().hex,
'backups': 100,
'backup_gigabytes': 100,
'gigabytes': 10,
'per_volume_gigabytes': 10,
'snapshots': 0,
'volumes': 10,
}
quota_attrs.update(attrs)
quota = fakes.FakeResource(info=copy.deepcopy(quota_attrs), loaded=True)
quota.project_id = quota_attrs['id']
return quota
def create_one_default_vol_quota(attrs=None):
"""Create one quota"""
attrs = attrs or {}
quota_attrs = {
'id': 'project-id-' + uuid.uuid4().hex,
'backups': 100,
'backup_gigabytes': 100,
'gigabytes': 100,
'per_volume_gigabytes': 100,
'snapshots': 100,
'volumes': 100,
}
quota_attrs.update(attrs)
quota = fakes.FakeResource(info=copy.deepcopy(quota_attrs), loaded=True)
quota.project_id = quota_attrs['id']
return quota
def create_one_detailed_quota(attrs=None):
"""Create one quota"""
attrs = attrs or {}
quota_attrs = {
'volumes': {'limit': 3, 'in_use': 1, 'reserved': 0},
'per_volume_gigabytes': {'limit': -1, 'in_use': 0, 'reserved': 0},
'snapshots': {'limit': 10, 'in_use': 0, 'reserved': 0},
'gigabytes': {'limit': 1000, 'in_use': 5, 'reserved': 0},
'backups': {'limit': 10, 'in_use': 0, 'reserved': 0},
'backup_gigabytes': {'limit': 1000, 'in_use': 0, 'reserved': 0},
'volumes_lvmdriver-1': {'limit': -1, 'in_use': 1, 'reserved': 0},
'gigabytes_lvmdriver-1': {'limit': -1, 'in_use': 5, 'reserved': 0},
'snapshots_lvmdriver-1': {'limit': -1, 'in_use': 0, 'reserved': 0},
'volumes___DEFAULT__': {'limit': -1, 'in_use': 0, 'reserved': 0},
'gigabytes___DEFAULT__': {'limit': -1, 'in_use': 0, 'reserved': 0},
'snapshots___DEFAULT__': {'limit': -1, 'in_use': 0, 'reserved': 0},
'groups': {'limit': 10, 'in_use': 0, 'reserved': 0},
'id': uuid.uuid4().hex,
}
quota_attrs.update(attrs)
quota = fakes.FakeResource(info=copy.deepcopy(quota_attrs), loaded=True)
return quota

View File

@ -58,10 +58,6 @@ class FakeVolumeClient:
self.group_types.resource_class = fakes.FakeResource(None, {})
self.messages = mock.Mock()
self.messages.resource_class = fakes.FakeResource(None, {})
self.quota_classes = mock.Mock()
self.quota_classes.resource_class = fakes.FakeResource(None, {})
self.quotas = mock.Mock()
self.quotas.resource_class = fakes.FakeResource(None, {})
self.resource_filters = mock.Mock()
self.resource_filters.resource_class = fakes.FakeResource(None, {})
self.restores = mock.Mock()

View File

@ -7,7 +7,7 @@ pbr!=2.1.0,>=2.0.0 # Apache-2.0
cryptography>=2.7 # BSD/Apache-2.0
cliff>=3.5.0 # Apache-2.0
iso8601>=0.1.11 # MIT
openstacksdk>=3.2.0 # Apache-2.0
openstacksdk>=3.3.0 # Apache-2.0
osc-lib>=2.3.0 # Apache-2.0
oslo.i18n>=3.15.3 # Apache-2.0
python-keystoneclient>=3.22.0 # Apache-2.0