Skip to content

Commit af5e9d1

Browse files
committed
compute: Fix 'usage * -f yaml' output
Make use of 'FormattableColumn'-derived formatters, which provide better output than what we were using before, particularly for the YAML output format. Change-Id: Ic770f27cb1f74222636f05350f97400808adffbf Signed-off-by: Stephen Finucane <sfinucan@redhat.com>
1 parent 03776d8 commit af5e9d1

3 files changed

Lines changed: 107 additions & 44 deletions

File tree

openstackclient/compute/v2/usage.py

Lines changed: 79 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,67 @@
1717

1818
import collections
1919
import datetime
20+
import functools
2021

22+
from cliff import columns as cliff_columns
2123
from novaclient import api_versions
2224
from osc_lib.command import command
2325
from osc_lib import utils
2426

2527
from openstackclient.i18n import _
2628

2729

30+
# TODO(stephenfin): This exists in a couple of places and should be moved to a
31+
# common module
32+
class ProjectColumn(cliff_columns.FormattableColumn):
33+
"""Formattable column for project column.
34+
35+
Unlike the parent FormattableColumn class, the initializer of the class
36+
takes project_cache as the second argument.
37+
``osc_lib.utils.get_item_properties`` instantiates ``FormattableColumn``
38+
objects with a single parameter, the column value, so you need to pass a
39+
partially initialized class like ``functools.partial(ProjectColumn,
40+
project_cache)`` to use this.
41+
"""
42+
43+
def __init__(self, value, project_cache=None):
44+
super().__init__(value)
45+
self.project_cache = project_cache or {}
46+
47+
def human_readable(self):
48+
project = self._value
49+
if not project:
50+
return ''
51+
52+
if project in self.project_cache.keys():
53+
return self.project_cache[project].name
54+
55+
return project
56+
57+
58+
class CountColumn(cliff_columns.FormattableColumn):
59+
60+
def human_readable(self):
61+
return len(self._value)
62+
63+
64+
class FloatColumn(cliff_columns.FormattableColumn):
65+
66+
def human_readable(self):
67+
return float("%.2f" % self._value)
68+
69+
70+
def _formatters(project_cache):
71+
return {
72+
'tenant_id': functools.partial(
73+
ProjectColumn, project_cache=project_cache),
74+
'server_usages': CountColumn,
75+
'total_memory_mb_usage': FloatColumn,
76+
'total_vcpus_usage': FloatColumn,
77+
'total_local_gb_usage': FloatColumn,
78+
}
79+
80+
2881
def _get_usage_marker(usage):
2982
marker = None
3083
if hasattr(usage, 'server_usages') and usage.server_usages:
@@ -147,17 +200,15 @@ def _format_project(project):
147200
"end": end.strftime(dateformat),
148201
})
149202

150-
return (column_headers,
151-
(utils.get_item_properties(
203+
return (
204+
column_headers,
205+
(
206+
utils.get_item_properties(
152207
s, columns,
153-
formatters={
154-
'tenant_id': _format_project,
155-
'server_usages': lambda x: len(x),
156-
'total_memory_mb_usage': lambda x: float("%.2f" % x),
157-
'total_vcpus_usage': lambda x: float("%.2f" % x),
158-
'total_local_gb_usage': lambda x: float("%.2f" % x),
159-
},
160-
) for s in usage_list))
208+
formatters=_formatters(project_cache),
209+
) for s in usage_list
210+
),
211+
)
161212

162213

163214
class ShowUsage(command.ShowOne):
@@ -222,17 +273,21 @@ def take_action(self, parsed_args):
222273
"project": project,
223274
})
224275

225-
info = {}
226-
info['Servers'] = (
227-
len(usage.server_usages)
228-
if hasattr(usage, "server_usages") else None)
229-
info['RAM MB-Hours'] = (
230-
float("%.2f" % usage.total_memory_mb_usage)
231-
if hasattr(usage, "total_memory_mb_usage") else None)
232-
info['CPU Hours'] = (
233-
float("%.2f" % usage.total_vcpus_usage)
234-
if hasattr(usage, "total_vcpus_usage") else None)
235-
info['Disk GB-Hours'] = (
236-
float("%.2f" % usage.total_local_gb_usage)
237-
if hasattr(usage, "total_local_gb_usage") else None)
238-
return zip(*sorted(info.items()))
276+
columns = (
277+
"tenant_id",
278+
"server_usages",
279+
"total_memory_mb_usage",
280+
"total_vcpus_usage",
281+
"total_local_gb_usage"
282+
)
283+
column_headers = (
284+
"Project",
285+
"Servers",
286+
"RAM MB-Hours",
287+
"CPU Hours",
288+
"Disk GB-Hours"
289+
)
290+
291+
data = utils.get_item_properties(
292+
usage, columns, formatters=_formatters(None))
293+
return column_headers, data

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

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
from novaclient import api_versions
1818

19-
from openstackclient.compute.v2 import usage
19+
from openstackclient.compute.v2 import usage as usage_cmds
2020
from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes
2121
from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes
2222

@@ -49,11 +49,11 @@ class TestUsageList(TestUsage):
4949
)
5050

5151
data = [(
52-
usages[0].tenant_id,
53-
len(usages[0].server_usages),
54-
float("%.2f" % usages[0].total_memory_mb_usage),
55-
float("%.2f" % usages[0].total_vcpus_usage),
56-
float("%.2f" % usages[0].total_local_gb_usage),
52+
usage_cmds.ProjectColumn(usages[0].tenant_id),
53+
usage_cmds.CountColumn(usages[0].server_usages),
54+
usage_cmds.FloatColumn(usages[0].total_memory_mb_usage),
55+
usage_cmds.FloatColumn(usages[0].total_vcpus_usage),
56+
usage_cmds.FloatColumn(usages[0].total_local_gb_usage),
5757
)]
5858

5959
def setUp(self):
@@ -63,7 +63,7 @@ def setUp(self):
6363

6464
self.projects_mock.list.return_value = [self.project]
6565
# Get the command object to test
66-
self.cmd = usage.ListUsage(self.app, None)
66+
self.cmd = usage_cmds.ListUsage(self.app, None)
6767

6868
def test_usage_list_no_options(self):
6969

@@ -79,8 +79,8 @@ def test_usage_list_no_options(self):
7979

8080
self.projects_mock.list.assert_called_with()
8181

82-
self.assertEqual(self.columns, columns)
83-
self.assertEqual(tuple(self.data), tuple(data))
82+
self.assertCountEqual(self.columns, columns)
83+
self.assertCountEqual(tuple(self.data), tuple(data))
8484

8585
def test_usage_list_with_options(self):
8686
arglist = [
@@ -102,8 +102,8 @@ def test_usage_list_with_options(self):
102102
datetime.datetime(2016, 12, 20, 0, 0),
103103
detailed=True)
104104

105-
self.assertEqual(self.columns, columns)
106-
self.assertEqual(tuple(self.data), tuple(data))
105+
self.assertCountEqual(self.columns, columns)
106+
self.assertCountEqual(tuple(self.data), tuple(data))
107107

108108
def test_usage_list_with_pagination(self):
109109
arglist = []
@@ -127,8 +127,8 @@ def test_usage_list_with_pagination(self):
127127
mock.call(mock.ANY, mock.ANY, detailed=True,
128128
marker=self.usages[0]['server_usages'][0]['instance_id'])
129129
])
130-
self.assertEqual(self.columns, columns)
131-
self.assertEqual(tuple(self.data), tuple(data))
130+
self.assertCountEqual(self.columns, columns)
131+
self.assertCountEqual(tuple(self.data), tuple(data))
132132

133133

134134
class TestUsageShow(TestUsage):
@@ -139,17 +139,19 @@ class TestUsageShow(TestUsage):
139139
attrs={'tenant_id': project.name})
140140

141141
columns = (
142+
'Project',
143+
'Servers',
144+
'RAM MB-Hours',
142145
'CPU Hours',
143146
'Disk GB-Hours',
144-
'RAM MB-Hours',
145-
'Servers',
146147
)
147148

148149
data = (
149-
float("%.2f" % usage.total_vcpus_usage),
150-
float("%.2f" % usage.total_local_gb_usage),
151-
float("%.2f" % usage.total_memory_mb_usage),
152-
len(usage.server_usages),
150+
usage_cmds.ProjectColumn(usage.tenant_id),
151+
usage_cmds.CountColumn(usage.server_usages),
152+
usage_cmds.FloatColumn(usage.total_memory_mb_usage),
153+
usage_cmds.FloatColumn(usage.total_vcpus_usage),
154+
usage_cmds.FloatColumn(usage.total_local_gb_usage),
153155
)
154156

155157
def setUp(self):
@@ -159,7 +161,7 @@ def setUp(self):
159161

160162
self.projects_mock.get.return_value = self.project
161163
# Get the command object to test
162-
self.cmd = usage.ShowUsage(self.app, None)
164+
self.cmd = usage_cmds.ShowUsage(self.app, None)
163165

164166
def test_usage_show_no_options(self):
165167

releasenotes/notes/improved-server-output-6965b664f6abda8d.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,9 @@ fixes:
88
will now be rendered as objects. In addition, the ``power_state`` field
99
will now be humanized and rendered as a string value when using the table
1010
formatter.
11+
- |
12+
The ``usage list`` and ``usage show`` commands will now display the name
13+
of the project being queried rather than the ID when using the table
14+
formatter. In addition, the ``server_usages``, ``total_memory_mb_usage``,
15+
``total_vcpus_usage`` and ``total_local_gb_usage`` values will only be
16+
humanized when using the table formatter.

0 commit comments

Comments
 (0)