Skip to content

Commit 1d16eed

Browse files
Zuulopenstack-gerrit
authored andcommitted
Merge "Paginate over usage list to return all usages"
2 parents f5980f9 + c79de8a commit 1d16eed

3 files changed

Lines changed: 76 additions & 1 deletion

File tree

openstackclient/compute/v2/usage.py

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,47 @@
1515

1616
"""Usage action implementations"""
1717

18+
import collections
1819
import datetime
1920

21+
from novaclient import api_versions
2022
from osc_lib.command import command
2123
from osc_lib import utils
2224
import six
2325

2426
from openstackclient.i18n import _
2527

2628

29+
def _get_usage_marker(usage):
30+
marker = None
31+
if hasattr(usage, 'server_usages') and usage.server_usages:
32+
marker = usage.server_usages[-1]['instance_id']
33+
return marker
34+
35+
36+
def _get_usage_list_marker(usage_list):
37+
marker = None
38+
if usage_list:
39+
marker = _get_usage_marker(usage_list[-1])
40+
return marker
41+
42+
43+
def _merge_usage(usage, next_usage):
44+
usage.server_usages.extend(next_usage.server_usages)
45+
usage.total_hours += next_usage.total_hours
46+
usage.total_memory_mb_usage += next_usage.total_memory_mb_usage
47+
usage.total_vcpus_usage += next_usage.total_vcpus_usage
48+
usage.total_local_gb_usage += next_usage.total_local_gb_usage
49+
50+
51+
def _merge_usage_list(usages, next_usage_list):
52+
for next_usage in next_usage_list:
53+
if next_usage.tenant_id in usages:
54+
_merge_usage(usages[next_usage.tenant_id], next_usage)
55+
else:
56+
usages[next_usage.tenant_id] = next_usage
57+
58+
2759
class ListUsage(command.Lister):
2860
_description = _("List resource usage per project")
2961

@@ -83,7 +115,23 @@ def _format_project(project):
83115
else:
84116
end = now + datetime.timedelta(days=1)
85117

86-
usage_list = compute_client.usage.list(start, end, detailed=True)
118+
if compute_client.api_version < api_versions.APIVersion("2.40"):
119+
usage_list = compute_client.usage.list(start, end, detailed=True)
120+
else:
121+
# If the number of instances used to calculate the usage is greater
122+
# than CONF.api.max_limit, the usage will be split across multiple
123+
# requests and the responses will need to be merged back together.
124+
usages = collections.OrderedDict()
125+
usage_list = compute_client.usage.list(start, end, detailed=True)
126+
_merge_usage_list(usages, usage_list)
127+
marker = _get_usage_list_marker(usage_list)
128+
while marker:
129+
next_usage_list = compute_client.usage.list(
130+
start, end, detailed=True, marker=marker)
131+
marker = _get_usage_list_marker(next_usage_list)
132+
if marker:
133+
_merge_usage_list(usages, next_usage_list)
134+
usage_list = list(usages.values())
87135

88136
# Cache the project list
89137
project_cache = {}

openstackclient/tests/unit/compute/v2/fakes.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1304,6 +1304,7 @@ def create_one_usage(attrs=None):
13041304
'local_gb': 1,
13051305
'memory_mb': 512,
13061306
'name': 'usage-name-' + uuid.uuid4().hex,
1307+
'instance_id': uuid.uuid4().hex,
13071308
'state': 'active',
13081309
'uptime': 3600,
13091310
'vcpus': 1

openstackclient/tests/unit/compute/v2/test_usage.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import datetime
1515

1616
import mock
17+
from novaclient import api_versions
1718

1819
from openstackclient.compute.v2 import usage
1920
from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes
@@ -104,6 +105,31 @@ def test_usage_list_with_options(self):
104105
self.assertEqual(self.columns, columns)
105106
self.assertEqual(tuple(self.data), tuple(data))
106107

108+
def test_usage_list_with_pagination(self):
109+
arglist = []
110+
verifylist = [
111+
('start', None),
112+
('end', None),
113+
]
114+
115+
self.app.client_manager.compute.api_version = api_versions.APIVersion(
116+
'2.40')
117+
self.usage_mock.list.reset_mock()
118+
self.usage_mock.list.side_effect = [self.usages, []]
119+
120+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
121+
122+
columns, data = self.cmd.take_action(parsed_args)
123+
124+
self.projects_mock.list.assert_called_with()
125+
self.usage_mock.list.assert_has_calls([
126+
mock.call(mock.ANY, mock.ANY, detailed=True),
127+
mock.call(mock.ANY, mock.ANY, detailed=True,
128+
marker=self.usages[0]['server_usages'][0]['instance_id'])
129+
])
130+
self.assertEqual(self.columns, columns)
131+
self.assertEqual(tuple(self.data), tuple(data))
132+
107133

108134
class TestUsageShow(TestUsage):
109135

0 commit comments

Comments
 (0)