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, default=False,
): ):
def _network_quota_to_dict(network_quota, detail=False): def _network_quota_to_dict(network_quota, detail=False):
if not isinstance(network_quota, dict): dict_quota = network_quota.to_dict(computed=False)
dict_quota = network_quota.to_dict()
else:
dict_quota = network_quota
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(): for key, values in dict_quota.items():
if values is None: if values is None:
continue continue
if isinstance(values, dict):
# NOTE(slaweq): Neutron returns values with key "used" but Nova for result[key] = values['limit']
# example returns same data with key "in_use" instead. Because of result["reservation"][key] = values['reserved']
# that we need to convert Neutron key to the same as is returned result["usage"][key] = values['used']
# from Nova to make result more consistent
if isinstance(values, dict) and 'used' in values:
values['in_use'] = values.pop("used")
result[key] = values
return result return result
@ -756,6 +768,19 @@ and ``server-group-members`` output for a given quota class."""
) )
info = {} 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(compute_quota_info)
info.update(volume_quota_info) info.update(volume_quota_info)
info.update(network_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()] row_headers = [str(r) for r in row.keys()]
self.assertEqual(sorted(expected_headers), sorted(row_headers)) self.assertEqual(sorted(expected_headers), sorted(row_headers))
resources.append(row['Resource']) resources.append(row['Resource'])
for header in expected_headers[1:]:
self.assertIsInstance(row[header], int)
# Ensure that returned quota has network quota... # Ensure that returned quota has network quota...
self.assertIn("networks", resources) self.assertIn("networks", resources)
# ...and compute quota # ...and compute quota

View File

@ -955,6 +955,23 @@ class TestQuotaSet(TestQuota):
class TestQuotaShow(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): def setUp(self):
super().setUp() super().setUp()
@ -980,9 +997,15 @@ class TestQuotaShow(TestQuota):
self.default_volume_quotas self.default_volume_quotas
) )
self.network_client.get_quota.return_value = ( def get_network_quota_mock(*args, **kwargs):
sdk_fakes.generate_fake_resource(_network_quota_set.Quota) 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( self.default_network_quotas = sdk_fakes.generate_fake_resource(
_network_quota_set.QuotaDefault _network_quota_set.QuotaDefault
) )