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>
This commit is contained in:
parent
03776d82e5
commit
af5e9d16e8
@ -17,7 +17,9 @@
|
|||||||
|
|
||||||
import collections
|
import collections
|
||||||
import datetime
|
import datetime
|
||||||
|
import functools
|
||||||
|
|
||||||
|
from cliff import columns as cliff_columns
|
||||||
from novaclient import api_versions
|
from novaclient import api_versions
|
||||||
from osc_lib.command import command
|
from osc_lib.command import command
|
||||||
from osc_lib import utils
|
from osc_lib import utils
|
||||||
@ -25,6 +27,57 @@ from osc_lib import utils
|
|||||||
from openstackclient.i18n import _
|
from openstackclient.i18n import _
|
||||||
|
|
||||||
|
|
||||||
|
# TODO(stephenfin): This exists in a couple of places and should be moved to a
|
||||||
|
# common module
|
||||||
|
class ProjectColumn(cliff_columns.FormattableColumn):
|
||||||
|
"""Formattable column for project column.
|
||||||
|
|
||||||
|
Unlike the parent FormattableColumn class, the initializer of the class
|
||||||
|
takes project_cache as the second argument.
|
||||||
|
``osc_lib.utils.get_item_properties`` instantiates ``FormattableColumn``
|
||||||
|
objects with a single parameter, the column value, so you need to pass a
|
||||||
|
partially initialized class like ``functools.partial(ProjectColumn,
|
||||||
|
project_cache)`` to use this.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, value, project_cache=None):
|
||||||
|
super().__init__(value)
|
||||||
|
self.project_cache = project_cache or {}
|
||||||
|
|
||||||
|
def human_readable(self):
|
||||||
|
project = self._value
|
||||||
|
if not project:
|
||||||
|
return ''
|
||||||
|
|
||||||
|
if project in self.project_cache.keys():
|
||||||
|
return self.project_cache[project].name
|
||||||
|
|
||||||
|
return project
|
||||||
|
|
||||||
|
|
||||||
|
class CountColumn(cliff_columns.FormattableColumn):
|
||||||
|
|
||||||
|
def human_readable(self):
|
||||||
|
return len(self._value)
|
||||||
|
|
||||||
|
|
||||||
|
class FloatColumn(cliff_columns.FormattableColumn):
|
||||||
|
|
||||||
|
def human_readable(self):
|
||||||
|
return float("%.2f" % self._value)
|
||||||
|
|
||||||
|
|
||||||
|
def _formatters(project_cache):
|
||||||
|
return {
|
||||||
|
'tenant_id': functools.partial(
|
||||||
|
ProjectColumn, project_cache=project_cache),
|
||||||
|
'server_usages': CountColumn,
|
||||||
|
'total_memory_mb_usage': FloatColumn,
|
||||||
|
'total_vcpus_usage': FloatColumn,
|
||||||
|
'total_local_gb_usage': FloatColumn,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def _get_usage_marker(usage):
|
def _get_usage_marker(usage):
|
||||||
marker = None
|
marker = None
|
||||||
if hasattr(usage, 'server_usages') and usage.server_usages:
|
if hasattr(usage, 'server_usages') and usage.server_usages:
|
||||||
@ -147,17 +200,15 @@ class ListUsage(command.Lister):
|
|||||||
"end": end.strftime(dateformat),
|
"end": end.strftime(dateformat),
|
||||||
})
|
})
|
||||||
|
|
||||||
return (column_headers,
|
return (
|
||||||
(utils.get_item_properties(
|
column_headers,
|
||||||
|
(
|
||||||
|
utils.get_item_properties(
|
||||||
s, columns,
|
s, columns,
|
||||||
formatters={
|
formatters=_formatters(project_cache),
|
||||||
'tenant_id': _format_project,
|
) for s in usage_list
|
||||||
'server_usages': lambda x: len(x),
|
),
|
||||||
'total_memory_mb_usage': lambda x: float("%.2f" % x),
|
)
|
||||||
'total_vcpus_usage': lambda x: float("%.2f" % x),
|
|
||||||
'total_local_gb_usage': lambda x: float("%.2f" % x),
|
|
||||||
},
|
|
||||||
) for s in usage_list))
|
|
||||||
|
|
||||||
|
|
||||||
class ShowUsage(command.ShowOne):
|
class ShowUsage(command.ShowOne):
|
||||||
@ -222,17 +273,21 @@ class ShowUsage(command.ShowOne):
|
|||||||
"project": project,
|
"project": project,
|
||||||
})
|
})
|
||||||
|
|
||||||
info = {}
|
columns = (
|
||||||
info['Servers'] = (
|
"tenant_id",
|
||||||
len(usage.server_usages)
|
"server_usages",
|
||||||
if hasattr(usage, "server_usages") else None)
|
"total_memory_mb_usage",
|
||||||
info['RAM MB-Hours'] = (
|
"total_vcpus_usage",
|
||||||
float("%.2f" % usage.total_memory_mb_usage)
|
"total_local_gb_usage"
|
||||||
if hasattr(usage, "total_memory_mb_usage") else None)
|
)
|
||||||
info['CPU Hours'] = (
|
column_headers = (
|
||||||
float("%.2f" % usage.total_vcpus_usage)
|
"Project",
|
||||||
if hasattr(usage, "total_vcpus_usage") else None)
|
"Servers",
|
||||||
info['Disk GB-Hours'] = (
|
"RAM MB-Hours",
|
||||||
float("%.2f" % usage.total_local_gb_usage)
|
"CPU Hours",
|
||||||
if hasattr(usage, "total_local_gb_usage") else None)
|
"Disk GB-Hours"
|
||||||
return zip(*sorted(info.items()))
|
)
|
||||||
|
|
||||||
|
data = utils.get_item_properties(
|
||||||
|
usage, columns, formatters=_formatters(None))
|
||||||
|
return column_headers, data
|
||||||
|
@ -16,7 +16,7 @@ from unittest import mock
|
|||||||
|
|
||||||
from novaclient import api_versions
|
from novaclient import api_versions
|
||||||
|
|
||||||
from openstackclient.compute.v2 import usage
|
from openstackclient.compute.v2 import usage as usage_cmds
|
||||||
from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes
|
from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes
|
||||||
from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes
|
from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes
|
||||||
|
|
||||||
@ -49,11 +49,11 @@ class TestUsageList(TestUsage):
|
|||||||
)
|
)
|
||||||
|
|
||||||
data = [(
|
data = [(
|
||||||
usages[0].tenant_id,
|
usage_cmds.ProjectColumn(usages[0].tenant_id),
|
||||||
len(usages[0].server_usages),
|
usage_cmds.CountColumn(usages[0].server_usages),
|
||||||
float("%.2f" % usages[0].total_memory_mb_usage),
|
usage_cmds.FloatColumn(usages[0].total_memory_mb_usage),
|
||||||
float("%.2f" % usages[0].total_vcpus_usage),
|
usage_cmds.FloatColumn(usages[0].total_vcpus_usage),
|
||||||
float("%.2f" % usages[0].total_local_gb_usage),
|
usage_cmds.FloatColumn(usages[0].total_local_gb_usage),
|
||||||
)]
|
)]
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
@ -63,7 +63,7 @@ class TestUsageList(TestUsage):
|
|||||||
|
|
||||||
self.projects_mock.list.return_value = [self.project]
|
self.projects_mock.list.return_value = [self.project]
|
||||||
# Get the command object to test
|
# Get the command object to test
|
||||||
self.cmd = usage.ListUsage(self.app, None)
|
self.cmd = usage_cmds.ListUsage(self.app, None)
|
||||||
|
|
||||||
def test_usage_list_no_options(self):
|
def test_usage_list_no_options(self):
|
||||||
|
|
||||||
@ -79,8 +79,8 @@ class TestUsageList(TestUsage):
|
|||||||
|
|
||||||
self.projects_mock.list.assert_called_with()
|
self.projects_mock.list.assert_called_with()
|
||||||
|
|
||||||
self.assertEqual(self.columns, columns)
|
self.assertCountEqual(self.columns, columns)
|
||||||
self.assertEqual(tuple(self.data), tuple(data))
|
self.assertCountEqual(tuple(self.data), tuple(data))
|
||||||
|
|
||||||
def test_usage_list_with_options(self):
|
def test_usage_list_with_options(self):
|
||||||
arglist = [
|
arglist = [
|
||||||
@ -102,8 +102,8 @@ class TestUsageList(TestUsage):
|
|||||||
datetime.datetime(2016, 12, 20, 0, 0),
|
datetime.datetime(2016, 12, 20, 0, 0),
|
||||||
detailed=True)
|
detailed=True)
|
||||||
|
|
||||||
self.assertEqual(self.columns, columns)
|
self.assertCountEqual(self.columns, columns)
|
||||||
self.assertEqual(tuple(self.data), tuple(data))
|
self.assertCountEqual(tuple(self.data), tuple(data))
|
||||||
|
|
||||||
def test_usage_list_with_pagination(self):
|
def test_usage_list_with_pagination(self):
|
||||||
arglist = []
|
arglist = []
|
||||||
@ -127,8 +127,8 @@ class TestUsageList(TestUsage):
|
|||||||
mock.call(mock.ANY, mock.ANY, detailed=True,
|
mock.call(mock.ANY, mock.ANY, detailed=True,
|
||||||
marker=self.usages[0]['server_usages'][0]['instance_id'])
|
marker=self.usages[0]['server_usages'][0]['instance_id'])
|
||||||
])
|
])
|
||||||
self.assertEqual(self.columns, columns)
|
self.assertCountEqual(self.columns, columns)
|
||||||
self.assertEqual(tuple(self.data), tuple(data))
|
self.assertCountEqual(tuple(self.data), tuple(data))
|
||||||
|
|
||||||
|
|
||||||
class TestUsageShow(TestUsage):
|
class TestUsageShow(TestUsage):
|
||||||
@ -139,17 +139,19 @@ class TestUsageShow(TestUsage):
|
|||||||
attrs={'tenant_id': project.name})
|
attrs={'tenant_id': project.name})
|
||||||
|
|
||||||
columns = (
|
columns = (
|
||||||
|
'Project',
|
||||||
|
'Servers',
|
||||||
|
'RAM MB-Hours',
|
||||||
'CPU Hours',
|
'CPU Hours',
|
||||||
'Disk GB-Hours',
|
'Disk GB-Hours',
|
||||||
'RAM MB-Hours',
|
|
||||||
'Servers',
|
|
||||||
)
|
)
|
||||||
|
|
||||||
data = (
|
data = (
|
||||||
float("%.2f" % usage.total_vcpus_usage),
|
usage_cmds.ProjectColumn(usage.tenant_id),
|
||||||
float("%.2f" % usage.total_local_gb_usage),
|
usage_cmds.CountColumn(usage.server_usages),
|
||||||
float("%.2f" % usage.total_memory_mb_usage),
|
usage_cmds.FloatColumn(usage.total_memory_mb_usage),
|
||||||
len(usage.server_usages),
|
usage_cmds.FloatColumn(usage.total_vcpus_usage),
|
||||||
|
usage_cmds.FloatColumn(usage.total_local_gb_usage),
|
||||||
)
|
)
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
@ -159,7 +161,7 @@ class TestUsageShow(TestUsage):
|
|||||||
|
|
||||||
self.projects_mock.get.return_value = self.project
|
self.projects_mock.get.return_value = self.project
|
||||||
# Get the command object to test
|
# Get the command object to test
|
||||||
self.cmd = usage.ShowUsage(self.app, None)
|
self.cmd = usage_cmds.ShowUsage(self.app, None)
|
||||||
|
|
||||||
def test_usage_show_no_options(self):
|
def test_usage_show_no_options(self):
|
||||||
|
|
||||||
|
@ -8,3 +8,9 @@ fixes:
|
|||||||
will now be rendered as objects. In addition, the ``power_state`` field
|
will now be rendered as objects. In addition, the ``power_state`` field
|
||||||
will now be humanized and rendered as a string value when using the table
|
will now be humanized and rendered as a string value when using the table
|
||||||
formatter.
|
formatter.
|
||||||
|
- |
|
||||||
|
The ``usage list`` and ``usage show`` commands will now display the name
|
||||||
|
of the project being queried rather than the ID when using the table
|
||||||
|
formatter. In addition, the ``server_usages``, ``total_memory_mb_usage``,
|
||||||
|
``total_vcpus_usage`` and ``total_local_gb_usage`` values will only be
|
||||||
|
humanized when using the table formatter.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user