Fix networking quota usage show

Quotas details returned from the Neutron service are in different format
then quota details returned from Nova and Cinder services. This patch
fixes helper function to convert data from Neutron to the same
format as data from Nova and Cinder is given.

Closes-Bug: #2102513
Change-Id: I18649f6c2ee179b64b7e605f4ea07d4b0c7a1635
This commit is contained in:
Slawek Kaplonski 2025-03-17 16:08:00 +01:00
parent 966aede8ab
commit 6d27b2f2b6
3 changed files with 67 additions and 17 deletions

View File

@ -176,25 +176,37 @@ def get_network_quotas(
default=False,
):
def _network_quota_to_dict(network_quota, detail=False):
if not isinstance(network_quota, dict):
dict_quota = network_quota.to_dict()
else:
dict_quota = network_quota
dict_quota = network_quota.to_dict(computed=False)
result = {}
if not detail:
return dict_quota
# Neutron returns quota details in dict which is in format like:
# {'resource_name': {'in_use': X, 'limit': Y, 'reserved': Z},
# 'resource_name_2': {'in_use': X2, 'limit': Y2, 'reserved': Z2}}
#
# but Nova and Cinder returns quota in different format, like:
# {'resource_name': X,
# 'resource_name_2': X2,
# 'usage': {
# 'resource_name': Y,
# 'resource_name_2': Y2
# },
# 'reserved': {
# 'resource_name': Z,
# 'resource_name_2': Z2
# }}
#
# so we need to make conversion to have data in same format from
# all of the services
result = {"usage": {}, "reservation": {}}
for key, values in dict_quota.items():
if values is None:
continue
# NOTE(slaweq): Neutron returns values with key "used" but Nova for
# example returns same data with key "in_use" instead. Because of
# that we need to convert Neutron key to the same as is returned
# from Nova to make result more consistent
if isinstance(values, dict) and 'used' in values:
values['in_use'] = values.pop("used")
result[key] = values
if isinstance(values, dict):
result[key] = values['limit']
result["reservation"][key] = values['reserved']
result["usage"][key] = values['used']
return result
@ -756,6 +768,19 @@ and ``server-group-members`` output for a given quota class."""
)
info = {}
if parsed_args.usage:
info["reservation"] = compute_quota_info.pop("reservation", {})
info["reservation"].update(
volume_quota_info.pop("reservation", {})
)
info["reservation"].update(
network_quota_info.pop("reservation", {})
)
info["usage"] = compute_quota_info.pop("usage", {})
info["usage"].update(volume_quota_info.pop("usage", {}))
info["usage"].update(network_quota_info.pop("usage", {}))
info.update(compute_quota_info)
info.update(volume_quota_info)
info.update(network_quota_info)

View File

@ -250,6 +250,8 @@ class QuotaTests(base.TestCase):
row_headers = [str(r) for r in row.keys()]
self.assertEqual(sorted(expected_headers), sorted(row_headers))
resources.append(row['Resource'])
for header in expected_headers[1:]:
self.assertIsInstance(row[header], int)
# Ensure that returned quota has network quota...
self.assertIn("networks", resources)
# ...and compute quota

View File

@ -955,6 +955,23 @@ class TestQuotaSet(TestQuota):
class TestQuotaShow(TestQuota):
_network_quota_details = {
'floating_ips': {'limit': 0, 'reserved': 0, 'used': 0},
'health_monitors': {'limit': 0, 'reserved': 0, 'used': 0},
'l7_policies': {'limit': 0, 'reserved': 0, 'used': 0},
'listeners': {'limit': 0, 'reserved': 0, 'used': 0},
'load_balancers': {'limit': 0, 'reserved': 0, 'used': 0},
'networks': {'limit': 0, 'reserved': 0, 'used': 0},
'pools': {'limit': 0, 'reserved': 0, 'used': 0},
'ports': {'limit': 0, 'reserved': 0, 'used': 0},
'rbac_policies': {'limit': 0, 'reserved': 0, 'used': 0},
'routers': {'limit': 0, 'reserved': 0, 'used': 0},
'security_group_rules': {'limit': 0, 'reserved': 0, 'used': 0},
'security_groups': {'limit': 0, 'reserved': 0, 'used': 0},
'subnet_pools': {'limit': 0, 'reserved': 0, 'used': 0},
'subnets': {'limit': 0, 'reserved': 0, 'used': 0},
}
def setUp(self):
super().setUp()
@ -980,9 +997,15 @@ class TestQuotaShow(TestQuota):
self.default_volume_quotas
)
self.network_client.get_quota.return_value = (
sdk_fakes.generate_fake_resource(_network_quota_set.Quota)
)
def get_network_quota_mock(*args, **kwargs):
if kwargs.get("details"):
return sdk_fakes.generate_fake_resource(
_network_quota_set.QuotaDetails,
**self._network_quota_details,
)
return sdk_fakes.generate_fake_resource(_network_quota_set.Quota)
self.network_client.get_quota.side_effect = get_network_quota_mock
self.default_network_quotas = sdk_fakes.generate_fake_resource(
_network_quota_set.QuotaDefault
)