Use the compute SDK in usage commands
Update usage list and usage show to use the compute component of the OpenStack SDK instead of directly using the nova interface. Change-Id: I1c4d2247c9c1a577ed9efad7e8332e7c9b974ad5
This commit is contained in:
parent
ccd9356550
commit
ce4cbeab67
@ -15,12 +15,10 @@
|
|||||||
|
|
||||||
"""Usage action implementations"""
|
"""Usage action implementations"""
|
||||||
|
|
||||||
import collections
|
|
||||||
import datetime
|
import datetime
|
||||||
import functools
|
import functools
|
||||||
|
|
||||||
from cliff import columns as cliff_columns
|
from cliff import columns as cliff_columns
|
||||||
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
|
||||||
|
|
||||||
@ -58,7 +56,7 @@ class ProjectColumn(cliff_columns.FormattableColumn):
|
|||||||
class CountColumn(cliff_columns.FormattableColumn):
|
class CountColumn(cliff_columns.FormattableColumn):
|
||||||
|
|
||||||
def human_readable(self):
|
def human_readable(self):
|
||||||
return len(self._value)
|
return len(self._value) if self._value is not None else None
|
||||||
|
|
||||||
|
|
||||||
class FloatColumn(cliff_columns.FormattableColumn):
|
class FloatColumn(cliff_columns.FormattableColumn):
|
||||||
@ -69,7 +67,7 @@ class FloatColumn(cliff_columns.FormattableColumn):
|
|||||||
|
|
||||||
def _formatters(project_cache):
|
def _formatters(project_cache):
|
||||||
return {
|
return {
|
||||||
'tenant_id': functools.partial(
|
'project_id': functools.partial(
|
||||||
ProjectColumn, project_cache=project_cache),
|
ProjectColumn, project_cache=project_cache),
|
||||||
'server_usages': CountColumn,
|
'server_usages': CountColumn,
|
||||||
'total_memory_mb_usage': FloatColumn,
|
'total_memory_mb_usage': FloatColumn,
|
||||||
@ -102,10 +100,10 @@ def _merge_usage(usage, next_usage):
|
|||||||
|
|
||||||
def _merge_usage_list(usages, next_usage_list):
|
def _merge_usage_list(usages, next_usage_list):
|
||||||
for next_usage in next_usage_list:
|
for next_usage in next_usage_list:
|
||||||
if next_usage.tenant_id in usages:
|
if next_usage.project_id in usages:
|
||||||
_merge_usage(usages[next_usage.tenant_id], next_usage)
|
_merge_usage(usages[next_usage.project_id], next_usage)
|
||||||
else:
|
else:
|
||||||
usages[next_usage.tenant_id] = next_usage
|
usages[next_usage.project_id] = next_usage
|
||||||
|
|
||||||
|
|
||||||
class ListUsage(command.Lister):
|
class ListUsage(command.Lister):
|
||||||
@ -138,9 +136,9 @@ class ListUsage(command.Lister):
|
|||||||
else:
|
else:
|
||||||
return project
|
return project
|
||||||
|
|
||||||
compute_client = self.app.client_manager.compute
|
compute_client = self.app.client_manager.sdk_connection.compute
|
||||||
columns = (
|
columns = (
|
||||||
"tenant_id",
|
"project_id",
|
||||||
"server_usages",
|
"server_usages",
|
||||||
"total_memory_mb_usage",
|
"total_memory_mb_usage",
|
||||||
"total_vcpus_usage",
|
"total_vcpus_usage",
|
||||||
@ -154,36 +152,25 @@ class ListUsage(command.Lister):
|
|||||||
"Disk GB-Hours"
|
"Disk GB-Hours"
|
||||||
)
|
)
|
||||||
|
|
||||||
dateformat = "%Y-%m-%d"
|
date_cli_format = "%Y-%m-%d"
|
||||||
|
date_api_format = "%Y-%m-%dT%H:%M:%S"
|
||||||
now = datetime.datetime.utcnow()
|
now = datetime.datetime.utcnow()
|
||||||
|
|
||||||
if parsed_args.start:
|
if parsed_args.start:
|
||||||
start = datetime.datetime.strptime(parsed_args.start, dateformat)
|
start = datetime.datetime.strptime(
|
||||||
|
parsed_args.start, date_cli_format)
|
||||||
else:
|
else:
|
||||||
start = now - datetime.timedelta(weeks=4)
|
start = now - datetime.timedelta(weeks=4)
|
||||||
|
|
||||||
if parsed_args.end:
|
if parsed_args.end:
|
||||||
end = datetime.datetime.strptime(parsed_args.end, dateformat)
|
end = datetime.datetime.strptime(parsed_args.end, date_cli_format)
|
||||||
else:
|
else:
|
||||||
end = now + datetime.timedelta(days=1)
|
end = now + datetime.timedelta(days=1)
|
||||||
|
|
||||||
if compute_client.api_version < api_versions.APIVersion("2.40"):
|
usage_list = list(compute_client.usages(
|
||||||
usage_list = compute_client.usage.list(start, end, detailed=True)
|
start=start.strftime(date_api_format),
|
||||||
else:
|
end=end.strftime(date_api_format),
|
||||||
# If the number of instances used to calculate the usage is greater
|
detailed=True))
|
||||||
# than CONF.api.max_limit, the usage will be split across multiple
|
|
||||||
# requests and the responses will need to be merged back together.
|
|
||||||
usages = collections.OrderedDict()
|
|
||||||
usage_list = compute_client.usage.list(start, end, detailed=True)
|
|
||||||
_merge_usage_list(usages, usage_list)
|
|
||||||
marker = _get_usage_list_marker(usage_list)
|
|
||||||
while marker:
|
|
||||||
next_usage_list = compute_client.usage.list(
|
|
||||||
start, end, detailed=True, marker=marker)
|
|
||||||
marker = _get_usage_list_marker(next_usage_list)
|
|
||||||
if marker:
|
|
||||||
_merge_usage_list(usages, next_usage_list)
|
|
||||||
usage_list = list(usages.values())
|
|
||||||
|
|
||||||
# Cache the project list
|
# Cache the project list
|
||||||
project_cache = {}
|
project_cache = {}
|
||||||
@ -196,8 +183,8 @@ class ListUsage(command.Lister):
|
|||||||
|
|
||||||
if parsed_args.formatter == 'table' and len(usage_list) > 0:
|
if parsed_args.formatter == 'table' and len(usage_list) > 0:
|
||||||
self.app.stdout.write(_("Usage from %(start)s to %(end)s: \n") % {
|
self.app.stdout.write(_("Usage from %(start)s to %(end)s: \n") % {
|
||||||
"start": start.strftime(dateformat),
|
"start": start.strftime(date_cli_format),
|
||||||
"end": end.strftime(dateformat),
|
"end": end.strftime(date_cli_format),
|
||||||
})
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -239,17 +226,19 @@ class ShowUsage(command.ShowOne):
|
|||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
identity_client = self.app.client_manager.identity
|
identity_client = self.app.client_manager.identity
|
||||||
compute_client = self.app.client_manager.compute
|
compute_client = self.app.client_manager.sdk_connection.compute
|
||||||
dateformat = "%Y-%m-%d"
|
date_cli_format = "%Y-%m-%d"
|
||||||
|
date_api_format = "%Y-%m-%dT%H:%M:%S"
|
||||||
now = datetime.datetime.utcnow()
|
now = datetime.datetime.utcnow()
|
||||||
|
|
||||||
if parsed_args.start:
|
if parsed_args.start:
|
||||||
start = datetime.datetime.strptime(parsed_args.start, dateformat)
|
start = datetime.datetime.strptime(
|
||||||
|
parsed_args.start, date_cli_format)
|
||||||
else:
|
else:
|
||||||
start = now - datetime.timedelta(weeks=4)
|
start = now - datetime.timedelta(weeks=4)
|
||||||
|
|
||||||
if parsed_args.end:
|
if parsed_args.end:
|
||||||
end = datetime.datetime.strptime(parsed_args.end, dateformat)
|
end = datetime.datetime.strptime(parsed_args.end, date_cli_format)
|
||||||
else:
|
else:
|
||||||
end = now + datetime.timedelta(days=1)
|
end = now + datetime.timedelta(days=1)
|
||||||
|
|
||||||
@ -262,19 +251,21 @@ class ShowUsage(command.ShowOne):
|
|||||||
# Get the project from the current auth
|
# Get the project from the current auth
|
||||||
project = self.app.client_manager.auth_ref.project_id
|
project = self.app.client_manager.auth_ref.project_id
|
||||||
|
|
||||||
usage = compute_client.usage.get(project, start, end)
|
usage = compute_client.get_usage(
|
||||||
|
project=project, start=start.strftime(date_api_format),
|
||||||
|
end=end.strftime(date_api_format))
|
||||||
|
|
||||||
if parsed_args.formatter == 'table':
|
if parsed_args.formatter == 'table':
|
||||||
self.app.stdout.write(_(
|
self.app.stdout.write(_(
|
||||||
"Usage from %(start)s to %(end)s on project %(project)s: \n"
|
"Usage from %(start)s to %(end)s on project %(project)s: \n"
|
||||||
) % {
|
) % {
|
||||||
"start": start.strftime(dateformat),
|
"start": start.strftime(date_cli_format),
|
||||||
"end": end.strftime(dateformat),
|
"end": end.strftime(date_cli_format),
|
||||||
"project": project,
|
"project": project,
|
||||||
})
|
})
|
||||||
|
|
||||||
columns = (
|
columns = (
|
||||||
"tenant_id",
|
"project_id",
|
||||||
"server_usages",
|
"server_usages",
|
||||||
"total_memory_mb_usage",
|
"total_memory_mb_usage",
|
||||||
"total_vcpus_usage",
|
"total_vcpus_usage",
|
||||||
|
@ -11,11 +11,8 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
#
|
#
|
||||||
|
|
||||||
import datetime
|
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
|
||||||
from novaclient import api_versions
|
|
||||||
|
|
||||||
from openstackclient.compute.v2 import usage as usage_cmds
|
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
|
||||||
@ -26,8 +23,9 @@ class TestUsage(compute_fakes.TestComputev2):
|
|||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestUsage, self).setUp()
|
super(TestUsage, self).setUp()
|
||||||
|
|
||||||
self.usage_mock = self.app.client_manager.compute.usage
|
self.app.client_manager.sdk_connection = mock.Mock()
|
||||||
self.usage_mock.reset_mock()
|
self.app.client_manager.sdk_connection.compute = mock.Mock()
|
||||||
|
self.sdk_client = self.app.client_manager.sdk_connection.compute
|
||||||
|
|
||||||
self.projects_mock = self.app.client_manager.identity.projects
|
self.projects_mock = self.app.client_manager.identity.projects
|
||||||
self.projects_mock.reset_mock()
|
self.projects_mock.reset_mock()
|
||||||
@ -38,7 +36,7 @@ class TestUsageList(TestUsage):
|
|||||||
project = identity_fakes.FakeProject.create_one_project()
|
project = identity_fakes.FakeProject.create_one_project()
|
||||||
# Return value of self.usage_mock.list().
|
# Return value of self.usage_mock.list().
|
||||||
usages = compute_fakes.FakeUsage.create_usages(
|
usages = compute_fakes.FakeUsage.create_usages(
|
||||||
attrs={'tenant_id': project.name}, count=1)
|
attrs={'project_id': project.name}, count=1)
|
||||||
|
|
||||||
columns = (
|
columns = (
|
||||||
"Project",
|
"Project",
|
||||||
@ -49,7 +47,7 @@ class TestUsageList(TestUsage):
|
|||||||
)
|
)
|
||||||
|
|
||||||
data = [(
|
data = [(
|
||||||
usage_cmds.ProjectColumn(usages[0].tenant_id),
|
usage_cmds.ProjectColumn(usages[0].project_id),
|
||||||
usage_cmds.CountColumn(usages[0].server_usages),
|
usage_cmds.CountColumn(usages[0].server_usages),
|
||||||
usage_cmds.FloatColumn(usages[0].total_memory_mb_usage),
|
usage_cmds.FloatColumn(usages[0].total_memory_mb_usage),
|
||||||
usage_cmds.FloatColumn(usages[0].total_vcpus_usage),
|
usage_cmds.FloatColumn(usages[0].total_vcpus_usage),
|
||||||
@ -59,7 +57,7 @@ class TestUsageList(TestUsage):
|
|||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestUsageList, self).setUp()
|
super(TestUsageList, self).setUp()
|
||||||
|
|
||||||
self.usage_mock.list.return_value = self.usages
|
self.sdk_client.usages.return_value = self.usages
|
||||||
|
|
||||||
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
|
||||||
@ -97,9 +95,9 @@ class TestUsageList(TestUsage):
|
|||||||
columns, data = self.cmd.take_action(parsed_args)
|
columns, data = self.cmd.take_action(parsed_args)
|
||||||
|
|
||||||
self.projects_mock.list.assert_called_with()
|
self.projects_mock.list.assert_called_with()
|
||||||
self.usage_mock.list.assert_called_with(
|
self.sdk_client.usages.assert_called_with(
|
||||||
datetime.datetime(2016, 11, 11, 0, 0),
|
start='2016-11-11T00:00:00',
|
||||||
datetime.datetime(2016, 12, 20, 0, 0),
|
end='2016-12-20T00:00:00',
|
||||||
detailed=True)
|
detailed=True)
|
||||||
|
|
||||||
self.assertCountEqual(self.columns, columns)
|
self.assertCountEqual(self.columns, columns)
|
||||||
@ -112,20 +110,13 @@ class TestUsageList(TestUsage):
|
|||||||
('end', None),
|
('end', None),
|
||||||
]
|
]
|
||||||
|
|
||||||
self.app.client_manager.compute.api_version = api_versions.APIVersion(
|
|
||||||
'2.40')
|
|
||||||
self.usage_mock.list.reset_mock()
|
|
||||||
self.usage_mock.list.side_effect = [self.usages, []]
|
|
||||||
|
|
||||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||||
|
|
||||||
columns, data = self.cmd.take_action(parsed_args)
|
columns, data = self.cmd.take_action(parsed_args)
|
||||||
|
|
||||||
self.projects_mock.list.assert_called_with()
|
self.projects_mock.list.assert_called_with()
|
||||||
self.usage_mock.list.assert_has_calls([
|
self.sdk_client.usages.assert_has_calls([
|
||||||
mock.call(mock.ANY, mock.ANY, detailed=True),
|
mock.call(start=mock.ANY, end=mock.ANY, detailed=True)
|
||||||
mock.call(mock.ANY, mock.ANY, detailed=True,
|
|
||||||
marker=self.usages[0]['server_usages'][0]['instance_id'])
|
|
||||||
])
|
])
|
||||||
self.assertCountEqual(self.columns, columns)
|
self.assertCountEqual(self.columns, columns)
|
||||||
self.assertCountEqual(tuple(self.data), tuple(data))
|
self.assertCountEqual(tuple(self.data), tuple(data))
|
||||||
@ -136,7 +127,7 @@ class TestUsageShow(TestUsage):
|
|||||||
project = identity_fakes.FakeProject.create_one_project()
|
project = identity_fakes.FakeProject.create_one_project()
|
||||||
# Return value of self.usage_mock.list().
|
# Return value of self.usage_mock.list().
|
||||||
usage = compute_fakes.FakeUsage.create_one_usage(
|
usage = compute_fakes.FakeUsage.create_one_usage(
|
||||||
attrs={'tenant_id': project.name})
|
attrs={'project_id': project.name})
|
||||||
|
|
||||||
columns = (
|
columns = (
|
||||||
'Project',
|
'Project',
|
||||||
@ -147,7 +138,7 @@ class TestUsageShow(TestUsage):
|
|||||||
)
|
)
|
||||||
|
|
||||||
data = (
|
data = (
|
||||||
usage_cmds.ProjectColumn(usage.tenant_id),
|
usage_cmds.ProjectColumn(usage.project_id),
|
||||||
usage_cmds.CountColumn(usage.server_usages),
|
usage_cmds.CountColumn(usage.server_usages),
|
||||||
usage_cmds.FloatColumn(usage.total_memory_mb_usage),
|
usage_cmds.FloatColumn(usage.total_memory_mb_usage),
|
||||||
usage_cmds.FloatColumn(usage.total_vcpus_usage),
|
usage_cmds.FloatColumn(usage.total_vcpus_usage),
|
||||||
@ -157,7 +148,7 @@ class TestUsageShow(TestUsage):
|
|||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestUsageShow, self).setUp()
|
super(TestUsageShow, self).setUp()
|
||||||
|
|
||||||
self.usage_mock.get.return_value = self.usage
|
self.sdk_client.get_usage.return_value = self.usage
|
||||||
|
|
||||||
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
|
||||||
@ -199,10 +190,10 @@ class TestUsageShow(TestUsage):
|
|||||||
|
|
||||||
columns, data = self.cmd.take_action(parsed_args)
|
columns, data = self.cmd.take_action(parsed_args)
|
||||||
|
|
||||||
self.usage_mock.get.assert_called_with(
|
self.sdk_client.get_usage.assert_called_with(
|
||||||
self.project.id,
|
project=self.project.id,
|
||||||
datetime.datetime(2016, 11, 11, 0, 0),
|
start='2016-11-11T00:00:00',
|
||||||
datetime.datetime(2016, 12, 20, 0, 0))
|
end='2016-12-20T00:00:00')
|
||||||
|
|
||||||
self.assertEqual(self.columns, columns)
|
self.assertEqual(self.columns, columns)
|
||||||
self.assertEqual(self.data, data)
|
self.assertEqual(self.data, data)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user